From f39735801336de66b61a61f2fe021b545cf6c7e7 Mon Sep 17 00:00:00 2001 From: Gabriel Bianconi <1275491+GabrielBianconi@users.noreply.github.com> Date: Sun, 26 Oct 2025 10:42:07 -0400 Subject: [PATCH 1/2] Fix text type in StoredInput // deserializer edition (#4183) * [WIP] Fix text type in StoredInput // deserializer edition * [WIP] Fix text type in StoredInput // deserializer edition * [WIP] Fix text type in StoredInput // deserializer edition * Update tensorzero-core/src/inference/types/stored_input.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [WIP] Fix text type in StoredInput // deserializer edition * Update ui/app/utils/clickhouse/common.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [WIP] Fix text type in StoredInput // deserializer edition * [WIP] Fix text type in StoredInput // deserializer edition * Update tensorzero-core/src/inference/types/stored_input.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [WIP] Fix text type in StoredInput // deserializer edition * [WIP] Fix text type in StoredInput // deserializer edition * Update tensorzero-core/src/inference/types/stored_input.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix typo in TODO comment for textInputSchema --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- clients/rust/src/input_handling.rs | 4 +- evaluations/src/evaluators/exact_match.rs | 35 +-- evaluations/tests/tests.rs | 58 ++--- .../cursor/feedback/src/cursor.rs | 16 +- .../bindings/ResolvedInputMessageContent.ts | 3 +- .../lib/bindings/StoredInput.ts | 2 + .../lib/bindings/StoredInputMessage.ts | 3 + .../lib/bindings/StoredInputMessageContent.ts | 3 +- .../src/db/clickhouse/dataset_queries.rs | 14 +- .../datasets/v1/update_datapoints.rs | 17 +- tensorzero-core/src/inference/types/mod.rs | 4 +- .../src/inference/types/pyo3_helpers.rs | 20 +- .../src/inference/types/resolved_input.rs | 14 +- .../src/inference/types/stored_input.rs | 232 +++++++++++++++--- tensorzero-core/src/optimization/dicl.rs | 6 +- .../gcp_vertex_gemini/optimization.rs | 6 +- .../src/providers/openai/optimization.rs | 18 +- tensorzero-core/src/variant/dicl.rs | 76 +++--- tensorzero-core/tests/e2e/best_of_n.rs | 8 +- tensorzero-core/tests/e2e/cache.rs | 2 +- tensorzero-core/tests/e2e/datasets.rs | 62 ++--- .../tests/e2e/db/dataset_queries.rs | 18 +- tensorzero-core/tests/e2e/dicl.rs | 49 ++-- .../e2e/endpoints/datasets/datasets_update.rs | 72 +++--- .../e2e/endpoints/datasets/get_datapoints.rs | 120 ++++----- tensorzero-core/tests/e2e/feedback.rs | 18 +- tensorzero-core/tests/e2e/human_feedback.rs | 14 +- tensorzero-core/tests/e2e/inference.rs | 34 +-- .../inference_evaluation_human_feedback.rs | 4 +- tensorzero-core/tests/e2e/mixture_of_n.rs | 10 +- .../tests/e2e/openai_compatible.rs | 8 +- .../tests/e2e/optimization/dicl.rs | 7 +- tensorzero-core/tests/e2e/prometheus.rs | 10 +- .../tests/e2e/providers/anthropic.rs | 8 +- .../tests/e2e/providers/aws_bedrock.rs | 4 +- tensorzero-core/tests/e2e/providers/batch.rs | 26 +- tensorzero-core/tests/e2e/providers/common.rs | 60 ++--- tensorzero-core/tests/e2e/providers/openai.rs | 16 +- .../tests/e2e/providers/reasoning.rs | 8 +- .../tests/e2e/render_inferences.rs | 136 +++++----- tensorzero-core/tests/e2e/retries.rs | 6 +- tensorzero-core/tests/e2e/template.rs | 14 +- .../tests/optimization/common/dicl.rs | 8 +- .../tests/optimization/common/mod.rs | 36 +-- .../routes/api/tensorzero/inference.utils.tsx | 4 + ui/app/utils/clickhouse/common.ts | 15 +- ui/app/utils/clickhouse/datasets.server.ts | 9 +- ui/app/utils/clickhouse/inference.server.ts | 4 +- ui/app/utils/resolve.server.ts | 121 ++++++++- 49 files changed, 833 insertions(+), 609 deletions(-) diff --git a/clients/rust/src/input_handling.rs b/clients/rust/src/input_handling.rs index c570872fb8b..251e06a7730 100644 --- a/clients/rust/src/input_handling.rs +++ b/clients/rust/src/input_handling.rs @@ -39,8 +39,8 @@ fn resolved_input_message_content_to_client_input_message_content( resolved_input_message_content: ResolvedInputMessageContent, ) -> ClientInputMessageContent { match resolved_input_message_content { - ResolvedInputMessageContent::Text { text } => { - ClientInputMessageContent::Text(TextKind::Text { text }) + ResolvedInputMessageContent::Text(text) => { + ClientInputMessageContent::Text(TextKind::Text { text: text.text }) } ResolvedInputMessageContent::Template(template) => { ClientInputMessageContent::Template(template) diff --git a/evaluations/src/evaluators/exact_match.rs b/evaluations/src/evaluators/exact_match.rs index b17ae2213ac..2f38f86732b 100644 --- a/evaluations/src/evaluators/exact_match.rs +++ b/evaluations/src/evaluators/exact_match.rs @@ -76,7 +76,7 @@ mod tests { }, inference::types::{ ContentBlockChatOutput, JsonInferenceOutput, StoredInput, StoredInputMessage, - StoredInputMessageContent, Text, Usage, + StoredInputMessageContent, TemplateInput, Text, Usage, }, }; use uuid::Uuid; @@ -90,9 +90,9 @@ mod tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, dataset_name: "test".to_string(), @@ -153,9 +153,9 @@ mod tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, name: None, @@ -185,9 +185,10 @@ mod tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"foo": "bar"}), - }], + content: vec![StoredInputMessageContent::Template(TemplateInput { + name: "user".to_string(), + arguments: serde_json::from_value(json!({"foo": "bar"})).unwrap(), + })], }], }, dataset_name: "test".to_string(), @@ -258,9 +259,10 @@ mod tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"foo": "bar"}), - }], + content: vec![StoredInputMessageContent::Template(TemplateInput { + name: "user".to_string(), + arguments: serde_json::from_value(json!({"foo": "bar"})).unwrap(), + })], }], }, dataset_name: "test".to_string(), @@ -294,9 +296,10 @@ mod tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"foo": "bar"}), - }], + content: vec![StoredInputMessageContent::Template(TemplateInput { + name: "user".to_string(), + arguments: serde_json::from_value(json!({"foo": "bar"})).unwrap(), + })], }], }, dataset_name: "test".to_string(), diff --git a/evaluations/tests/tests.rs b/evaluations/tests/tests.rs index 508e38aec98..cae2b98bc85 100644 --- a/evaluations/tests/tests.rs +++ b/evaluations/tests/tests.rs @@ -17,7 +17,7 @@ use tensorzero_core::endpoints::datasets::Datapoint; use tensorzero_core::evaluations::{LLMJudgeConfig, LLMJudgeInputFormat, LLMJudgeOutputType}; use tensorzero_core::function::{FunctionConfig, FunctionConfigJson}; use tensorzero_core::inference::types::{ - StoredInput, StoredInputMessage, StoredInputMessageContent, TemplateInput, Text, + StoredInput, StoredInputMessage, StoredInputMessageContent, Text, }; use tokio::time::sleep; use url::Url; @@ -386,22 +386,8 @@ async fn run_exact_match_evaluation_chat() { }; let clickhouse_input: StoredInput = serde_json::from_str(clickhouse_inference["input"].as_str().unwrap()).unwrap(); - // The fixture is parsed from the old-style template, so convert it in place - let mut parsed_input = parsed.datapoint.input().clone(); - for message in &mut parsed_input.messages { - for content in &mut message.content { - if let StoredInputMessageContent::Text { value } = content { - if value.is_object() { - *content = StoredInputMessageContent::Template(TemplateInput { - name: message.role.implicit_template_name().to_string(), - arguments: value.as_object().unwrap().clone(), - }); - } - } - } - } // Check the input to the inference is the same as the input to the datapoint - assert_eq!(&clickhouse_input, &parsed_input); + assert_eq!(&clickhouse_input, parsed.datapoint.input()); let clickhouse_output: Vec = serde_json::from_str(clickhouse_inference["output"].as_str().unwrap()).unwrap(); // Check the output to the inference is the same as the output in the response @@ -525,22 +511,8 @@ async fn run_llm_judge_evaluation_chat() { }; let clickhouse_input: StoredInput = serde_json::from_str(clickhouse_inference["input"].as_str().unwrap()).unwrap(); - // The fixture is parsed from the old-style template, so convert it in place - let mut parsed_input = parsed.datapoint.input().clone(); - for message in &mut parsed_input.messages { - for content in &mut message.content { - if let StoredInputMessageContent::Text { value } = content { - if value.is_object() { - *content = StoredInputMessageContent::Template(TemplateInput { - name: message.role.implicit_template_name().to_string(), - arguments: value.as_object().unwrap().clone(), - }); - } - } - } - } // Check the input to the inference is the same as the input to the datapoint - assert_eq!(&clickhouse_input, &parsed_input); + assert_eq!(&clickhouse_input, parsed.datapoint.input()); let clickhouse_output: Vec = serde_json::from_str(clickhouse_inference["output"].as_str().unwrap()).unwrap(); // Check the output to the inference is the same as the output in the response @@ -1311,9 +1283,9 @@ async fn test_run_llm_judge_evaluator_chat() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, auxiliary: String::new(), @@ -1420,9 +1392,9 @@ async fn test_run_llm_judge_evaluator_chat() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, auxiliary: String::new(), @@ -1486,9 +1458,9 @@ async fn test_run_llm_judge_evaluator_json() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, auxiliary: String::new(), @@ -1596,9 +1568,9 @@ async fn test_run_llm_judge_evaluator_json() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, auxiliary: String::new(), diff --git a/examples/integrations/cursor/feedback/src/cursor.rs b/examples/integrations/cursor/feedback/src/cursor.rs index 56a6da188df..ffb029d9d3d 100644 --- a/examples/integrations/cursor/feedback/src/cursor.rs +++ b/examples/integrations/cursor/feedback/src/cursor.rs @@ -3,7 +3,7 @@ use regex::Regex; use serde_json::Value; use std::path::PathBuf; use tensorzero_core::inference::types::{ - ContentBlockChatOutput, StoredInput, StoredInputMessage, StoredInputMessageContent, + ContentBlockChatOutput, StoredInput, StoredInputMessage, StoredInputMessageContent, Text, }; /* This file handles the outputs of inferences from Cursor. We handle two cases: @@ -138,12 +138,7 @@ fn parse_cursor_edit_output( } let second_message = &messages[1]; let second_message_text = match second_message.content.as_slice() { - [StoredInputMessageContent::Text { value: text }] => { - let Value::String(text) = text else { - return Err(anyhow::anyhow!("Expected text in second user message")); - }; - text - } + [StoredInputMessageContent::Text(Text { text })] => text, _ => { return Err(anyhow::anyhow!("Expected text in second user message")); } @@ -211,12 +206,7 @@ fn parse_cursor_insert_output( // Extract workspace path from the first message let first_message = &messages[0]; let first_message_text = match first_message.content.as_slice() { - [StoredInputMessageContent::Text { value }] => { - let Value::String(s) = value else { - return Err(anyhow::anyhow!("Expected text in first user message")); - }; - s - } + [StoredInputMessageContent::Text(Text { text })] => text, _ => { return Err(anyhow::anyhow!( "Expected the first user message to contain a plain-text block" diff --git a/internal/tensorzero-node/lib/bindings/ResolvedInputMessageContent.ts b/internal/tensorzero-node/lib/bindings/ResolvedInputMessageContent.ts index 48af17ea9d3..28121518c4c 100644 --- a/internal/tensorzero-node/lib/bindings/ResolvedInputMessageContent.ts +++ b/internal/tensorzero-node/lib/bindings/ResolvedInputMessageContent.ts @@ -1,13 +1,14 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { FileWithPath } from "./FileWithPath"; import type { TemplateInput } from "./TemplateInput"; +import type { Text } from "./Text"; import type { Thought } from "./Thought"; import type { ToolCall } from "./ToolCall"; import type { ToolResult } from "./ToolResult"; import type { JsonValue } from "./serde_json/JsonValue"; export type ResolvedInputMessageContent = - | { type: "text"; text: string } + | ({ type: "text" } & Text) | ({ type: "template" } & TemplateInput) | ({ type: "tool_call" } & ToolCall) | ({ type: "tool_result" } & ToolResult) diff --git a/internal/tensorzero-node/lib/bindings/StoredInput.ts b/internal/tensorzero-node/lib/bindings/StoredInput.ts index 831cfcef787..f155f3d45d7 100644 --- a/internal/tensorzero-node/lib/bindings/StoredInput.ts +++ b/internal/tensorzero-node/lib/bindings/StoredInput.ts @@ -7,6 +7,8 @@ import type { JsonValue } from "./serde_json/JsonValue"; * This is almost identical to `ResolvedInput`, but without `File` data. * Only the object-storage path is actually stored in clickhouse * (which can be used to re-fetch the file and produce a `ResolvedInput`). + * + * `StoredInputMessage` has a custom deserializer that addresses legacy data formats in the database. */ export type StoredInput = { system?: JsonValue; diff --git a/internal/tensorzero-node/lib/bindings/StoredInputMessage.ts b/internal/tensorzero-node/lib/bindings/StoredInputMessage.ts index 733c3965331..243dd1b8a04 100644 --- a/internal/tensorzero-node/lib/bindings/StoredInputMessage.ts +++ b/internal/tensorzero-node/lib/bindings/StoredInputMessage.ts @@ -2,6 +2,9 @@ import type { Role } from "./Role"; import type { StoredInputMessageContent } from "./StoredInputMessageContent"; +/** + * `StoredInputMessage` has a custom deserializer that addresses legacy data formats in the database. + */ export type StoredInputMessage = { role: Role; content: Array; diff --git a/internal/tensorzero-node/lib/bindings/StoredInputMessageContent.ts b/internal/tensorzero-node/lib/bindings/StoredInputMessageContent.ts index ff4b2ce4877..8fa36d05e3f 100644 --- a/internal/tensorzero-node/lib/bindings/StoredInputMessageContent.ts +++ b/internal/tensorzero-node/lib/bindings/StoredInputMessageContent.ts @@ -1,13 +1,14 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { StoredFile } from "./StoredFile"; import type { TemplateInput } from "./TemplateInput"; +import type { Text } from "./Text"; import type { Thought } from "./Thought"; import type { ToolCall } from "./ToolCall"; import type { ToolResult } from "./ToolResult"; import type { JsonValue } from "./serde_json/JsonValue"; export type StoredInputMessageContent = - | { type: "text"; value: JsonValue } + | ({ type: "text" } & Text) | ({ type: "template" } & TemplateInput) | ({ type: "tool_call" } & ToolCall) | ({ type: "tool_result" } & ToolResult) diff --git a/tensorzero-core/src/db/clickhouse/dataset_queries.rs b/tensorzero-core/src/db/clickhouse/dataset_queries.rs index 15e7949587f..6a08ca18c2b 100644 --- a/tensorzero-core/src/db/clickhouse/dataset_queries.rs +++ b/tensorzero-core/src/db/clickhouse/dataset_queries.rs @@ -384,7 +384,7 @@ impl DatasetQueries for ClickHouseConnectionInfo { params: &CountDatapointsForDatasetFunctionParams, ) -> Result { let query = " - SELECT toUInt32(count()) as count + SELECT toUInt32(count()) as count FROM {table:Identifier} WHERE dataset_name = {dataset_name:String} AND function_name = {function_name:String}"; @@ -3124,18 +3124,18 @@ mod tests { '' as output_schema,"); assert_query_contains(query, "tags, - auxiliary, - source_inference_id, - is_deleted, - is_custom, - staled_at, + auxiliary, + source_inference_id, + is_deleted, + is_custom, + staled_at, formatDateTime(updated_at, '%Y-%m-%dT%H:%i:%SZ') AS updated_at"); assert_query_contains(query, "FROM ChatInferenceDatapoint AS i FINAL WHERE true AND dataset_name = {dataset_name:String} AND id IN ['123e4567-e89b-12d3-a456-426614174000'] AND staled_at IS NULL"); - assert_query_contains(query, "ORDER BY updated_at DESC, id DESC + assert_query_contains(query, "ORDER BY updated_at DESC, id DESC LIMIT {subquery_page_size:UInt32}"); assert_query_contains(query, "UNION ALL"); assert_query_contains(query, " diff --git a/tensorzero-core/src/endpoints/datasets/v1/update_datapoints.rs b/tensorzero-core/src/endpoints/datasets/v1/update_datapoints.rs index 1482447d31c..0fcbbad3e3c 100644 --- a/tensorzero-core/src/endpoints/datasets/v1/update_datapoints.rs +++ b/tensorzero-core/src/endpoints/datasets/v1/update_datapoints.rs @@ -629,9 +629,9 @@ mod tests { system: None, messages: vec![crate::inference::types::StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("original input"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "original input".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -666,9 +666,9 @@ mod tests { system: None, messages: vec![crate::inference::types::StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("original input"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "original input".to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -800,9 +800,8 @@ mod tests { // Input should be updated assert_eq!(updated.input.messages.len(), 1); match &updated.input.messages[0].content[0] { - StoredInputMessageContent::Text { value } => { - let text: String = serde_json::from_value(value.clone()).unwrap(); - assert_eq!(text, "new input text"); + StoredInputMessageContent::Text(text) => { + assert_eq!(text.text, "new input text"); } _ => panic!("Expected text content"), } diff --git a/tensorzero-core/src/inference/types/mod.rs b/tensorzero-core/src/inference/types/mod.rs index 19247e9ac38..56dde005172 100644 --- a/tensorzero-core/src/inference/types/mod.rs +++ b/tensorzero-core/src/inference/types/mod.rs @@ -416,7 +416,7 @@ impl LazyResolvedInputMessageContent { pub async fn resolve(self) -> Result { Ok(match self { LazyResolvedInputMessageContent::Text { text } => { - ResolvedInputMessageContent::Text { text } + ResolvedInputMessageContent::Text(Text { text }) } LazyResolvedInputMessageContent::Template(template) => { ResolvedInputMessageContent::Template(template) @@ -1467,7 +1467,7 @@ impl From for InputMessageContent { #[cfg(test)] impl From for ResolvedInputMessageContent { fn from(text: String) -> Self { - ResolvedInputMessageContent::Text { text } + ResolvedInputMessageContent::Text(Text { text }) } } diff --git a/tensorzero-core/src/inference/types/pyo3_helpers.rs b/tensorzero-core/src/inference/types/pyo3_helpers.rs index adb63e64fb5..43bdd2c4d8c 100644 --- a/tensorzero-core/src/inference/types/pyo3_helpers.rs +++ b/tensorzero-core/src/inference/types/pyo3_helpers.rs @@ -5,7 +5,6 @@ use pyo3::types::{IntoPyDict, PyDict}; use pyo3::{intern, prelude::*}; use pyo3::{sync::PyOnceLock, types::PyModule, Bound, Py, PyAny, PyErr, PyResult, Python}; use serde::Deserialize; -use serde_json::Value; use uuid::Uuid; use crate::endpoints::datasets::Datapoint; @@ -210,19 +209,10 @@ pub fn stored_input_message_content_to_python( content: StoredInputMessageContent, ) -> PyResult> { match content { - StoredInputMessageContent::Text { value } => { + StoredInputMessageContent::Text(text) => { let text_content_block = import_text_content_block(py)?; - match value { - Value::String(s) => { - let kwargs = [(intern!(py, "text"), s)].into_py_dict(py)?; - text_content_block.call(py, (), Some(&kwargs)) - } - _ => { - let value = serialize_to_dict(py, value)?; - let kwargs = [(intern!(py, "arguments"), value)].into_py_dict(py)?; - text_content_block.call(py, (), Some(&kwargs)) - } - } + let kwargs = [(intern!(py, "text"), text.text)].into_py_dict(py)?; + text_content_block.call(py, (), Some(&kwargs)) } StoredInputMessageContent::Template(template) => { let template_content_block = import_template_content_block(py)?; @@ -284,9 +274,9 @@ pub fn resolved_input_message_content_to_python( content: ResolvedInputMessageContent, ) -> PyResult> { match content { - ResolvedInputMessageContent::Text { text } => { + ResolvedInputMessageContent::Text(text) => { let text_content_block = import_text_content_block(py)?; - let kwargs = [(intern!(py, "text"), text)].into_py_dict(py)?; + let kwargs = [(intern!(py, "text"), text.text)].into_py_dict(py)?; text_content_block.call(py, (), Some(&kwargs)) } ResolvedInputMessageContent::Template(template) => { diff --git a/tensorzero-core/src/inference/types/resolved_input.rs b/tensorzero-core/src/inference/types/resolved_input.rs index f6d61203910..a00f5f9b43e 100644 --- a/tensorzero-core/src/inference/types/resolved_input.rs +++ b/tensorzero-core/src/inference/types/resolved_input.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use url::Url; -use super::{storage::StoragePath, Base64File, Role, Thought}; +use super::{storage::StoragePath, Base64File, Role, Text, Thought}; use crate::config::{Config, ObjectStoreInfo}; use crate::error::{Error, ErrorDetails}; use crate::inference::types::file::Base64FileMetadata; @@ -354,9 +354,7 @@ impl ResolvedInputMessage { #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export))] pub enum ResolvedInputMessageContent { - Text { - text: String, - }, + Text(Text), Template(TemplateInput), ToolCall(ToolCall), ToolResult(ToolResult), @@ -376,9 +374,7 @@ pub enum ResolvedInputMessageContent { impl ResolvedInputMessageContent { pub fn into_stored_input_message_content(self) -> StoredInputMessageContent { match self { - ResolvedInputMessageContent::Text { text } => StoredInputMessageContent::Text { - value: Value::String(text), - }, + ResolvedInputMessageContent::Text(text) => StoredInputMessageContent::Text(text), ResolvedInputMessageContent::Template(template) => { StoredInputMessageContent::Template(template) } @@ -409,8 +405,8 @@ impl ResolvedInputMessageContent { pub fn into_lazy_resolved_input_message_content(self) -> LazyResolvedInputMessageContent { match self { - ResolvedInputMessageContent::Text { text } => { - LazyResolvedInputMessageContent::Text { text } + ResolvedInputMessageContent::Text(text) => { + LazyResolvedInputMessageContent::Text { text: text.text } } ResolvedInputMessageContent::Template(template) => { LazyResolvedInputMessageContent::Template(template) diff --git a/tensorzero-core/src/inference/types/stored_input.rs b/tensorzero-core/src/inference/types/stored_input.rs index 87f0c38e230..b3e93627ea9 100644 --- a/tensorzero-core/src/inference/types/stored_input.rs +++ b/tensorzero-core/src/inference/types/stored_input.rs @@ -1,8 +1,6 @@ use crate::config::Config; use crate::endpoints::object_storage::get_object; use crate::error::Error; -use crate::error::ErrorDetails; -use crate::error::IMPOSSIBLE_ERROR_MESSAGE; use crate::inference::types::file::Base64FileMetadata; #[cfg(feature = "pyo3")] use crate::inference::types::pyo3_helpers::stored_input_message_content_to_python; @@ -14,8 +12,9 @@ use crate::inference::types::ResolvedInputMessage; use crate::inference::types::ResolvedInputMessageContent; use crate::inference::types::StoredContentBlock; use crate::inference::types::TemplateInput; -use crate::inference::types::{Role, Thought, ToolCall, ToolResult}; +use crate::inference::types::{Role, Text, Thought, ToolCall, ToolResult}; use futures::future::try_join_all; +use serde::de::{self, Deserializer, MapAccess, Visitor}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -28,6 +27,8 @@ use pyo3::prelude::*; /// This is almost identical to `ResolvedInput`, but without `File` data. /// Only the object-storage path is actually stored in clickhouse /// (which can be used to re-fetch the file and produce a `ResolvedInput`). +/// +/// `StoredInputMessage` has a custom deserializer that addresses legacy data formats in the database. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)] #[serde(deny_unknown_fields)] #[cfg_attr(feature = "pyo3", pyclass(str))] @@ -75,11 +76,11 @@ impl StoredInput { } } -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -#[serde(deny_unknown_fields)] +#[derive(Clone, Debug, Serialize, PartialEq)] #[cfg_attr(feature = "pyo3", pyclass(str))] #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export))] +/// `StoredInputMessage` has a custom deserializer that addresses legacy data formats in the database. pub struct StoredInputMessage { pub role: Role, pub content: Vec, @@ -95,21 +96,117 @@ impl StoredInputMessage { content: try_join_all( self.content .into_iter() - .map(|content| content.reresolve(self.role, resolver)), + .map(|content| content.reresolve(resolver)), ) .await?, }) } } +impl<'de> Deserialize<'de> for StoredInputMessage { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Role, + Content, + } + + struct StoredInputMessageVisitor; + + impl<'de> Visitor<'de> for StoredInputMessageVisitor { + type Value = StoredInputMessage; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct StoredInputMessage") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut role: Option = None; + let mut content: Option> = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Role => { + if role.is_some() { + return Err(de::Error::duplicate_field("role")); + } + role = Some(map.next_value()?); + } + Field::Content => { + if content.is_some() { + return Err(de::Error::duplicate_field("content")); + } + content = Some(map.next_value()?); + } + } + } + + let role = role.ok_or_else(|| de::Error::missing_field("role"))?; + let content_values = content.ok_or_else(|| de::Error::missing_field("content"))?; + + // Transform legacy Text format to new format + let transformed_content: Result, V::Error> = + content_values + .into_iter() + .map(|mut value| { + // Check if this is a legacy Text variant: {"type": "text", "value": ...} + if let Some(obj) = value.as_object_mut() { + if obj.get("type").and_then(|v| v.as_str()) == Some("text") { + if let Some(val) = obj.remove("value") { + // Convert based on value type + match val { + Value::String(text) => { + // Convert to new format: {"type": "text", "text": "..."} + obj.insert("text".to_string(), Value::String(text)); + } + Value::Object(arguments) => { + // Convert to Template format by constructing a new object + let mut new_obj = serde_json::Map::new(); + new_obj.insert("type".to_string(), Value::String("template".to_string())); + new_obj.insert("name".to_string(), Value::String(role.implicit_template_name().to_string())); + new_obj.insert("arguments".to_string(), Value::Object(arguments)); + *obj = new_obj; + } + _ => { + return Err(de::Error::custom( + r#"The `value` field in a `{"type": "text", "value": ... }` content block must be a string or object"# + )); + } + } + } + } + } + + // Deserialize the transformed value + serde_json::from_value(value).map_err(de::Error::custom) + }) + .collect(); + + Ok(StoredInputMessage { + role, + content: transformed_content?, + }) + } + } + + const FIELDS: &[&str] = &["role", "content"]; + deserializer.deserialize_struct("StoredInputMessage", FIELDS, StoredInputMessageVisitor) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(tag = "type", rename_all = "snake_case")] #[cfg_attr(test, derive(ts_rs::TS))] #[cfg_attr(test, ts(export))] pub enum StoredInputMessageContent { - Text { - value: Value, - }, + Text(Text), Template(TemplateInput), ToolCall(ToolCall), ToolResult(ToolResult), @@ -129,31 +226,10 @@ pub enum StoredInputMessageContent { impl StoredInputMessageContent { pub async fn reresolve( self, - role: Role, resolver: &impl StoragePathResolver, ) -> Result { match self { - StoredInputMessageContent::Text { value } => { - match value { - // Plain string input (which might later be templated by an `input_wrapper` template) - Value::String(text) => Ok(ResolvedInputMessageContent::Text { text }), - // Convert legacy `{"type": "text", "arguments": {}}` inputs to `{"type": "template", "name": "", "arguments": {}}` inputs, - // where the template name is determined by the message role. - // We don't store any new entries in ClickHouse using the legacy format - this is needed to handle - // existing entries in our database. - Value::Object(object) => { - Ok(ResolvedInputMessageContent::Template(TemplateInput { - name: role.implicit_template_name().to_string(), - arguments: object, - })) - } - _ => Err(Error::new(ErrorDetails::InternalError { - message: format!( - "Invalid text content: {value:?}. {IMPOSSIBLE_ERROR_MESSAGE}" - ), - })), - } - } + StoredInputMessageContent::Text(text) => Ok(ResolvedInputMessageContent::Text(text)), StoredInputMessageContent::Template(template) => { Ok(ResolvedInputMessageContent::Template(template)) } @@ -274,3 +350,97 @@ pub struct StoredRequestMessage { pub role: Role, pub content: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_deserialize_legacy_text_string() { + // Legacy format with string value + let json = json!({ + "role": "user", + "content": [{"type": "text", "value": "Hello, world!"}] + }); + + let message: StoredInputMessage = serde_json::from_value(json).unwrap(); + assert_eq!(message.role, Role::User); + assert_eq!(message.content.len(), 1); + match &message.content[0] { + StoredInputMessageContent::Text(text) => { + assert_eq!(text.text, "Hello, world!"); + } + _ => panic!("Expected Text variant"), + } + } + + #[test] + fn test_deserialize_legacy_text_object() { + // Legacy format with object value (should convert to Template) + let json = json!({ + "role": "user", + "content": [{"type": "text", "value": {"foo": "bar", "baz": 123}}] + }); + + let message: StoredInputMessage = serde_json::from_value(json).unwrap(); + assert_eq!(message.role, Role::User); + assert_eq!(message.content.len(), 1); + match &message.content[0] { + StoredInputMessageContent::Template(template) => { + assert_eq!(template.name, "user"); + assert_eq!(template.arguments.get("foo").unwrap(), "bar"); + assert_eq!(template.arguments.get("baz").unwrap(), 123); + } + _ => panic!("Expected Template variant"), + } + } + + #[test] + fn test_deserialize_new_text_format() { + // New format with text field + let json = json!({ + "role": "user", + "content": [{"type": "text", "text": "Hello, world!"}] + }); + + let message: StoredInputMessage = serde_json::from_value(json).unwrap(); + assert_eq!(message.role, Role::User); + assert_eq!(message.content.len(), 1); + match &message.content[0] { + StoredInputMessageContent::Text(text) => { + assert_eq!(text.text, "Hello, world!"); + } + _ => panic!("Expected Text variant"), + } + } + + #[test] + fn test_deserialize_legacy_text_invalid_value() { + // Legacy format with invalid value type (number) + let json = json!({ + "role": "user", + "content": [{"type": "text", "value": 123}] + }); + + let result: Result = serde_json::from_value(json); + assert!(result.is_err()); + let err_msg = result.unwrap_err().to_string(); + assert!(err_msg.contains("must be a string or object")); + } + + #[test] + fn test_round_trip_serialization() { + let message = StoredInputMessage { + role: Role::User, + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], + }; + + let json = serde_json::to_value(&message).unwrap(); + let deserialized: StoredInputMessage = serde_json::from_value(json).unwrap(); + + assert_eq!(message, deserialized); + } +} diff --git a/tensorzero-core/src/optimization/dicl.rs b/tensorzero-core/src/optimization/dicl.rs index d30eec607cc..cadb836e2df 100644 --- a/tensorzero-core/src/optimization/dicl.rs +++ b/tensorzero-core/src/optimization/dicl.rs @@ -1030,9 +1030,9 @@ mod tests { system: Some(json!("Test system")), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Test message"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { diff --git a/tensorzero-core/src/providers/gcp_vertex_gemini/optimization.rs b/tensorzero-core/src/providers/gcp_vertex_gemini/optimization.rs index 293211299c4..be525a05c92 100644 --- a/tensorzero-core/src/providers/gcp_vertex_gemini/optimization.rs +++ b/tensorzero-core/src/providers/gcp_vertex_gemini/optimization.rs @@ -274,9 +274,9 @@ mod tests { system: Some(json!("You are a helpful assistant named Dr. M.M. Patel.")), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("What is the capital of France?"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the capital of France?".to_string(), + })], }], }, output: output.clone(), diff --git a/tensorzero-core/src/providers/openai/optimization.rs b/tensorzero-core/src/providers/openai/optimization.rs index 7092f33074e..937554caba9 100644 --- a/tensorzero-core/src/providers/openai/optimization.rs +++ b/tensorzero-core/src/providers/openai/optimization.rs @@ -635,9 +635,9 @@ mod tests { system: Some(json!("You are a helpful assistant named Dr. M.M. Patel.")), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the capital of France?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the capital of France?".to_string(), + })], }], }, output: output.clone(), @@ -700,9 +700,9 @@ mod tests { system: Some(json!("You are a helpful assistant named Dr. M.M. Patel.")), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("What is the capital of France?"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the capital of France?".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -770,9 +770,9 @@ mod tests { system: Some(json!("You are a helpful assistant.")), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("What's the weather like?"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What's the weather like?".to_string(), + })], }], }, output: Some(vec![ diff --git a/tensorzero-core/src/variant/dicl.rs b/tensorzero-core/src/variant/dicl.rs index cde8122af37..75541f56b30 100644 --- a/tensorzero-core/src/variant/dicl.rs +++ b/tensorzero-core/src/variant/dicl.rs @@ -22,7 +22,7 @@ use crate::inference::types::ResolvedInputMessageContent; use crate::inference::types::StoredInput; use crate::inference::types::StoredInputMessageContent; use crate::inference::types::{ - batch::StartBatchModelInferenceWithMetadata, ModelInferenceRequest, RequestMessage, Role, + batch::StartBatchModelInferenceWithMetadata, ModelInferenceRequest, RequestMessage, Role, Text, }; use crate::model::ModelTable; use crate::model_table::ShorthandModelConfig; @@ -390,7 +390,7 @@ fn lazy_content_to_resolved_discarding_incompatible( ) -> Result { Ok(match content { LazyResolvedInputMessageContent::Text { text } => { - ResolvedInputMessageContent::Text { text } + ResolvedInputMessageContent::Text(Text { text }) } LazyResolvedInputMessageContent::Template(template) => { // Stringify template as JSON for DICL @@ -404,7 +404,7 @@ fn lazy_content_to_resolved_discarding_incompatible( message: format!("Failed to stringify template content block: {e}"), }) })?; - ResolvedInputMessageContent::Text { text: json_str } + ResolvedInputMessageContent::Text(Text { text: json_str }) } LazyResolvedInputMessageContent::ToolCall(tool_call) => { ResolvedInputMessageContent::ToolCall(tool_call) @@ -937,15 +937,15 @@ mod tests { messages: vec![ StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, assistant!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, assistant!".to_string(), + })], }, StoredInputMessage { role: Role::Assistant, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, user!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, user!".to_string(), + })], }, ], }; @@ -1037,9 +1037,9 @@ mod tests { ResolvedInputMessage { role: Role::User, content: vec![ - ResolvedInputMessageContent::Text { + ResolvedInputMessageContent::Text(Text { text: "Hello, assistant!".to_string(), - }, + }), ResolvedInputMessageContent::ToolCall(ToolCall { id: "tool_call_1".to_string(), name: "search_tool".to_string(), @@ -1049,9 +1049,9 @@ mod tests { }, ResolvedInputMessage { role: Role::Assistant, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "Here are the search results for rust programming.".to_string(), - }], + })], }, ], }; @@ -1080,9 +1080,9 @@ mod tests { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the boiling point of water?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the boiling point of water?".to_string(), + })], }], }) .unwrap(), @@ -1098,9 +1098,9 @@ mod tests { messages: vec![StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: json!("What is the name of the capital city of Japan?"), - }, + StoredInputMessageContent::Text(Text { + text: "What is the name of the capital city of Japan?".to_string(), + }), StoredInputMessageContent::File(Box::new(StoredFile { file: Base64FileMetadata { url: None, @@ -1144,9 +1144,9 @@ mod tests { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the boiling point of water?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the boiling point of water?".to_string(), + })], }], }) .unwrap(), @@ -1183,9 +1183,9 @@ mod tests { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the boiling point of water?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the boiling point of water?".to_string(), + })], }], }) .unwrap(), @@ -1200,9 +1200,9 @@ mod tests { system: Some(json!({"assistant_name": "Pinocchio"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the name of the capital city of Japan?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the name of the capital city of Japan?".to_string(), + })], }], }) .unwrap(), @@ -1243,9 +1243,9 @@ mod tests { system: Some(json!({"assistant_name": "JsonTester"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Provide a sample JSON response.".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Provide a sample JSON response.".to_string(), + })], }], }) .unwrap(), @@ -1266,9 +1266,9 @@ mod tests { system: Some(json!({"assistant_name": "JsonTester"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Provide another JSON response.".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Provide another JSON response.".to_string(), + })], }], }) .unwrap(), @@ -1454,9 +1454,9 @@ mod tests { system: Some(json!({"context": "example"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Example question"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Example question".to_string(), + })], }], }; let example = Example::Chat(ChatExample { @@ -1693,7 +1693,7 @@ mod tests { let result = lazy_content_to_resolved_discarding_incompatible(template).unwrap(); match result { - ResolvedInputMessageContent::Text { text } => { + ResolvedInputMessageContent::Text(Text { text }) => { let parsed: serde_json::Value = serde_json::from_str(&text).unwrap(); assert_eq!(parsed["type"], "template"); assert_eq!(parsed["name"], "test_template"); diff --git a/tensorzero-core/tests/e2e/best_of_n.rs b/tensorzero-core/tests/e2e/best_of_n.rs index 46e353abaef..2ff0feddaa4 100644 --- a/tensorzero-core/tests/e2e/best_of_n.rs +++ b/tensorzero-core/tests/e2e/best_of_n.rs @@ -113,7 +113,7 @@ async fn e2e_test_best_of_n_dummy_candidates_dummy_judge_inner( { "role": "user", "content": [ - {"type": "text", "value": format!("Please write me a sentence about Megumin making an explosion: {random_input}")}, + {"type": "text", "text": format!("Please write me a sentence about Megumin making an explosion: {random_input}")}, ] } ] @@ -263,7 +263,7 @@ async fn e2e_test_best_of_n_dummy_candidates_real_judge() { { "role": "user", "content": [ - {"type": "text", "value": "Please write me a sentence about Megumin making an explosion."}, + {"type": "text", "text": "Please write me a sentence about Megumin making an explosion."}, {"type": "unknown", "model_provider_name": "tensorzero::model_name::json::provider_name::json", "data": {"type": "text", "text": "My extra json-model input", "my": {"other": "keys"}}}, {"type": "unknown", "model_provider_name": "tensorzero::model_name::gemini-2.0-flash-001::provider_name::gcp_vertex_gemini", "data": {"text": "My extra gemini text"}} ] @@ -574,7 +574,7 @@ async fn e2e_test_best_of_n_json_real_judge() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What's the first word in the typical output of one's first program. Answer as a json object with a single field 'answer' containing the string."}] + "content": [{"type": "text", "text": "What's the first word in the typical output of one's first program. Answer as a json object with a single field 'answer' containing the string."}] } ] } @@ -854,7 +854,7 @@ async fn e2e_test_best_of_n_json_real_judge_implicit_tool() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What's the first word in the typical output of one's first program. Answer as a json object with a single field 'answer' containing the string."}] + "content": [{"type": "text", "text": "What's the first word in the typical output of one's first program. Answer as a json object with a single field 'answer' containing the string."}] } ] } diff --git a/tensorzero-core/tests/e2e/cache.rs b/tensorzero-core/tests/e2e/cache.rs index f745bd9bc00..529047a0b49 100644 --- a/tensorzero-core/tests/e2e/cache.rs +++ b/tensorzero-core/tests/e2e/cache.rs @@ -656,7 +656,7 @@ pub async fn check_test_streaming_cache_with_err( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "My test input string"}] + "content": [{"type": "text", "text": "My test input string"}] } ] }); diff --git a/tensorzero-core/tests/e2e/datasets.rs b/tensorzero-core/tests/e2e/datasets.rs index 09c007d9abc..6ee31ab694c 100644 --- a/tensorzero-core/tests/e2e/datasets.rs +++ b/tensorzero-core/tests/e2e/datasets.rs @@ -91,7 +91,7 @@ async fn test_datapoint_insert_synthetic_chat() { "function_name": "basic_test", "id": id.to_string(), "episode_id": null, - "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"My synthetic input\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"My synthetic input\"}]}]}", "output": "[{\"type\":\"text\",\"text\":\"My synthetic output\"}]", "tool_params": "", "tags": {}, @@ -279,14 +279,7 @@ async fn test_create_delete_datapoint_chat() { let content = first_message.content; assert!(!content.is_empty()); let first_content = content[0].clone(); - assert!(matches!( - first_content, - StoredInputMessageContent::Text { .. } - )); - assert!(matches!( - first_content, - StoredInputMessageContent::Text { value: _, .. } - )); + assert!(matches!(first_content, StoredInputMessageContent::Text(_))); // Verify the list datapoint input structure and content let input = &list_datapoint.input; @@ -302,10 +295,7 @@ async fn test_create_delete_datapoint_chat() { let content = first_message.content; assert!(!content.is_empty()); let first_content = content[0].clone(); - assert!(matches!( - first_content, - StoredInputMessageContent::Text { .. } - )); + assert!(matches!(first_content, StoredInputMessageContent::Text(_))); // Verify output if present if let Some(output) = &datapoint.output { @@ -647,7 +637,7 @@ async fn test_datapoint_insert_synthetic_chat_with_tools() { "function_name": "basic_test", "id": id.to_string(), "episode_id": null, - "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"My synthetic input\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"My synthetic input\"}]}]}", "output": "[{\"type\":\"tool_call\",\"arguments\":{\"location\":\"New York\",\"units\":\"fahrenheit\"},\"id\":\"call_123\",\"name\":\"get_temperature\",\"raw_arguments\":\"{\\\"location\\\":\\\"New York\\\",\\\"units\\\":\\\"fahrenheit\\\"}\",\"raw_name\":\"get_temperature\"}]", "tool_params": "{\"tools_available\":[{\"description\":\"Get the current temperature in a given location\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"The location to get the temperature for (e.g. \\\"New York\\\")\"},\"units\":{\"type\":\"string\",\"description\":\"The units to get the temperature in (must be \\\"fahrenheit\\\" or \\\"celsius\\\")\",\"enum\":[\"fahrenheit\",\"celsius\"]}},\"required\":[\"location\"],\"additionalProperties\":false},\"name\":\"get_temperature\",\"strict\":false}],\"tool_choice\":\"auto\",\"parallel_tool_calls\":false}", "tags": {}, @@ -675,7 +665,7 @@ async fn test_datapoint_insert_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "Hello"}, "output_schema": {}, "source_inference_id": source_inference_id, @@ -751,7 +741,7 @@ async fn test_datapoint_insert_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "New answer"}, "output_schema": {"type": "object", "properties": {"confidence": {"type": "number"}}, "required": ["confidence"]}, "is_custom": true, @@ -783,7 +773,7 @@ async fn test_datapoint_insert_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "New answer"}, "output_schema": { "type": "object", @@ -866,7 +856,7 @@ async fn test_datapoint_insert_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "New answer"}, "output_schema": { "type": "object", @@ -905,7 +895,7 @@ async fn test_datapoint_insert_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "New answer"}, "output_schema": { "type": "object", @@ -990,12 +980,12 @@ async fn test_create_delete_datapoint_json() { "datapoints": [ { "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "Hello"}, }, { "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"response": "Hello"}, "output_schema": alternate_output_schema } @@ -1232,7 +1222,7 @@ async fn test_datapoint_insert_bad_name() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": {"answer": "Hello"}, "output_schema": {}, })) @@ -1334,7 +1324,7 @@ async fn test_datapoint_insert_invalid_output_synthetic_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Ferris"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Ferris"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": "Not a json object", "output_schema": {"type": "object", "properties": {"answer": {"type": "string"}}, "required": ["answer"]}, "is_custom": false, @@ -1512,7 +1502,7 @@ async fn test_datapoint_insert_output_inherit_chat() { "function_name": "basic_test", "id": datapoint_id.to_string(), "episode_id": episode_id.to_string(), - "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"Hello, world!\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Hello, world!\"}]}]}", "output": "[{\"type\":\"text\",\"text\":\"Megumin gleefully chanted her spell, unleashing a thunderous explosion that lit up the sky and left a massive crater in its wake.\"}]", "tool_params": "", "tags": {}, @@ -1629,7 +1619,7 @@ async fn test_datapoint_insert_output_none_chat() { "function_name": "basic_test", "id": datapoint_id.to_string(), "episode_id": episode_id.to_string(), - "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"Hello, world!\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Hello, world!\"}]}]}", "output": null, "tool_params": "", "tags": {}, @@ -1802,7 +1792,7 @@ async fn test_datapoint_insert_output_demonstration_chat() { "function_name": "basic_test", "id": datapoint_id.to_string(), "episode_id": episode_id.to_string(), - "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"Hello, world!\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Alfred Pennyworth\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Hello, world!\"}]}]}", "output": "[{\"type\":\"text\",\"text\":\"My demonstration chat answer\"}]", "tool_params": "", "tags": {}, @@ -1826,7 +1816,7 @@ async fn test_datapoint_insert_output_inherit_json() { "input": { "system": {"assistant_name": "Alfred Pennyworth"}, "messages": [{"role": "user", "content": [ - {"type": "text", "arguments": {"country": "Japan"}} + {"type": "template", "name": "user", "arguments": {"country": "Japan"}} ]}], }, "stream": false, @@ -1943,7 +1933,7 @@ async fn test_datapoint_insert_output_none_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -2040,7 +2030,7 @@ async fn test_datapoint_insert_output_demonstration_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -2186,7 +2176,7 @@ async fn test_datapoint_missing_demonstration() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -2285,7 +2275,7 @@ async fn test_datapoint_insert_missing_output_chat() { "function_name": "basic_test", "id": id.to_string(), "episode_id": null, - "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"My synthetic input\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"My synthetic input\"}]}]}", "output": null, "tool_params": "", "tags": {}, @@ -2351,7 +2341,7 @@ async fn test_datapoint_insert_null_output_chat() { "function_name": "basic_test", "id": id.to_string(), "episode_id": null, - "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"value\":\"My synthetic input\"}]}]}", + "input": "{\"system\":{\"assistant_name\":\"Dummy\"},\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"My synthetic input\"}]}]}", "output": null, "tool_params": "", "tags": {}, @@ -2378,7 +2368,7 @@ async fn test_datapoint_insert_missing_output_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output_schema": {}, "is_custom": false, // output field is deliberately omitted @@ -2445,7 +2435,7 @@ async fn test_datapoint_insert_null_output_json() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "US"}}]}]}, + "input": {"system": {"assistant_name": "Dummy"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "US"}}]}]}, "output": null, // explicitly null output "output_schema": {}, "is_custom": true, @@ -2706,7 +2696,7 @@ async fn test_stale_dataset_with_datapoints() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Test"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Brazil"}}]}]}, + "input": {"system": {"assistant_name": "Test"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Brazil"}}]}]}, "output": {"answer": "Result 1"}, "output_schema": {}, "is_custom": false, @@ -2722,7 +2712,7 @@ async fn test_stale_dataset_with_datapoints() { ))) .json(&json!({ "function_name": "json_success", - "input": {"system": {"assistant_name": "Test"}, "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "France"}}]}]}, + "input": {"system": {"assistant_name": "Test"}, "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "France"}}]}]}, "output": {"answer": "Result 2"}, "output_schema": {}, "is_custom": false, diff --git a/tensorzero-core/tests/e2e/db/dataset_queries.rs b/tensorzero-core/tests/e2e/db/dataset_queries.rs index 5e39b0d65b4..336f951e4a7 100644 --- a/tensorzero-core/tests/e2e/db/dataset_queries.rs +++ b/tensorzero-core/tests/e2e/db/dataset_queries.rs @@ -978,9 +978,9 @@ async fn test_get_datapoint_returns_correct_json_datapoint_with_specific_id() { let input_messages = datapoint.input.messages; assert!(input_messages.contains(&StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Is it a living thing?".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Is it a living thing?".to_string(), + })], })); assert_eq!( @@ -2351,9 +2351,9 @@ async fn test_datapoint_with_mixed_file_types() { StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: "Here are some files".into(), - }, + StoredInputMessageContent::Text(Text { + text: "Here are some files".to_string(), + }), StoredInputMessageContent::File(Box::new(stored_file1.clone())), ], }, @@ -2400,10 +2400,8 @@ async fn test_datapoint_with_mixed_file_types() { // Check first message with text and file assert_eq!(chat_dp.input.messages[0].content.len(), 2); match &chat_dp.input.messages[0].content[0] { - StoredInputMessageContent::Text { value } => { - // value is a JSON string, so we need to deserialize it - let text: String = serde_json::from_value(value.clone()).unwrap(); - assert_eq!(text, "Here are some files"); + StoredInputMessageContent::Text(text) => { + assert_eq!(text.text, "Here are some files"); } _ => panic!("Expected Text content"), } diff --git a/tensorzero-core/tests/e2e/dicl.rs b/tensorzero-core/tests/e2e/dicl.rs index 402da8ae799..d5609dccab5 100644 --- a/tensorzero-core/tests/e2e/dicl.rs +++ b/tensorzero-core/tests/e2e/dicl.rs @@ -20,6 +20,7 @@ use tensorzero_core::{ inference::types::{ ContentBlockChatOutput, JsonInferenceOutput, ResolvedInput, ResolvedInputMessage, ResolvedInputMessageContent, Role, StoredContentBlock, StoredRequestMessage, TemplateInput, + Text, }, model_table::ProviderTypeDefaultCredentials, rate_limiting::ScopeInfo, @@ -234,7 +235,7 @@ pub async fn test_dicl_inference_request_no_examples(dicl_variant_name: &str) { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan?"}] } ] }); @@ -465,9 +466,9 @@ pub async fn test_dicl_inference_request_simple() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is the boiling point of water?".to_string(), - }], + })], }], }; let output: Vec = vec!["100 degrees Celsius".to_string().into()]; @@ -485,9 +486,9 @@ pub async fn test_dicl_inference_request_simple() { system: Some(json!({"assistant_name": "Pinocchio"})), messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What the capital city of India?".to_string(), - }], + })], }], }; let output: Vec = @@ -506,9 +507,9 @@ pub async fn test_dicl_inference_request_simple() { system: Some(json!({"assistant_name": "Pinocchio"})), messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is an example of a computationally hard problem?".to_string(), - }], + })], }], }; let output: Vec = vec![ @@ -529,9 +530,9 @@ pub async fn test_dicl_inference_request_simple() { system: Some(json!({"assistant_name": "Pinocchio"})), messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "Who wrote Lord of the Rings?".to_string(), - }], + })], }], }; let output: Vec = @@ -648,7 +649,7 @@ pub async fn test_dicl_inference_request_simple() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Who was the author of the Harry Potter series?"}] + "content": [{"type": "text", "text": "Who was the author of the Harry Potter series?"}] } ] }); @@ -892,7 +893,7 @@ pub async fn test_dicl_inference_request_simple() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Who was the author of the Harry Potter series?"}] + "content": [{"type": "text", "text": "Who was the author of the Harry Potter series?"}] } ] }); @@ -1154,7 +1155,7 @@ async fn test_dicl_json_request() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Brazil"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Brazil"}}] } ]}, "stream": false, @@ -1402,9 +1403,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is the capital of France?".to_string(), - }], + })], }], }; let output: Vec = vec!["Paris".to_string().into()]; @@ -1421,9 +1422,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is the capital of Germany?".to_string(), - }], + })], }], }; let output: Vec = vec!["Berlin".to_string().into()]; @@ -1440,9 +1441,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is the capital of Italy?".to_string(), - }], + })], }], }; let output: Vec = vec!["Rome".to_string().into()]; @@ -1574,9 +1575,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What the capital city of India?".to_string(), - }], + })], }], }; let output: Vec = @@ -1594,9 +1595,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "What is an example of a computationally hard problem?".to_string(), - }], + })], }], }; let output: Vec = vec![ @@ -1617,9 +1618,9 @@ max_tokens = 100 system: None, messages: vec![ResolvedInputMessage { role: Role::User, - content: vec![ResolvedInputMessageContent::Text { + content: vec![ResolvedInputMessageContent::Text(Text { text: "Who wrote Lord of the Rings?".to_string(), - }], + })], }], }; let output: Vec = diff --git a/tensorzero-core/tests/e2e/endpoints/datasets/datasets_update.rs b/tensorzero-core/tests/e2e/endpoints/datasets/datasets_update.rs index e7ac9cb6b6e..2caafd37cb3 100644 --- a/tensorzero-core/tests/e2e/endpoints/datasets/datasets_update.rs +++ b/tensorzero-core/tests/e2e/endpoints/datasets/datasets_update.rs @@ -43,9 +43,9 @@ async fn test_update_chat_datapoint_output() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Original message".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Original message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -171,9 +171,9 @@ async fn test_update_json_datapoint_output() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"country": "US"}).to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: json!({"country": "US"}).to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -261,9 +261,9 @@ async fn test_update_multiple_datapoints() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Message 1".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Message 1".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -287,9 +287,9 @@ async fn test_update_multiple_datapoints() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Message 2".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Message 2".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -420,9 +420,9 @@ async fn test_update_datapoint_type_mismatch() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -482,9 +482,9 @@ async fn test_update_datapoint_with_metadata() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -614,9 +614,9 @@ async fn test_update_chat_datapoint_set_output_to_null() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test message".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -696,9 +696,9 @@ async fn test_update_chat_datapoint_set_tool_params_to_null() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test message".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -785,9 +785,9 @@ async fn test_update_chat_datapoint_set_tags_to_empty() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test message".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -867,9 +867,9 @@ async fn test_update_chat_datapoint_set_name_to_null() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test message".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test message".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::Text(Text { @@ -957,9 +957,9 @@ async fn test_update_json_datapoint_set_output_to_null() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"question": "test"}).to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: json!({"question": "test"}).to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -1050,9 +1050,9 @@ async fn test_update_json_datapoint_set_tags_to_empty() { system: Some(json!({"assistant_name": "Test"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"question": "test"}).to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: json!({"question": "test"}).to_string(), + })], }], }, output: Some(JsonInferenceOutput { diff --git a/tensorzero-core/tests/e2e/endpoints/datasets/get_datapoints.rs b/tensorzero-core/tests/e2e/endpoints/datasets/get_datapoints.rs index 12fe02808df..2d3529e8efd 100644 --- a/tensorzero-core/tests/e2e/endpoints/datasets/get_datapoints.rs +++ b/tensorzero-core/tests/e2e/endpoints/datasets/get_datapoints.rs @@ -42,9 +42,9 @@ mod get_datapoints_tests { system: Some(json!({"assistant_name": "TestBot"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Hello, world!".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![ @@ -123,9 +123,9 @@ mod get_datapoints_tests { system: Some(json!({"assistant_name": "JsonBot"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"query": "test"}).to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: json!({"query": "test"}).to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -192,9 +192,9 @@ mod get_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Message 1".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Message 1".to_string(), + })], }], }, output: Some(vec![ @@ -220,9 +220,9 @@ mod get_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Message 2".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Message 2".to_string(), + })], }], }, output: Some(vec![ @@ -248,9 +248,9 @@ mod get_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Query".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Query".to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -322,9 +322,9 @@ mod get_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ @@ -388,9 +388,9 @@ mod get_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ @@ -506,9 +506,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: format!("Message {i}").into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: format!("Message {i}"), + })], }], }, output: Some(vec![ @@ -616,9 +616,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 1".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 1".to_string(), + })], }], }, output: Some(vec![ @@ -645,9 +645,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 2".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 2".to_string(), + })], }], }, output: Some(vec![ @@ -730,9 +730,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 1".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 1".to_string(), + })], }], }, output: Some(vec![ @@ -759,9 +759,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 2".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 2".to_string(), + })], }], }, output: Some(vec![ @@ -849,9 +849,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ @@ -948,9 +948,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 1".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 1".to_string(), + })], }], }, output: Some(vec![ @@ -977,9 +977,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 2".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 2".to_string(), + })], }], }, output: Some(vec![ @@ -1006,9 +1006,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test 3".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test 3".to_string(), + })], }], }, output: Some(vec![ @@ -1139,9 +1139,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Test".to_string(), + })], }], }, output: Some(vec![ @@ -1240,9 +1240,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "Chat test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Chat test".to_string(), + })], }], }, output: Some(vec![ @@ -1269,9 +1269,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "JSON test".to_string().into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "JSON test".to_string(), + })], }], }, output: Some(JsonInferenceOutput { @@ -1342,9 +1342,9 @@ mod list_datapoints_tests { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: format!("Message {i}").into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: format!("Message {i}"), + })], }], }, output: Some(vec![ diff --git a/tensorzero-core/tests/e2e/feedback.rs b/tensorzero-core/tests/e2e/feedback.rs index 5d962d696ea..1dd44103aff 100644 --- a/tensorzero-core/tests/e2e/feedback.rs +++ b/tensorzero-core/tests/e2e/feedback.rs @@ -27,7 +27,7 @@ async fn e2e_test_comment_feedback_normal_function() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, })).await; @@ -129,7 +129,7 @@ async fn e2e_test_comment_feedback_with_payload(inference_payload: serde_json::V "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -384,7 +384,7 @@ async fn e2e_test_demonstration_feedback_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -498,7 +498,7 @@ async fn e2e_test_demonstration_feedback_llm_judge() { "function_name": "tensorzero::llm_judge::haiku_without_outputs::topic_starts_with_f", "input": { "messages": [{"role": "user", "content": [ - {"type": "text", "arguments": {"input": "foo", "reference_output": null, "generated_output": "A poem about a cat"}}, + {"type": "template", "name": "user", "arguments": {"input": "foo", "reference_output": null, "generated_output": "A poem about a cat"}}, ]}] }, "stream": false, @@ -591,7 +591,7 @@ async fn e2e_test_demonstration_feedback_dynamic_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, "output_schema": new_output_schema, @@ -1017,7 +1017,7 @@ async fn e2e_test_float_feedback_normal_function() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, })).await; @@ -1149,7 +1149,7 @@ async fn e2e_test_float_feedback_with_payload(inference_payload: serde_json::Val "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -1253,7 +1253,7 @@ async fn e2e_test_boolean_feedback_normal_function() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, })).await; @@ -1393,7 +1393,7 @@ async fn e2e_test_boolean_feedback_with_payload(inference_payload: serde_json::V "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); diff --git a/tensorzero-core/tests/e2e/human_feedback.rs b/tensorzero-core/tests/e2e/human_feedback.rs index eff39de5555..f78a0e86447 100644 --- a/tensorzero-core/tests/e2e/human_feedback.rs +++ b/tensorzero-core/tests/e2e/human_feedback.rs @@ -24,7 +24,7 @@ async fn e2e_test_comment_human_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -293,7 +293,7 @@ async fn e2e_test_demonstration_feedback_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -418,7 +418,7 @@ async fn e2e_test_demonstration_feedback_dynamic_json() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, "output_schema": new_output_schema, @@ -857,7 +857,7 @@ async fn e2e_test_float_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -962,7 +962,7 @@ async fn e2e_test_float_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -1036,7 +1036,7 @@ async fn e2e_test_boolean_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -1148,7 +1148,7 @@ async fn e2e_test_boolean_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); diff --git a/tensorzero-core/tests/e2e/inference.rs b/tensorzero-core/tests/e2e/inference.rs index b36af80beca..0d6f394efea 100644 --- a/tensorzero-core/tests/e2e/inference.rs +++ b/tensorzero-core/tests/e2e/inference.rs @@ -166,7 +166,7 @@ async fn e2e_test_inference_chat_strip_unknown_block_non_stream() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] }, { "role": "user", @@ -314,7 +314,7 @@ async fn test_dummy_only_inference_chat_strip_unknown_block_stream() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] }, { "role": "user", @@ -459,7 +459,7 @@ async fn e2e_test_inference_model_fallback() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ] } @@ -616,7 +616,7 @@ async fn e2e_test_tool_call() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] + "content": [{"type": "text", "text": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] } ] } @@ -812,7 +812,7 @@ async fn e2e_test_tool_call_malformed() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] + "content": [{"type": "text", "text": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] } ] } @@ -989,7 +989,7 @@ async fn e2e_test_inference_json_fail() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ] }); @@ -1073,7 +1073,7 @@ async fn e2e_test_inference_json_success() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -1218,7 +1218,7 @@ async fn e2e_test_variant_failover() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"type": "tacos", "quantity": 13}}], + "content": [{"type": "template", "name": "user", "arguments": {"type": "tacos", "quantity": 13}}], } ]}, "stream": false, @@ -1539,7 +1539,7 @@ async fn e2e_test_streaming() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ]} ); @@ -2215,7 +2215,7 @@ async fn e2e_test_tool_call_streaming() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] + "content": [{"type": "text", "text": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] } ] } @@ -2429,7 +2429,7 @@ async fn e2e_test_tool_call_streaming_split_tool_name() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] + "content": [{"type": "text", "text": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] } ] } @@ -2762,7 +2762,7 @@ pub async fn e2e_test_dynamic_api_key() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan?"}] } ] }); @@ -3286,7 +3286,7 @@ async fn test_inference_zero_tokens_helper( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ] } @@ -3592,7 +3592,7 @@ async fn test_json_cot_inference_request() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -3633,7 +3633,7 @@ async fn test_json_cot_inference_request_implicit_tool() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -3907,11 +3907,11 @@ async fn test_multiple_text_blocks_in_message() { assert_eq!(input.messages[0].content.len(), 2); assert!(matches!( input.messages[0].content[0], - StoredInputMessageContent::Text { .. } + StoredInputMessageContent::Text(_) )); assert!(matches!( input.messages[0].content[1], - StoredInputMessageContent::Text { .. } + StoredInputMessageContent::Text(_) )); } diff --git a/tensorzero-core/tests/e2e/inference_evaluation_human_feedback.rs b/tensorzero-core/tests/e2e/inference_evaluation_human_feedback.rs index eda7ee50d0e..c7ba5150b0e 100644 --- a/tensorzero-core/tests/e2e/inference_evaluation_human_feedback.rs +++ b/tensorzero-core/tests/e2e/inference_evaluation_human_feedback.rs @@ -22,7 +22,7 @@ async fn e2e_test_float_human_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -137,7 +137,7 @@ async fn e2e_test_boolean_human_feedback() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); diff --git a/tensorzero-core/tests/e2e/mixture_of_n.rs b/tensorzero-core/tests/e2e/mixture_of_n.rs index 1991d7b3103..894332464c8 100644 --- a/tensorzero-core/tests/e2e/mixture_of_n.rs +++ b/tensorzero-core/tests/e2e/mixture_of_n.rs @@ -132,7 +132,7 @@ async fn e2e_test_mixture_of_n_dummy_candidates_dummy_judge_inner( { "role": "user", "content": [ - {"type": "text", "value": format!("Please write me a sentence about Megumin making an explosion: {random_input}")}, + {"type": "text", "text": format!("Please write me a sentence about Megumin making an explosion: {random_input}")}, ] } ] @@ -365,7 +365,7 @@ async fn e2e_test_mixture_of_n_dummy_candidates_real_judge_inner(stream: bool) { { "role": "user", "content": [ - {"type": "text", "value": "Please write me a sentence about the anime character Megumin."}, + {"type": "text", "text": "Please write me a sentence about the anime character Megumin."}, {"type": "unknown", "model_provider_name": "tensorzero::model_name::test::provider_name::good", "data": {"type": "text", "text": "My extra test-model input"}}, ] } @@ -626,7 +626,7 @@ async fn e2e_test_mixture_of_n_json_real_judge() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What are the first names of the Beatles? Respond in the format {\"names\": List[str]}"}] + "content": [{"type": "text", "text": "What are the first names of the Beatles? Respond in the format {\"names\": List[str]}"}] } ] } @@ -1008,7 +1008,7 @@ async fn e2e_test_mixture_of_n_bad_fuser_streaming() { { "role": "user", "content": [ - {"type": "text", "value": "Please write me a sentence about Megumin making an explosion"}, + {"type": "text", "text": "Please write me a sentence about Megumin making an explosion"}, ] } ] @@ -1166,7 +1166,7 @@ async fn e2e_test_mixture_of_n_single_candidate_inner( { "role": "user", "content": [ - {"type": "text", "value": "Please write me a sentence about Megumin making an explosion"}, + {"type": "text", "text": "Please write me a sentence about Megumin making an explosion"}, ] } ] diff --git a/tensorzero-core/tests/e2e/openai_compatible.rs b/tensorzero-core/tests/e2e/openai_compatible.rs index d713f158c77..4241d942ab3 100644 --- a/tensorzero-core/tests/e2e/openai_compatible.rs +++ b/tensorzero-core/tests/e2e/openai_compatible.rs @@ -109,7 +109,7 @@ async fn test_openai_compatible_route_with_function_name_as_model(model: &str) { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -382,7 +382,7 @@ async fn test_openai_compatible_route_with_default_function( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -550,7 +550,7 @@ async fn test_openai_compatible_route_with_json_mode_on() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -687,7 +687,7 @@ async fn test_openai_compatible_route_with_json_schema() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); diff --git a/tensorzero-core/tests/e2e/optimization/dicl.rs b/tensorzero-core/tests/e2e/optimization/dicl.rs index 3e7e5ffb499..56d7ebdada2 100644 --- a/tensorzero-core/tests/e2e/optimization/dicl.rs +++ b/tensorzero-core/tests/e2e/optimization/dicl.rs @@ -1,4 +1,3 @@ -use serde_json::json; use std::collections::HashMap; use tensorzero_core::db::clickhouse::test_helpers::get_clickhouse; use tensorzero_core::inference::types::{ @@ -30,9 +29,9 @@ fn create_test_rendered_sample(input: &str, output: &str) -> RenderedSample { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!(input), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: input.to_string(), + })], }], }, output: Some(output_vec.clone()), diff --git a/tensorzero-core/tests/e2e/prometheus.rs b/tensorzero-core/tests/e2e/prometheus.rs index dd0efabdf9a..a396691470d 100644 --- a/tensorzero-core/tests/e2e/prometheus.rs +++ b/tensorzero-core/tests/e2e/prometheus.rs @@ -177,7 +177,7 @@ async fn test_prometheus_metrics_feedback_boolean() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -236,7 +236,7 @@ async fn test_prometheus_metrics_feedback_boolean_dryrun() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -295,7 +295,7 @@ async fn test_prometheus_metrics_feedback_float() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -353,7 +353,7 @@ async fn test_prometheus_metrics_feedback_float_dryrun() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); @@ -417,7 +417,7 @@ async fn test_prometheus_metrics_feedback_comment() { "function_name": "json_success", "input": { "system": {"assistant_name": "Alfred Pennyworth"}, - "messages": [{"role": "user", "content": [{"type": "text", "arguments": {"country": "Japan"}}]}] + "messages": [{"role": "user", "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}]}] }, "stream": false, }); diff --git a/tensorzero-core/tests/e2e/providers/anthropic.rs b/tensorzero-core/tests/e2e/providers/anthropic.rs index 166c1d45796..2a3da34b349 100644 --- a/tensorzero-core/tests/e2e/providers/anthropic.rs +++ b/tensorzero-core/tests/e2e/providers/anthropic.rs @@ -444,7 +444,7 @@ async fn test_thinking_signature() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] + "content": [{"type": "text", "text": "Hi I'm visiting Brooklyn from Brazil. What's the weather?"}] } ] }); @@ -621,11 +621,11 @@ async fn test_redacted_thinking() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB"}] + "content": [{"type": "text", "text": "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB"}] }, { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -842,7 +842,7 @@ async fn test_streaming_thinking() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ]} ); diff --git a/tensorzero-core/tests/e2e/providers/aws_bedrock.rs b/tensorzero-core/tests/e2e/providers/aws_bedrock.rs index 7715d6b36ec..a5fdcb7b0c2 100644 --- a/tensorzero-core/tests/e2e/providers/aws_bedrock.rs +++ b/tensorzero-core/tests/e2e/providers/aws_bedrock.rs @@ -168,7 +168,7 @@ async fn test_inference_with_explicit_region() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ] }); @@ -275,7 +275,7 @@ async fn test_inference_with_empty_system() { "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"topic": "artificial intelligence"}}] + "content": [{"type": "template", "name": "user", "arguments": {"topic": "artificial intelligence"}}] } ]}, "stream": false, diff --git a/tensorzero-core/tests/e2e/providers/batch.rs b/tensorzero-core/tests/e2e/providers/batch.rs index 66ff0e893ac..98d9bc4dc38 100644 --- a/tensorzero-core/tests/e2e/providers/batch.rs +++ b/tensorzero-core/tests/e2e/providers/batch.rs @@ -740,7 +740,7 @@ pub async fn test_start_simple_image_batch_inference_request_with_provider( { "role": "user", "content": [ - {"type": "text", "value": "What kind of animal is in this image?"}, + {"type": "text", "text": "What kind of animal is in this image?"}, { "type": "file", "file": { @@ -1481,25 +1481,25 @@ pub async fn test_tool_use_batch_inference_request_with_provider(provider: E2ETe "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] }, { "system": {"assistant_name": "Dr. Mehta"}, - "messages": [{"role": "user", "content": [{"type": "text", "value": "What is your name?"}]}] + "messages": [{"role": "user", "content": [{"type": "text", "text": "What is your name?"}]}] }, { "system": {"assistant_name": "Dr. Mehta"}, - "messages": [{"role": "user", "content": [{"type": "text", "value": "What is your name?"}]}] + "messages": [{"role": "user", "content": [{"type": "text", "text": "What is your name?"}]}] }, { "system": {"assistant_name": "Dr. Mehta"}, - "messages": [{"role": "user", "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}]}] + "messages": [{"role": "user", "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}]}] }, { "system": {"assistant_name": "Dr. Mehta"}, - "messages": [{"role": "user", "content": [{"type": "text", "value": "What is the temperature like in Tokyo (in Celsius)? Use the `get_temperature` tool."}]}] + "messages": [{"role": "user", "content": [{"type": "text", "text": "What is the temperature like in Tokyo (in Celsius)? Use the `get_temperature` tool."}]}] } ]); let expected_input_messages = [ @@ -2149,7 +2149,7 @@ pub async fn test_allowed_tools_batch_inference_request_with_provider(provider: "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] + "content": [{"type": "text", "text": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] } ] }); @@ -2549,7 +2549,7 @@ pub async fn test_multi_turn_parallel_tool_use_batch_inference_request_with_prov "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Fahrenheit)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Fahrenheit)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] }, { "role": "assistant", @@ -2795,7 +2795,7 @@ pub async fn test_tool_multi_turn_batch_inference_request_with_provider(provider "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] }, { "role": "assistant", @@ -3351,7 +3351,7 @@ pub async fn test_dynamic_tool_use_batch_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] } ] }); @@ -3699,7 +3699,7 @@ pub async fn test_parallel_tool_use_batch_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] } ] }); @@ -4012,7 +4012,7 @@ pub async fn test_json_mode_batch_inference_request_with_provider(provider: E2ET "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}], "tags": [{"test_type": "json_mode_v2"}] @@ -4360,7 +4360,7 @@ pub async fn test_dynamic_json_mode_batch_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}], "output_schemas": [output_schema.clone()], diff --git a/tensorzero-core/tests/e2e/providers/common.rs b/tensorzero-core/tests/e2e/providers/common.rs index f25a1aa1f80..2dd073f7286 100644 --- a/tensorzero-core/tests/e2e/providers/common.rs +++ b/tensorzero-core/tests/e2e/providers/common.rs @@ -2584,7 +2584,7 @@ pub async fn check_base64_pdf_response( { "role": "user", "content": [ - {"type": "text", "value": "Describe the contents of the PDF"}, + {"type": "text", "text": "Describe the contents of the PDF"}, { "type": "file", "file": { @@ -2739,7 +2739,7 @@ pub async fn check_base64_image_response( { "role": "user", "content": [ - {"type": "text", "value": "Describe the contents of the image"}, + {"type": "text", "text": "Describe the contents of the image"}, { "type": "file", "file": { @@ -2894,7 +2894,7 @@ pub async fn check_url_image_response( { "role": "user", "content": [ - {"type": "text", "value": "Describe the contents of the image"}, + {"type": "text", "text": "Describe the contents of the image"}, { "type": "file", "file": { @@ -3058,7 +3058,7 @@ pub async fn check_simple_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan?"}] } ] }); @@ -3263,7 +3263,7 @@ pub async fn check_simple_image_inference_response( { "role": "user", "content": [ - {"type": "text", "value": "What kind of animal is in this image?"}, + {"type": "text", "text": "What kind of animal is in this image?"}, { "type": "file", "file": { @@ -3644,7 +3644,7 @@ pub async fn test_simple_streaming_inference_request_with_provider_cache( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan?"}] } ] }); @@ -4151,7 +4151,7 @@ pub async fn test_inference_params_streaming_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan?"}] } ] }); @@ -4419,7 +4419,7 @@ pub async fn check_tool_use_tool_choice_auto_used_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] } @@ -4736,7 +4736,7 @@ pub async fn test_tool_use_tool_choice_auto_used_streaming_inference_request_wit "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] } @@ -5028,7 +5028,7 @@ pub async fn check_tool_use_tool_choice_auto_unused_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is your name?"}] + "content": [{"type": "text", "text": "What is your name?"}] } ] } @@ -5324,7 +5324,7 @@ pub async fn test_tool_use_tool_choice_auto_unused_streaming_inference_request_w "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is your name?"}] + "content": [{"type": "text", "text": "What is your name?"}] } ] } @@ -5639,7 +5639,7 @@ pub async fn check_tool_use_tool_choice_required_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is your name?"}] + "content": [{"type": "text", "text": "What is your name?"}] } ] } @@ -5957,7 +5957,7 @@ pub async fn test_tool_use_tool_choice_required_streaming_inference_request_with "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is your name?"}] + "content": [{"type": "text", "text": "What is your name?"}] } ] } @@ -6269,7 +6269,7 @@ pub async fn check_tool_use_tool_choice_none_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] } @@ -6546,7 +6546,7 @@ pub async fn test_tool_use_tool_choice_none_streaming_inference_request_with_pro "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] } @@ -6862,7 +6862,7 @@ pub async fn check_tool_use_tool_choice_specific_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the temperature like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the temperature like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] } ] } @@ -7240,7 +7240,7 @@ pub async fn test_tool_use_tool_choice_specific_streaming_inference_request_with "messages": [ { "role": "user", - "content": [{"type": "text", "value": prompt}] + "content": [{"type": "text", "text": prompt}] } ] } @@ -7596,7 +7596,7 @@ pub async fn check_tool_use_tool_choice_allowed_tools_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] + "content": [{"type": "text", "text": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] } ] } @@ -7921,7 +7921,7 @@ pub async fn test_tool_use_allowed_tools_streaming_inference_request_with_provid "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] + "content": [{"type": "text", "text": "What can you tell me about the weather in Tokyo (e.g. temperature, humidity, wind)? Use the provided tools and return what you can (not necessarily everything)."}] } ] } @@ -8228,7 +8228,7 @@ pub async fn check_tool_use_multi_turn_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] }, { "role": "assistant", @@ -8526,7 +8526,7 @@ pub async fn test_tool_multi_turn_streaming_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the `get_temperature` tool."}] }, { "role": "assistant", @@ -8980,7 +8980,7 @@ pub async fn check_dynamic_tool_use_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] } ] } @@ -9296,7 +9296,7 @@ pub async fn test_dynamic_tool_use_streaming_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use the provided `get_temperature` tool. Do not say anything else, just call the function."}] } ] } @@ -9647,7 +9647,7 @@ pub async fn check_parallel_tool_use_inference_response( "role": "user", "content": [{ "type": "text", - "value": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions." + "text": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions." }] } ] @@ -10032,7 +10032,7 @@ pub async fn test_parallel_tool_use_streaming_inference_request_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] + "content": [{"type": "text", "text": "What is the weather like in Tokyo (in Celsius)? Use both the provided `get_temperature` and `get_humidity` tools. Do not say anything else, just call the two functions."}] } ] } @@ -10286,7 +10286,7 @@ pub async fn test_json_mode_inference_request_with_provider(provider: E2ETestPro "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -10544,7 +10544,7 @@ pub async fn test_dynamic_json_mode_inference_request_with_provider(provider: E2 "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -10802,7 +10802,7 @@ pub async fn test_json_mode_streaming_inference_request_with_provider(provider: "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": true, @@ -11253,7 +11253,7 @@ async fn check_short_inference_response( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the name of the capital city of Japan? Explain your answer."}] + "content": [{"type": "text", "text": "What is the name of the capital city of Japan? Explain your answer."}] } ] }); @@ -12063,7 +12063,7 @@ pub async fn test_json_mode_off_inference_request_with_provider(provider: E2ETes "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ] }, diff --git a/tensorzero-core/tests/e2e/providers/openai.rs b/tensorzero-core/tests/e2e/providers/openai.rs index 52d0291a0b7..c2101843573 100644 --- a/tensorzero-core/tests/e2e/providers/openai.rs +++ b/tensorzero-core/tests/e2e/providers/openai.rs @@ -512,7 +512,7 @@ async fn test_default_function_model_name_shorthand() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -626,7 +626,7 @@ async fn test_default_function_model_name_non_shorthand() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -862,7 +862,7 @@ async fn test_chat_function_json_override_with_mode(json_mode: ModelInferenceReq "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan (possibly as JSON)?"}] + "content": [{"type": "text", "text": "What is the capital of Japan (possibly as JSON)?"}] } ] }); @@ -1001,7 +1001,7 @@ async fn test_o4_mini_inference() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -1119,7 +1119,7 @@ async fn test_o3_mini_inference_with_reasoning_effort() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -1588,7 +1588,7 @@ async fn test_content_block_text_field() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of Japan?"}] + "content": [{"type": "text", "text": "What is the capital of Japan?"}] } ] }); @@ -2005,7 +2005,7 @@ pub async fn test_start_batch_inference_write_file() { { "role": "user", "content": [ - {"type": "text", "value": "Tell me about this image"}, + {"type": "text", "text": "Tell me about this image"}, { "type": "file", "file": { @@ -3035,7 +3035,7 @@ async fn test_responses_api_shorthand() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital of France?"}] + "content": [{"type": "text", "text": "What is the capital of France?"}] } ] }); diff --git a/tensorzero-core/tests/e2e/providers/reasoning.rs b/tensorzero-core/tests/e2e/providers/reasoning.rs index 2ff4f619211..d780e62cf25 100644 --- a/tensorzero-core/tests/e2e/providers/reasoning.rs +++ b/tensorzero-core/tests/e2e/providers/reasoning.rs @@ -126,7 +126,7 @@ pub async fn test_reasoning_inference_request_simple_with_provider(provider: E2E "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the capital city of Japan?"}] } ] }); @@ -391,7 +391,7 @@ pub async fn test_streaming_reasoning_inference_request_simple_with_provider( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "What is the capital city of Japan?"}] + "content": [{"type": "text", "text": "What is the capital city of Japan?"}] } ] }); @@ -546,7 +546,7 @@ pub async fn test_reasoning_inference_request_with_provider_json_mode(provider: "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": false, @@ -757,7 +757,7 @@ pub async fn test_streaming_reasoning_inference_request_with_provider_json_mode( "messages": [ { "role": "user", - "content": [{"type": "text", "arguments": {"country": "Japan"}}] + "content": [{"type": "template", "name": "user", "arguments": {"country": "Japan"}}] } ]}, "stream": true, diff --git a/tensorzero-core/tests/e2e/render_inferences.rs b/tensorzero-core/tests/e2e/render_inferences.rs index 107b7b26c06..e5280b1b558 100644 --- a/tensorzero-core/tests/e2e/render_inferences.rs +++ b/tensorzero-core/tests/e2e/render_inferences.rs @@ -13,7 +13,7 @@ use tensorzero_core::inference::types::stored_input::{ }; use tensorzero_core::inference::types::{ResolvedContentBlock, ResolvedRequestMessage}; use tensorzero_core::{ - inference::types::{ContentBlockChatOutput, JsonInferenceOutput, Text}, + inference::types::{ContentBlockChatOutput, JsonInferenceOutput, TemplateInput, Text}, tool::{ToolCallConfigDatabaseInsert, ToolCallOutput, ToolChoice}, }; use tracing_test::traced_test; @@ -47,9 +47,9 @@ pub async fn test_render_samples_no_function() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: vec![], @@ -83,9 +83,9 @@ pub async fn test_render_samples_no_variant() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: vec![], @@ -127,9 +127,9 @@ pub async fn test_render_samples_missing_variable() { system: Some(json!({"foo": "bar"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: vec![], @@ -166,9 +166,9 @@ pub async fn test_render_samples_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: vec![], @@ -186,9 +186,10 @@ pub async fn test_render_samples_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"country": "Japan"}), - }], + content: vec![StoredInputMessageContent::Template(TemplateInput { + name: "user".to_string(), + arguments: serde_json::Map::from_iter(vec![("country".to_string(), json!("Japan"))]), + })], }], }, output: JsonInferenceOutput { @@ -212,9 +213,9 @@ pub async fn test_render_samples_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: vec![ContentBlockChatOutput::ToolCall(ToolCallOutput { @@ -250,9 +251,9 @@ pub async fn test_render_samples_normal() { messages: vec![StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: json!("What is this a picture of?"), - }, + StoredInputMessageContent::Text(Text { + text: "What is this a picture of?".to_string(), + }), StoredInputMessageContent::File(Box::new(StoredFile { file: Base64FileMetadata { url: None, @@ -449,23 +450,23 @@ pub async fn test_render_samples_template_no_schema() { StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: "First user message".into(), - }, - StoredInputMessageContent::Text { - value: "Second user message".into(), - }, + StoredInputMessageContent::Text(Text { + text: "First user message".to_string(), + }), + StoredInputMessageContent::Text(Text { + text: "Second user message".to_string(), + }), ], }, StoredInputMessage { role: Role::Assistant, content: vec![ - StoredInputMessageContent::Text { - value: "First assistant message".into(), - }, - StoredInputMessageContent::Text { - value: "Second assistant message".into(), - }, + StoredInputMessageContent::Text(Text { + text: "First assistant message".to_string(), + }), + StoredInputMessageContent::Text(Text { + text: "Second assistant message".to_string(), + }), ], }, ], @@ -569,9 +570,9 @@ pub async fn test_render_datapoints_no_function() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![]), @@ -610,9 +611,9 @@ pub async fn test_render_datapoints_no_variant() { system: None, messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![]), @@ -659,9 +660,9 @@ pub async fn test_render_datapoints_missing_variable() { system: Some(json!({"foo": "bar"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![]), @@ -703,9 +704,9 @@ pub async fn test_render_datapoints_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![]), @@ -728,9 +729,10 @@ pub async fn test_render_datapoints_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!({"country": "Japan"}), - }], + content: vec![StoredInputMessageContent::Template(TemplateInput { + name: "user".to_string(), + arguments: serde_json::Map::from_iter(vec![("country".to_string(), json!("Japan"))]), + })], }], }, output: Some(JsonInferenceOutput { @@ -756,9 +758,9 @@ pub async fn test_render_datapoints_normal() { system: Some(json!({"assistant_name": "Dr. Mehta"})), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("Hello, world!"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "Hello, world!".to_string(), + })], }], }, output: Some(vec![ContentBlockChatOutput::ToolCall(ToolCallOutput { @@ -797,9 +799,9 @@ pub async fn test_render_datapoints_normal() { messages: vec![StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: json!("What is this a picture of?"), - }, + StoredInputMessageContent::Text(Text { + text: "What is this a picture of?".to_string(), + }), StoredInputMessageContent::File(Box::new(StoredFile { file: Base64FileMetadata { url: None, @@ -986,23 +988,23 @@ pub async fn test_render_datapoints_template_no_schema() { StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: "First user message".into(), - }, - StoredInputMessageContent::Text { - value: "Second user message".into(), - }, + StoredInputMessageContent::Text(Text { + text: "First user message".to_string(), + }), + StoredInputMessageContent::Text(Text { + text: "Second user message".to_string(), + }), ], }, StoredInputMessage { role: Role::Assistant, content: vec![ - StoredInputMessageContent::Text { - value: "First assistant message".into(), - }, - StoredInputMessageContent::Text { - value: "Second assistant message".into(), - }, + StoredInputMessageContent::Text(Text { + text: "First assistant message".to_string(), + }), + StoredInputMessageContent::Text(Text { + text: "Second assistant message".to_string(), + }), ], }, ], diff --git a/tensorzero-core/tests/e2e/retries.rs b/tensorzero-core/tests/e2e/retries.rs index 2024f30c8d2..0464429da87 100644 --- a/tensorzero-core/tests/e2e/retries.rs +++ b/tensorzero-core/tests/e2e/retries.rs @@ -86,7 +86,7 @@ async fn e2e_test_inference_flaky() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ] } @@ -257,7 +257,7 @@ async fn e2e_test_streaming_flaky() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Hello, world!"}] + "content": [{"type": "text", "text": "Hello, world!"}] } ]} ); @@ -420,7 +420,7 @@ async fn e2e_test_best_of_n_dummy_candidates_flaky_judge() { "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Please write me a sentence about Megumin making an explosion."}] + "content": [{"type": "text", "text": "Please write me a sentence about Megumin making an explosion."}] } ] } diff --git a/tensorzero-core/tests/e2e/template.rs b/tensorzero-core/tests/e2e/template.rs index 6a53e621495..74ff04b2127 100644 --- a/tensorzero-core/tests/e2e/template.rs +++ b/tensorzero-core/tests/e2e/template.rs @@ -113,11 +113,11 @@ async fn e2e_test_template_no_schema() { "system":"My system message", "messages":[ {"role":"user","content":[ - {"type":"text","value":"First user message"}, - {"type":"text","value":"Second user message"}, + {"type":"text","text":"First user message"}, + {"type":"text","text":"Second user message"}, {"type":"template","name":"my_custom_template","arguments":{"first_variable":"my_content","second_variable":"my_other_content"}} ]}, - {"role":"assistant","content":[{"type":"text","value":"First assistant message"},{"type":"text","value":"Second assistant message"}]}] + {"role":"assistant","content":[{"type":"text","text":"First assistant message"},{"type":"text","text":"Second assistant message"}]}] }) ); } @@ -398,7 +398,7 @@ async fn e2e_test_invalid_json_user_input_template_no_schema() { "role": "user", "content": [ {"type": "text", "text": "First user message"}, - {"type": "text", "arguments": {"my_invalid": "user message"}}, + {"type": "template", "name": "user", "arguments": {"my_invalid": "user message"}}, ] }, @@ -433,7 +433,7 @@ async fn e2e_test_invalid_user_input_template_no_schema() { "role": "user", "content": [ {"type": "text", "text": "First user message"}, - {"type": "text", "arguments": {"my_invalid": "user message"}}, + {"type": "template", "name": "user", "arguments": {"my_invalid": "user message"}}, ] }, @@ -468,7 +468,7 @@ async fn e2e_test_invalid_assistant_input_template_no_schema() { { "role": "assistant", "content": [ - {"type": "text", "arguments": {"my_invalid": "assistant message"}}, + {"type": "template", "name": "assistant", "arguments": {"my_invalid": "assistant message"}}, ] } ]}, @@ -502,7 +502,7 @@ async fn e2e_test_invalid_json_assistant_input_template_no_schema() { { "role": "assistant", "content": [ - {"type": "text", "arguments": {"my_invalid": "assistant message"}}, + {"type": "template", "name": "assistant", "arguments": {"my_invalid": "assistant message"}}, ] } ]}, diff --git a/tensorzero-core/tests/optimization/common/dicl.rs b/tensorzero-core/tests/optimization/common/dicl.rs index 049560e3779..b6fb065549f 100644 --- a/tensorzero-core/tests/optimization/common/dicl.rs +++ b/tensorzero-core/tests/optimization/common/dicl.rs @@ -817,7 +817,7 @@ async fn validate_inference_clickhouse( "messages": [ { "role": "user", - "content": [{"type": "text", "value": "Who was the author of the Harry Potter series?"}] + "content": [{"type": "text", "text": "Who was the author of the Harry Potter series?"}] } ] }); @@ -1189,9 +1189,9 @@ fn create_pinocchio_example( system: system.clone(), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!(question), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: question.to_string(), + })], }], }, output: Some(output), diff --git a/tensorzero-core/tests/optimization/common/mod.rs b/tensorzero-core/tests/optimization/common/mod.rs index 95dfc2e9e45..79e72720a67 100644 --- a/tensorzero-core/tests/optimization/common/mod.rs +++ b/tensorzero-core/tests/optimization/common/mod.rs @@ -307,9 +307,9 @@ fn generate_text_example() -> RenderedSample { system: Some(json!(system_prompt)), messages: vec![StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: "What is the capital of France?".into(), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the capital of France?".to_string(), + })], }], }, output: Some(output.clone()), @@ -399,16 +399,16 @@ fn generate_tool_call_example() -> RenderedSample { messages: vec![ StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("What is the weather in Paris?"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the weather in Paris?".to_string(), + })], }, StoredInputMessage { role: Role::Assistant, content: vec![ - StoredInputMessageContent::Text { - value: json!("Let me look that up for you."), - }, + StoredInputMessageContent::Text(Text { + text: "Let me look that up for you.".to_string(), + }), StoredInputMessageContent::ToolCall(ToolCall { name: "get_weather".to_string(), arguments: serde_json::json!({ @@ -432,15 +432,15 @@ fn generate_tool_call_example() -> RenderedSample { }, StoredInputMessage { role: Role::Assistant, - content: vec![StoredInputMessageContent::Text { - value: json!("The weather in Paris is sunny, 25 degrees Celsius."), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "The weather in Paris is sunny, 25 degrees Celsius.".to_string(), + })], }, StoredInputMessage { role: Role::User, - content: vec![StoredInputMessageContent::Text { - value: json!("What is the weather in London?"), - }], + content: vec![StoredInputMessageContent::Text(Text { + text: "What is the weather in London?".to_string(), + })], }, ], }, @@ -512,9 +512,9 @@ fn generate_image_example() -> RenderedSample { messages: vec![StoredInputMessage { role: Role::User, content: vec![ - StoredInputMessageContent::Text { - value: json!("What is the main color of this image?"), - }, + StoredInputMessageContent::Text(Text { + text: "What is the main color of this image?".to_string(), + }), StoredInputMessageContent::File(Box::new(StoredFile { file: Base64FileMetadata { url: None, diff --git a/ui/app/routes/api/tensorzero/inference.utils.tsx b/ui/app/routes/api/tensorzero/inference.utils.tsx index fdd1ede2f61..aa1d35a7172 100644 --- a/ui/app/routes/api/tensorzero/inference.utils.tsx +++ b/ui/app/routes/api/tensorzero/inference.utils.tsx @@ -218,6 +218,10 @@ function tensorZeroStoredContentToInputContent( ): InputMessageContent { switch (content.type) { case "text": + return { + type: "text", + value: content.text, + }; case "template": case "raw_text": return content; diff --git a/ui/app/utils/clickhouse/common.ts b/ui/app/utils/clickhouse/common.ts index efa0ee03a09..9440a6d75c1 100644 --- a/ui/app/utils/clickhouse/common.ts +++ b/ui/app/utils/clickhouse/common.ts @@ -30,7 +30,9 @@ export type Role = z.infer; export const textInputSchema = z.object({ type: z.literal("text"), - value: z.any(), // Value type from Rust maps to any in TS + // TODO: get rid of this type completely, we should not run queries in the UI... + value: JsonValueSchema.optional(), + text: z.string().optional(), }); export type TextInput = z.infer; @@ -60,7 +62,7 @@ export type DisplayTemplate = z.infer; // 3) is missing from the config so we don't know export const displayMissingFunctionTextInputSchema = z.object({ type: z.literal("missing_function_text"), - value: z.any(), + value: z.string(), }); export type DisplayMissingFunctionTextInput = z.infer< typeof displayMissingFunctionTextInputSchema @@ -470,9 +472,9 @@ function displayInputMessageContentToStoredInputMessageContent( ): StoredInputMessageContent { switch (content.type) { case "text": - return { type: "text", value: content.text }; + return { type: "text", text: content.text }; case "missing_function_text": - return { type: "text", value: content.value }; + return { type: "text", text: content.value }; case "file": return { type: "file", @@ -514,9 +516,8 @@ function displayInputMessageToStoredInputMessage( } /** * Converts DisplayInput to StoredInput before we save the datapoints. This is mostly to handle: - * 1. DisplayInput has { type: "text", "text": "..." } https://github.com/tensorzero/tensorzero/blob/b018a80797912fef0a86ec0115d9973378fde186/ui/app/utils/clickhouse/common.ts#L41-L44 - * but StoredInput has { type: "text", "value": "..." } https://github.com/tensorzero/tensorzero/blob/b018a80797912fef0a86ec0115d9973378fde186/tensorzero-core/src/inference/types/stored_input.rs#L107-L109 - * 2. missing_function_text and file_error are Fronend-only types, and we convert them back to text and file types for storage. + * 1. DisplayInput has { type: "text", "text": "..." } which matches StoredInput's { type: "text", "text": "..." } format + * 2. missing_function_text and file_error are frontend-only types, and we convert them back to text and file types for storage. * 3. StorageKind currently has a null / undefined mismatch, so we convert everything to undefined before going to the backend. */ export function displayInputToStoredInput( diff --git a/ui/app/utils/clickhouse/datasets.server.ts b/ui/app/utils/clickhouse/datasets.server.ts index 9cb12f41326..be9b3447f1d 100644 --- a/ui/app/utils/clickhouse/datasets.server.ts +++ b/ui/app/utils/clickhouse/datasets.server.ts @@ -18,7 +18,7 @@ import type { } from "./datasets"; import { displayInputToStoredInput } from "./common"; import { getConfig, getFunctionConfig } from "../config/index.server"; -import { resolveInput } from "../resolve.server"; +import { resolveStoredInput } from "../resolve.server"; // TODO(shuyangli): Consider removing this file and fully use DatabaseClient from tensorzero-node/lib. @@ -203,12 +203,7 @@ export async function getAdjacentDatapointIds( export async function datapointToParsedDatasetRow( datapoint: Datapoint, ): Promise { - const config = await getConfig(); - const functionConfig = await getFunctionConfig( - datapoint.function_name, - config, - ); - const resolvedInput = await resolveInput(datapoint.input, functionConfig); + const resolvedInput = await resolveStoredInput(datapoint.input); if (datapoint.type === "chat") { const chatDatapoint: ParsedChatInferenceDatapointRow = { diff --git a/ui/app/utils/clickhouse/inference.server.ts b/ui/app/utils/clickhouse/inference.server.ts index 1beefdbed8d..a3996d325c6 100644 --- a/ui/app/utils/clickhouse/inference.server.ts +++ b/ui/app/utils/clickhouse/inference.server.ts @@ -417,7 +417,7 @@ export async function queryInferenceById( c.function_name, c.variant_name, c.episode_id, - c.input, + c.input, -- CAREFUL: THIS MIGHT HAVE LEGACY DATA FORMAT! c.output, c.tool_params, c.inference_params, @@ -442,7 +442,7 @@ export async function queryInferenceById( j.function_name, j.variant_name, j.episode_id, - j.input, + j.input, -- CAREFUL: THIS MIGHT HAVE LEGACY DATA FORMAT! j.output, NULL AS tool_params, -- Placeholder for Chat column j.inference_params, diff --git a/ui/app/utils/resolve.server.ts b/ui/app/utils/resolve.server.ts index 38781b57292..f44da87d651 100644 --- a/ui/app/utils/resolve.server.ts +++ b/ui/app/utils/resolve.server.ts @@ -12,7 +12,13 @@ import type { Role, TextInput, } from "./clickhouse/common"; -import type { FunctionConfig } from "tensorzero-node"; +import type { + FunctionConfig, + JsonValue, + StoredInput, + StoredInputMessage, + StoredInputMessageContent, +} from "tensorzero-node"; import { getTensorZeroClient } from "./tensorzero.server"; export async function resolveInput( @@ -119,7 +125,7 @@ async function resolveContent( try { return { ...content, - file: await resolveFile(content as FileContent), + file: await resolveFile(content), }; } catch (error) { return { @@ -175,7 +181,7 @@ async function resolveModelInferenceContent( try { return { ...content, - file: await resolveFile(content as FileContent), + file: await resolveFile(content), }; } catch (error) { return { @@ -199,6 +205,7 @@ async function resolveFile(content: FileContent): Promise { // In the current data model we can't distinguish between a message being a structured one from a schema // or an unstructured one without a schema without knowing the function config. // So as we prepare the input for display, we check this and return an unambiguous type of structured or unstructured text. +// TODO (Gabriel): this function uses legacy types and should be deprecated ASAP. It won't handle sad paths very well. function prepareDisplayText( textBlock: TextInput, role: Role, @@ -208,7 +215,17 @@ function prepareDisplayText( if (!functionConfig) { return { type: "missing_function_text", - value: textBlock.value, + value: + typeof textBlock.value === "string" + ? textBlock.value + : JSON.stringify(textBlock.value), + }; + } + + if (textBlock.text !== undefined) { + return { + type: "text", + text: textBlock.text, }; } @@ -217,7 +234,18 @@ function prepareDisplayText( return { type: "template", name: "user", - arguments: textBlock.value, + arguments: (() => { + if ( + typeof textBlock.value === "object" && + textBlock.value !== null && + !Array.isArray(textBlock.value) + ) { + return textBlock.value as Record; + } + throw new Error( + `Invalid arguments for user template: expected object, got ${typeof textBlock.value}`, + ); + })(), }; } @@ -228,13 +256,92 @@ function prepareDisplayText( return { type: "template", name: "assistant", - arguments: textBlock.value, + arguments: (() => { + if ( + typeof textBlock.value === "object" && + textBlock.value !== null && + !Array.isArray(textBlock.value) + ) { + return textBlock.value as Record; + } + throw new Error( + `Invalid arguments for assistant template: expected object, got ${typeof textBlock.value}`, + ); + })(), }; } // Otherwise it's just unstructured text return { type: "text", - text: textBlock.value, + text: + typeof textBlock.value === "string" + ? textBlock.value + : JSON.stringify(textBlock.value), + }; +} + +// ===== StoredInput ===== +// TODO: These functions should be deprecated as we clean up the types... + +export async function resolveStoredInput( + input: StoredInput, +): Promise { + const resolvedMessages = await resolveStoredInputMessages(input.messages); + return { + ...input, + messages: resolvedMessages, + }; +} + +export async function resolveStoredInputMessages( + messages: StoredInputMessage[], +): Promise { + return Promise.all( + messages.map(async (message) => { + return resolveStoredInputMessage(message); + }), + ); +} + +async function resolveStoredInputMessage( + message: StoredInputMessage, +): Promise { + const resolvedContent = await Promise.all( + message.content.map(async (content) => { + return resolveStoredInputMessageContent(content); + }), + ); + return { + ...message, + content: resolvedContent, }; } + +async function resolveStoredInputMessageContent( + content: StoredInputMessageContent, +): Promise { + switch (content.type) { + case "tool_call": + case "tool_result": + case "raw_text": + case "thought": + case "unknown": + case "template": + case "text": + return content; + case "file": + try { + return { + ...content, + file: await resolveFile(content), + }; + } catch (error) { + return { + ...content, + type: "file_error", + error: error instanceof Error ? error.message : String(error), + }; + } + } +} From 1ea0c58810c9f322bb432350c1144ec23eceb949 Mon Sep 17 00:00:00 2001 From: Gabriel Bianconi <1275491+GabrielBianconi@users.noreply.github.com> Date: Sun, 26 Oct 2025 11:18:00 -0400 Subject: [PATCH 2/2] Extend wait time for merge queue workflow (#4186) Increased wait time for workflow creation from 3 seconds to 30 seconds. --- .github/workflows/slash-command-merge-queue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/slash-command-merge-queue.yml b/.github/workflows/slash-command-merge-queue.yml index 98a09255dad..e3ff9e43407 100644 --- a/.github/workflows/slash-command-merge-queue.yml +++ b/.github/workflows/slash-command-merge-queue.yml @@ -34,8 +34,8 @@ jobs: set -e gh workflow run merge-queue.yml --ref ${{ github.event.client_payload.pull_request.head.ref }} - # Wait a moment for the workflow to be created - sleep 3 + # Wait 30s for the workflow to be created + sleep 30 # Get the most recent workflow run for this branch RUN_ID=$(gh run list --workflow=merge-queue.yml --branch=${{ github.event.client_payload.pull_request.head.ref }} --limit=1 --json databaseId --jq '.[0].databaseId')