From eb04f4cd30c41b090989f3e22d67817ccd32565e Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 04:51:53 +0000 Subject: [PATCH 1/6] test(deepseek): mark ported upstream tests with the full describe chain 75 of the 93 tests in deepseek_chat_test.rs port an upstream case of @ai-sdk/deepseek@3.0.56 and now carry an RFC-0045 marker, in one mod per upstream describe. The other 18 are aimux's own or check only part of a snapshot case and stay unmarked. Bodies and fn names are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- aimux-providers/tests/deepseek_chat_test.rs | 4281 ++++++++++--------- 1 file changed, 2213 insertions(+), 2068 deletions(-) diff --git a/aimux-providers/tests/deepseek_chat_test.rs b/aimux-providers/tests/deepseek_chat_test.rs index 99d631de0..8c9e4223f 100644 --- a/aimux-providers/tests/deepseek_chat_test.rs +++ b/aimux-providers/tests/deepseek_chat_test.rs @@ -330,2257 +330,2402 @@ fn logprob(token: &str, logprob: f64, bytes: Value) -> Value { }) } -// =========================================================================== -// describe('model IDs') / describe('supportedUrls') -// =========================================================================== +fn reasoning_response() -> Value { + json!({ + "id": "r1", "object": "chat.completion", "created": 1764656316, "model": "deepseek-reasoner", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "There are 3 r's in strawberry.", + "reasoning_content": "Let me count the letters." + }, + "finish_reason": "stop" + }], + "usage": { "prompt_tokens": 20, "completion_tokens": 30, "total_tokens": 50, + "completion_tokens_details": { "reasoning_tokens": 10 } } + }) +} -#[tokio::test] -async fn should_forward_the_model_id() { - for model_id in ["deepseek-v4-flash", "deepseek-v4-pro"] { - assert_eq!(generate_body(model_id, &options()).await["model"], model_id); - } +fn logprobs_response() -> Value { + let mut body = text_response(); + body["choices"][0]["message"] = json!({ + "role": "assistant", "content": "OK", "reasoning_content": "Reasoning" + }); + body["choices"][0]["logprobs"] = json!({ + "content": [logprob("OK", -0.00002467602, json!([79, 75]))], + "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] + }); + body } -#[tokio::test] -async fn should_natively_support_http_image_urls() { - let server = MockServer::start().await; - let urls = chat(&server, "deepseek-chat").supported_urls().await; - let patterns = &urls.0["image/*"]; - assert_eq!(patterns.len(), 1); - assert!(patterns[0].is_match("https://example.com/a.png")); - assert!(patterns[0].is_match("http://example.com/a.png")); - assert!(!patterns[0].is_match("data:image/png;base64,AAAA")); +fn reasoning_options(effort: ReasoningEffort) -> CallOptions { + CallOptions { + reasoning: Some(effort), + ..options() + } } -// =========================================================================== -// describe('doGenerate') -// =========================================================================== +fn tool_call_response() -> Value { + json!({ + "id": "t1", "object": "chat.completion", "created": 1764656316, "model": "deepseek-reasoner", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", "content": "", + "reasoning_content": "I should look up the weather.", + "tool_calls": [{ + "id": "call_00_abc", "type": "function", + "function": { "name": "weather", "arguments": "{\"location\":\"San Francisco\"}" } + }] + }, + "finish_reason": "tool_calls" + }], + "usage": { "prompt_tokens": 50, "completion_tokens": 20, "total_tokens": 70 } + }) +} -#[tokio::test] -async fn should_reject_a_response_without_choices() { - let server = json_server(json!({ - "id": "chatcmpl-empty", "object": "chat.completion", "created": 0, - "model": "deepseek-chat", "choices": [], - "usage": { "prompt_tokens": 1, "completion_tokens": 0, "total_tokens": 1 } - })) - .await; - let error = chat(&server, "deepseek-chat") - .do_generate(&options()) - .await - .unwrap_err(); - assert!( - matches!(&error, AiMuxError::InvalidResponseData(m) if m == "Response did not contain any choices."), - "{error:?}" +fn tool_options() -> CallOptions { + let mut options = with_provider_options( + options(), + json!({ "deepseek": { "thinking": { "type": "enabled" } } }), ); + options.tools = Some(vec![Tool::Function(weather_tool())]); + options } -// ---- describe('text') ------------------------------------------------------ +fn weather_wire_tool() -> Value { + json!({ + "type": "function", + "function": { + "name": "weather", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { "location": { "type": "string" } }, + "required": ["location"], + "type": "object" + } + } + }) +} -#[tokio::test] -async fn text_should_send_correct_request_body() { - let mut options = options_for(vec![ - LanguageModelMessage::System { - content: ("You are a helpful assistant.").into(), - provider_options: None, - }, - LanguageModelMessage::user_text("Hello"), - ]); - options.temperature = Some(0.5); - options.top_p = Some(0.3); - assert_eq!( - generate_body("deepseek-chat", &options).await, - json!({ - "messages": [ - { "content": "You are a helpful assistant.", "role": "system" }, - { "content": "Hello", "role": "user" } - ], - "model": "deepseek-chat", - "temperature": 0.5, - "top_p": 0.3 - }) - ); +fn json_options(schema: Option) -> CallOptions { + let mut options = tool_options(); + options.response_format = Some(ResponseFormat::Json { + schema, + name: None, + description: None, + }); + options } -#[tokio::test] -async fn text_should_omit_deprecated_and_ineffective_sampling_options_in_default_v4_thinking_mode() -{ - let mut options = options(); - options.temperature = Some(0.2); - options.top_p = Some(0.4); - options.frequency_penalty = Some(0.5); - options.presence_penalty = Some(0.6); - let result = generate_result("deepseek-v4-flash", &options).await; - assert_eq!( - result.request.and_then(|request| request.body).unwrap(), - json!({ "model": "deepseek-v4-flash", "messages": [{ "role": "user", "content": "Hello" }] }) - ); - assert_eq!( - warning_values(&result.warnings), - warning_values(&[ - deprecated("frequencyPenalty"), - deprecated("presencePenalty"), - thinking_unsupported("temperature"), - thinking_unsupported("topP"), - ]) - ); +fn prefix_prompt(prefix_options: Value) -> LanguageModelPrompt { + vec![ + LanguageModelMessage::user_text("Complete this sentence."), + with_options( + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("The answer is").into(), + provider_options: None, + })], + provider_options: None, + }, + json!({ "deepseek": prefix_options }), + ), + ] } -#[tokio::test] -async fn text_should_preserve_supported_sampling_options_when_v4_thinking_is_disabled() { - let mut options = with_provider_options( - options(), - json!({ "deepseek": { "thinking": { "type": "disabled" } } }), - ); - options.temperature = Some(0.2); - options.top_p = Some(0.4); - options.frequency_penalty = Some(0.5); - options.presence_penalty = Some(0.6); - let result = generate_result("deepseek-v4-flash", &options).await; - assert_eq!( - result.request.and_then(|request| request.body).unwrap(), - json!({ - "model": "deepseek-v4-flash", - "messages": [{ "role": "user", "content": "Hello" }], - "temperature": 0.2, - "top_p": 0.4, - "thinking": { "type": "disabled" } +// Upstream emits provider error envelopes as error parts, including the first event. +async fn stream_error(data: &Value) -> aimux_core::error::ApiCallError { + let server = sse_server(vec![data_event(data)]).await; + let result = chat(&server, "deepseek-chat") + .do_stream(&options()) + .await + .unwrap(); + collect(result) + .await + .into_iter() + .find_map(|part| match part { + StreamPart::Error { + error: AiMuxError::ApiCall(error), + } => Some(*error), + _ => None, }) - ); - assert_eq!( - warning_values(&result.warnings), - warning_values(&[ - deprecated("frequencyPenalty"), - deprecated("presencePenalty") - ]) - ); + .expect("expected a provider error part") } -#[tokio::test] -async fn text_should_send_max_tokens_and_stop_sequences() { - let mut options = options(); - options.max_output_tokens = Some(64); - options.stop_sequences = Some(vec!["END".to_string()]); - let body = generate_body("deepseek-chat", &options).await; - assert_eq!(body["max_tokens"], 64); - assert_eq!(body["stop"], json!(["END"])); - assert!(body.get("max_completion_tokens").is_none()); +async fn stream_parts( + model_id: &str, + options: &CallOptions, + chunks: Vec, +) -> (Value, Vec) { + let server = sse_server(chunks).await; + let result = chat(&server, model_id).do_stream(options).await.unwrap(); + let body = result.request.clone().and_then(|r| r.body).unwrap(); + (body, collect(result).await) } -#[tokio::test] -async fn text_should_send_message_names() { - let name = |value: &str| json!({ "deepseek": { "name": value } }); - let messages = wire_messages( - "deepseek-chat", - vec![ - with_options( - LanguageModelMessage::System { - content: ("You are a helpful assistant.").into(), - provider_options: None, - }, - name("guide"), - ), - with_options(LanguageModelMessage::user_text("Hello"), name("alice")), - with_options( - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("Hello, Alice.").into(), - provider_options: None, - })], - provider_options: None, - }, - name("assistant"), - ), - with_options( - LanguageModelMessage::user_text("How are you?"), - name("alice"), - ), - ], - ) - .await; - assert_eq!( - messages, - [ - json!({ "content": "You are a helpful assistant.", "name": "guide", "role": "system" }), - json!({ "content": "Hello", "name": "alice", "role": "user" }), - json!({ "content": "Hello, Alice.", "name": "assistant", "role": "assistant" }), - json!({ "content": "How are you?", "name": "alice", "role": "user" }), - ] - ); +fn png_data() -> UserPart { + UserPart::File(FilePart { + data: FileData::Data { + data: FileBytes::Base64("AAECAw==".into()), + }, + media_type: "image/png".into(), + filename: None, + provider_options: None, + }) } -#[tokio::test] -async fn text_should_pass_provider_options_user_id_as_user_id() { - let options = with_provider_options( - options(), - json!({ "deepseek": { "userId": "tenant_123-user" } }), - ); - assert_eq!( - generate_body("deepseek-chat", &options).await["user_id"], - "tenant_123-user" - ); +fn file_with_options(mut part: UserPart, options: Value) -> UserPart { + if let UserPart::File(file) = &mut part { + file.provider_options = Some(serde_json::from_value(options).unwrap()); + } + part } -#[tokio::test] -async fn text_should_omit_user_id_when_user_id_is_not_provided() { - assert!( - generate_body("deepseek-chat", &options()) - .await - .get("user_id") - .is_none() - ); +fn tool_turn(reasoning: bool) -> Vec { + let mut assistant = vec![]; + if reasoning { + assistant.push(AssistantPart::Reasoning(ReasoningPart { + text: ("I think the tool will return the correct value.").into(), + provider_options: None, + })); + } + assistant.push(AssistantPart::ToolCall(ToolCallPart { + tool_call_id: ("quux").into(), + tool_name: ("thwomp").into(), + input: json!({ "foo": "bar123" }), + provider_executed: None, + provider_options: None, + })); + vec![ + LanguageModelMessage::Assistant { + content: assistant, + provider_options: None, + }, + LanguageModelMessage::Tool { + content: vec![ToolPart::ToolResult(ToolResultPart { + tool_call_id: ("quux").into(), + output: ToolResultOutput::Json { + value: json!({ "oof": "321rab" }), + provider_options: None, + }, + tool_name: "thwomp".into(), + provider_options: None, + })], + provider_options: None, + }, + ] } -#[tokio::test] -async fn text_should_not_send_unknown_provider_options() { - // upstream: `deepseekLanguageModelChatOptions` is a zod object, which - // strips keys it does not declare. - let options = - with_provider_options(options(), json!({ "deepseek": { "user": "u1", "foo": 1 } })); - let body = generate_body("deepseek-chat", &options).await; - assert!(body.get("user").is_none() && body.get("foo").is_none()); +fn wire_tool_call() -> Value { + json!([{ + "id": "quux", "type": "function", + "function": { "name": "thwomp", "arguments": "{\"foo\":\"bar123\"}" } + }]) } -/// TS: "should pass through strict mode when strict is false" (line ~40). -/// -/// A tool with `strict: false` should produce `"strict": false` in the -/// serialized tool definition. -#[tokio::test] -async fn should_pass_through_strict_mode_when_strict_is_false() { - let tool = FunctionTool { - name: "testFunction".to_string(), - description: Some("A test function".to_string()), - input_schema: json!({ "type": "object", "properties": {} }), - strict: Some(false), - provider_options: None, - input_examples: None, - }; - let mut options = options(); - options.tools = Some(vec![Tool::from(tool)]); - let body = generate_body("deepseek-chat", &options).await; +mod deep_seek_chat_language_model { + use super::*; - assert_eq!( - body["tools"], - json!([{ - "type": "function", - "function": { - "description": "A test function", - "name": "testFunction", - "parameters": { "type": "object", "properties": {} }, - "strict": false + mod model_ids { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › model IDs › should forward the %s model ID + #[tokio::test] + async fn should_forward_the_model_id() { + for model_id in ["deepseek-v4-flash", "deepseek-v4-pro"] { + assert_eq!(generate_body(model_id, &options()).await["model"], model_id); } - }]) - ); -} + } + } -/// TS: "should not include strict mode when strict is undefined" (line ~75). -/// -/// A tool without `strict` set should NOT have a `"strict"` field in the -/// serialized tool definition. -#[tokio::test] -async fn should_not_include_strict_mode_when_strict_is_undefined() { - let tool = FunctionTool { - name: "testFunction".to_string(), - description: Some("A test function".to_string()), - input_schema: json!({ "type": "object", "properties": {} }), - strict: None, - provider_options: None, - input_examples: None, - }; - let mut options = options(); - options.tools = Some(vec![Tool::from(tool)]); - let body = generate_body("deepseek-chat", &options).await; + mod supported_urls { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › supportedUrls › should natively support HTTP image URLs + #[tokio::test] + async fn should_natively_support_http_image_urls() { + let server = MockServer::start().await; + let urls = chat(&server, "deepseek-chat").supported_urls().await; + let patterns = &urls.0["image/*"]; + assert_eq!(patterns.len(), 1); + assert!(patterns[0].is_match("https://example.com/a.png")); + assert!(patterns[0].is_match("http://example.com/a.png")); + assert!(!patterns[0].is_match("data:image/png;base64,AAAA")); + } + } - assert_eq!( - body["tools"], - json!([{ - "type": "function", - "function": { - "description": "A test function", - "name": "testFunction", - "parameters": { "type": "object", "properties": {} } + mod do_generate { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › should reject a response without choices + #[tokio::test] + async fn should_reject_a_response_without_choices() { + let server = json_server(json!({ + "id": "chatcmpl-empty", "object": "chat.completion", "created": 0, + "model": "deepseek-chat", "choices": [], + "usage": { "prompt_tokens": 1, "completion_tokens": 0, "total_tokens": 1 } + })) + .await; + let error = chat(&server, "deepseek-chat") + .do_generate(&options()) + .await + .unwrap_err(); + assert!( + matches!(&error, AiMuxError::InvalidResponseData(m) if m == "Response did not contain any choices."), + "{error:?}" + ); + } + + mod text { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should send correct request body + #[tokio::test] + async fn text_should_send_correct_request_body() { + let mut options = options_for(vec![ + LanguageModelMessage::System { + content: ("You are a helpful assistant.").into(), + provider_options: None, + }, + LanguageModelMessage::user_text("Hello"), + ]); + options.temperature = Some(0.5); + options.top_p = Some(0.3); + assert_eq!( + generate_body("deepseek-chat", &options).await, + json!({ + "messages": [ + { "content": "You are a helpful assistant.", "role": "system" }, + { "content": "Hello", "role": "user" } + ], + "model": "deepseek-chat", + "temperature": 0.5, + "top_p": 0.3 + }) + ); } - }]) - ); - // Verify strict is absent. - assert!( - body["tools"][0]["function"].get("strict").is_none(), - "strict should be absent" - ); -} -#[tokio::test] -async fn text_should_reject_strict_tools_on_the_standard_endpoint_before_fetching() { - let mut options = options(); - options.tools = Some(vec![strict_tool("getWeather", Some(true))]); - let message = generate_error("deepseek-chat", &options).await; - assert!( - message.contains("DeepSeek strict tool calls require a beta base URL ending in `/beta`."), - "{message}" - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should omit deprecated and ineffective sampling options in default V4 thinking mode + #[tokio::test] + async fn text_should_omit_deprecated_and_ineffective_sampling_options_in_default_v4_thinking_mode() + { + let mut options = options(); + options.temperature = Some(0.2); + options.top_p = Some(0.4); + options.frequency_penalty = Some(0.5); + options.presence_penalty = Some(0.6); + let result = generate_result("deepseek-v4-flash", &options).await; + assert_eq!( + result.request.and_then(|request| request.body).unwrap(), + json!({ "model": "deepseek-v4-flash", "messages": [{ "role": "user", "content": "Hello" }] }) + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[ + deprecated("frequencyPenalty"), + deprecated("presencePenalty"), + thinking_unsupported("temperature"), + thinking_unsupported("topP"), + ]) + ); + } -#[tokio::test] -async fn text_should_send_all_strict_tools_on_the_beta_endpoint() { - let server = json_server(text_response()).await; - let mut options = options(); - options.tools = Some(vec![strict_tool("getWeather", Some(true))]); - let body = beta_chat(&server, "deepseek-chat") - .do_generate(&options) - .await - .unwrap() - .request - .and_then(|r| r.body) - .unwrap(); - assert_eq!(body["tools"][0]["type"], "function"); - assert_eq!(body["tools"][0]["function"]["name"], "getWeather"); - assert_eq!(body["tools"][0]["function"]["strict"], true); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should preserve supported sampling options when V4 thinking is disabled + #[tokio::test] + async fn text_should_preserve_supported_sampling_options_when_v4_thinking_is_disabled() + { + let mut options = with_provider_options( + options(), + json!({ "deepseek": { "thinking": { "type": "disabled" } } }), + ); + options.temperature = Some(0.2); + options.top_p = Some(0.4); + options.frequency_penalty = Some(0.5); + options.presence_penalty = Some(0.6); + let result = generate_result("deepseek-v4-flash", &options).await; + assert_eq!( + result.request.and_then(|request| request.body).unwrap(), + json!({ + "model": "deepseek-v4-flash", + "messages": [{ "role": "user", "content": "Hello" }], + "temperature": 0.2, + "top_p": 0.4, + "thinking": { "type": "disabled" } + }) + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[ + deprecated("frequencyPenalty"), + deprecated("presencePenalty") + ]) + ); + } -#[tokio::test] -async fn text_should_reject_mixed_strict_tools_in_streaming_requests() { - let server = sse_server(text_chunks()).await; - let mut options = options(); - options.tools = Some(vec![ - strict_tool("strictTool", Some(true)), - strict_tool("nonStrictTool", None), - ]); - let Err(error) = beta_chat(&server, "deepseek-chat") - .do_stream(&options) - .await - else { - panic!("the call must be rejected"); - }; - assert!( + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should send message names + #[tokio::test] + async fn text_should_send_message_names() { + let name = |value: &str| json!({ "deepseek": { "name": value } }); + let messages = wire_messages( + "deepseek-chat", + vec![ + with_options( + LanguageModelMessage::System { + content: ("You are a helpful assistant.").into(), + provider_options: None, + }, + name("guide"), + ), + with_options(LanguageModelMessage::user_text("Hello"), name("alice")), + with_options( + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("Hello, Alice.").into(), + provider_options: None, + })], + provider_options: None, + }, + name("assistant"), + ), + with_options( + LanguageModelMessage::user_text("How are you?"), + name("alice"), + ), + ], + ) + .await; + assert_eq!( + messages, + [ + json!({ "content": "You are a helpful assistant.", "name": "guide", "role": "system" }), + json!({ "content": "Hello", "name": "alice", "role": "user" }), + json!({ "content": "Hello, Alice.", "name": "assistant", "role": "assistant" }), + json!({ "content": "How are you?", "name": "alice", "role": "user" }), + ] + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should pass providerOptions userId as user_id + #[tokio::test] + async fn text_should_pass_provider_options_user_id_as_user_id() { + let options = with_provider_options( + options(), + json!({ "deepseek": { "userId": "tenant_123-user" } }), + ); + assert_eq!( + generate_body("deepseek-chat", &options).await["user_id"], + "tenant_123-user" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should omit user_id when userId is not provided + #[tokio::test] + async fn text_should_omit_user_id_when_user_id_is_not_provided() { + assert!( + generate_body("deepseek-chat", &options()) + .await + .get("user_id") + .is_none() + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should reject strict tools on the standard endpoint before fetching + #[tokio::test] + async fn text_should_reject_strict_tools_on_the_standard_endpoint_before_fetching() { + let mut options = options(); + options.tools = Some(vec![strict_tool("getWeather", Some(true))]); + let message = generate_error("deepseek-chat", &options).await; + assert!( + message.contains( + "DeepSeek strict tool calls require a beta base URL ending in `/beta`." + ), + "{message}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should send all-strict tools on the beta endpoint + #[tokio::test] + async fn text_should_send_all_strict_tools_on_the_beta_endpoint() { + let server = json_server(text_response()).await; + let mut options = options(); + options.tools = Some(vec![strict_tool("getWeather", Some(true))]); + let body = beta_chat(&server, "deepseek-chat") + .do_generate(&options) + .await + .unwrap() + .request + .and_then(|r| r.body) + .unwrap(); + assert_eq!(body["tools"][0]["type"], "function"); + assert_eq!(body["tools"][0]["function"]["name"], "getWeather"); + assert_eq!(body["tools"][0]["function"]["strict"], true); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should reject mixed strict tools in streaming requests + #[tokio::test] + async fn text_should_reject_mixed_strict_tools_in_streaming_requests() { + let server = sse_server(text_chunks()).await; + let mut options = options(); + options.tools = Some(vec![ + strict_tool("strictTool", Some(true)), + strict_tool("nonStrictTool", None), + ]); + let Err(error) = beta_chat(&server, "deepseek-chat") + .do_stream(&options) + .await + else { + panic!("the call must be rejected"); + }; + assert!( error.to_string().contains( "DeepSeek strict mode requires every function tool in the request to set `strict: true`." ), "{error}" ); - assert!(request_paths(&server).await.is_empty()); -} + assert!(request_paths(&server).await.is_empty()); + } -#[tokio::test] -async fn text_should_reject_invalid_user_id_before_making_an_api_request() { - for (user_id, expected) in [ - ("".to_string(), "userId must match /^[a-zA-Z0-9_-]+$/"), - ( - "contains space".to_string(), - "userId must match /^[a-zA-Z0-9_-]+$/", - ), - ( - "a".repeat(513), - "userId must be at most 512 characters long", - ), - ] { - let options = - with_provider_options(options(), json!({ "deepseek": { "userId": user_id } })); - let message = generate_error("deepseek-chat", &options).await; - assert!(message.contains("invalid provider options"), "{message}"); - assert!(message.contains(expected), "{message}"); - } -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should reject invalid userId %j before making an API request + #[tokio::test] + async fn text_should_reject_invalid_user_id_before_making_an_api_request() { + for (user_id, expected) in [ + ("".to_string(), "userId must match /^[a-zA-Z0-9_-]+$/"), + ( + "contains space".to_string(), + "userId must match /^[a-zA-Z0-9_-]+$/", + ), + ( + "a".repeat(513), + "userId must be at most 512 characters long", + ), + ] { + let options = with_provider_options( + options(), + json!({ "deepseek": { "userId": user_id } }), + ); + let message = generate_error("deepseek-chat", &options).await; + assert!(message.contains("invalid provider options"), "{message}"); + assert!(message.contains(expected), "{message}"); + } + } -#[tokio::test] -async fn text_should_extract_text_content() { - let result = generate_result("deepseek-chat", &options()).await; - assert_eq!(result.content.len(), 1); - assert!( - matches!(&result.content[0], GenerateContent::Text { text, .. } if text == "Hello, World!") - ); - assert_eq!(result.finish_reason.unified, FinishReasonUnified::Stop); - assert_eq!(result.finish_reason.raw.as_deref(), Some("stop")); - assert_eq!(result.usage.input_tokens.total, Some(13)); - assert_eq!(result.usage.output_tokens.total, Some(300)); - assert_eq!( - result.response.as_ref().and_then(|r| r.id.as_deref()), - Some("00f10ecd-60b3-4707-b5db-e4bcadf7aea1") - ); - assert_eq!( - result.response.as_ref().and_then(|r| r.model_id.as_deref()), - Some("deepseek-chat") - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should include the system fingerprint in provider metadata + #[tokio::test] + async fn text_should_include_the_system_fingerprint_in_provider_metadata() { + let result = generate_result("deepseek-chat", &options()).await; + assert_eq!( + result.provider_metadata.unwrap()["deepseek"]["systemFingerprint"], + "fp_eaab8d114b_prod0820_fp8_kvcache" + ); + } -#[tokio::test] -async fn text_should_report_usage_with_prompt_cache_tokens() { - // upstream convert-to-deepseek-usage.ts: cache hits are the cache-read tokens. - let mut body = text_response(); - body["usage"] = json!({ - "prompt_tokens": 100, "completion_tokens": 20, "total_tokens": 120, - "prompt_cache_hit_tokens": 70, "prompt_cache_miss_tokens": 30, - "completion_tokens_details": { "reasoning_tokens": 5 } - }); - let server = json_server(body).await; - let usage = chat(&server, "deepseek-chat") - .do_generate(&options()) - .await - .unwrap() - .usage; - assert_eq!(usage.input_tokens.total, Some(100)); - assert_eq!(usage.input_tokens.cache_read, Some(70)); - assert_eq!(usage.input_tokens.no_cache, Some(30)); - assert_eq!(usage.output_tokens.total, Some(20)); - assert_eq!(usage.output_tokens.reasoning, Some(5)); - assert_eq!(usage.output_tokens.text, Some(15)); - assert_eq!(usage.raw.unwrap()["prompt_cache_hit_tokens"], 70); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should tolerate a %s system fingerprint + #[tokio::test] + async fn text_should_tolerate_a_null_or_missing_system_fingerprint() { + for fingerprint in [Some(Value::Null), None] { + let mut body = text_response(); + match fingerprint { + Some(value) => body["system_fingerprint"] = value, + None => { + body.as_object_mut().unwrap().remove("system_fingerprint"); + } + } + let server = json_server(body).await; + let result = chat(&server, "deepseek-chat") + .do_generate(&options()) + .await + .unwrap(); + assert!( + result.provider_metadata.unwrap()["deepseek"] + .get("systemFingerprint") + .is_none() + ); + } + } + } -#[tokio::test] -async fn text_should_report_the_provider_metadata() { - // upstream doGenerate: promptCacheHitTokens, promptCacheMissTokens, - // responseObject, choiceIndex, messageRole, systemFingerprint. - let result = generate_result("deepseek-chat", &options()).await; - assert_eq!( - serde_json::to_value(result.provider_metadata.unwrap()).unwrap(), - json!({ "deepseek": { - "promptCacheHitTokens": 0, - "promptCacheMissTokens": 13, - "responseObject": "chat.completion", - "choiceIndex": 0, - "messageRole": "assistant", - "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" - } }) - ); -} + mod reasoning { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › reasoning › should send correct request body + #[tokio::test] + async fn reasoning_should_send_correct_request_body() { + let options = with_provider_options( + options_for(vec![LanguageModelMessage::user_text( + "How many \"r\"s are in the word \"strawberry\"?", + )]), + json!({ "deepseek": { "thinking": { "type": "enabled" } } }), + ); + assert_eq!( + generate_body("deepseek-reasoner", &options).await, + json!({ + "messages": [{ "content": "How many \"r\"s are in the word \"strawberry\"?", "role": "user" }], + "model": "deepseek-reasoner", + "thinking": { "type": "enabled" } + }) + ); + } + } -#[tokio::test] -async fn text_should_include_the_system_fingerprint_in_provider_metadata() { - let result = generate_result("deepseek-chat", &options()).await; - assert_eq!( - result.provider_metadata.unwrap()["deepseek"]["systemFingerprint"], - "fp_eaab8d114b_prod0820_fp8_kvcache" - ); -} + mod logprobs { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › logprobs › should send logprobs provider options + #[tokio::test] + async fn logprobs_should_send_logprobs_provider_options() { + let options = with_provider_options( + options(), + json!({ "deepseek": { "logprobs": true, "topLogprobs": 1 } }), + ); + assert_eq!( + generate_body("deepseek-v4-flash", &options).await, + json!({ + "logprobs": true, + "messages": [{ "content": "Hello", "role": "user" }], + "model": "deepseek-v4-flash", + "top_logprobs": 1 + }) + ); + } -#[tokio::test] -async fn text_should_tolerate_a_null_or_missing_system_fingerprint() { - for fingerprint in [Some(Value::Null), None] { - let mut body = text_response(); - match fingerprint { - Some(value) => body["system_fingerprint"] = value, - None => { - body.as_object_mut().unwrap().remove("system_fingerprint"); + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › logprobs › should enable logprobs when topLogprobs is set + #[tokio::test] + async fn logprobs_should_enable_logprobs_when_top_logprobs_is_set() { + let options = + with_provider_options(options(), json!({ "deepseek": { "topLogprobs": 1 } })); + let body = generate_body("deepseek-v4-flash", &options).await; + assert_eq!(body["logprobs"], true); + assert_eq!(body["top_logprobs"], 1); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › logprobs › should extract content and reasoning logprobs + #[tokio::test] + async fn logprobs_should_extract_content_and_reasoning_logprobs() { + let server = json_server(logprobs_response()).await; + let options = + with_provider_options(options(), json!({ "deepseek": { "logprobs": true } })); + let result = chat(&server, "deepseek-v4-flash") + .do_generate(&options) + .await + .unwrap(); + assert_eq!( + result.provider_metadata.unwrap()["deepseek"]["logprobs"], + json!({ + "content": [logprob("OK", -0.00002467602, json!([79, 75]))], + "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] + }) + ); } } - let server = json_server(body).await; - let result = chat(&server, "deepseek-chat") - .do_generate(&options()) - .await - .unwrap(); - assert!( - result.provider_metadata.unwrap()["deepseek"] - .get("systemFingerprint") - .is_none() - ); - } -} -// ---- describe('reasoning') --------------------------------------------------- + mod top_level_reasoning { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning to thinking enabled + #[tokio::test] + async fn top_level_reasoning_should_map_to_thinking_enabled() { + let body = generate_body( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::High), + ) + .await; + assert_eq!(body["thinking"], json!({ "type": "enabled" })); + assert_eq!(body["reasoning_effort"], "high"); + } -fn reasoning_response() -> Value { - json!({ - "id": "r1", "object": "chat.completion", "created": 1764656316, "model": "deepseek-reasoner", - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "content": "There are 3 r's in strawberry.", - "reasoning_content": "Let me count the letters." - }, - "finish_reason": "stop" - }], - "usage": { "prompt_tokens": 20, "completion_tokens": 30, "total_tokens": 50, - "completion_tokens_details": { "reasoning_tokens": 10 } } - }) -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning none to thinking disabled + #[tokio::test] + async fn top_level_reasoning_none_should_map_to_thinking_disabled() { + let body = generate_body( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::None), + ) + .await; + assert_eq!(body["thinking"], json!({ "type": "disabled" })); + assert!(body.get("reasoning_effort").is_none()); + } -#[tokio::test] -async fn reasoning_should_send_correct_request_body() { - let options = with_provider_options( - options_for(vec![LanguageModelMessage::user_text( - "How many \"r\"s are in the word \"strawberry\"?", - )]), - json!({ "deepseek": { "thinking": { "type": "enabled" } } }), - ); - assert_eq!( - generate_body("deepseek-reasoner", &options).await, - json!({ - "messages": [{ "content": "How many \"r\"s are in the word \"strawberry\"?", "role": "user" }], - "model": "deepseek-reasoner", - "thinking": { "type": "enabled" } - }) - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning xhigh to reasoning_effort max + #[tokio::test] + async fn top_level_reasoning_xhigh_should_map_to_reasoning_effort_max() { + let result = generate_result( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::Xhigh), + ) + .await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], + "max" + ); + assert!(warning_values(&result.warnings).contains(&serde_json::to_value(compatibility( + "reasoning", + "reasoning \"xhigh\" is not directly supported by this model. mapped to effort \"max\"." + )) + .unwrap())); + } -#[tokio::test] -async fn reasoning_should_extract_reasoning_before_text() { - let server = json_server(reasoning_response()).await; - let result = chat(&server, "deepseek-reasoner") - .do_generate(&options()) - .await - .unwrap(); - assert_eq!(result.content.len(), 2); - assert!( - matches!(&result.content[0], GenerateContent::Reasoning(ReasoningOutput { text, .. }) if text == "Let me count the letters.") - ); - assert!( - matches!(&result.content[1], GenerateContent::Text { text, .. } if text == "There are 3 r's in strawberry.") + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning low to reasoning_effort low without a compatibility warning + #[tokio::test] + async fn top_level_reasoning_low_should_map_to_reasoning_effort_low_without_a_compatibility_warning() + { + let result = generate_result( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::Low), + ) + .await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], + "low" + ); + assert!( + !result + .warnings + .iter() + .any(|w| matches!(w, Warning::Compatibility { feature, .. } if feature == "reasoning")) ); - assert_eq!(result.usage.output_tokens.reasoning, Some(10)); - assert_eq!(result.usage.output_tokens.text, Some(20)); -} + } -#[tokio::test] -async fn reasoning_should_not_read_the_reasoning_field() { - // upstream deepseekChatResponseSchema only declares `reasoning_content`. - let mut body = reasoning_response(); - let message = body["choices"][0]["message"].as_object_mut().unwrap(); - let reasoning = message.remove("reasoning_content").unwrap(); - message.insert("reasoning".to_string(), reasoning); - let server = json_server(body).await; - let result = chat(&server, "deepseek-reasoner") - .do_generate(&options()) - .await - .unwrap(); - assert_eq!(result.content.len(), 1); - assert!(matches!(&result.content[0], GenerateContent::Text { .. })); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning medium to reasoning_effort high with a compatibility warning + #[tokio::test] + async fn top_level_reasoning_medium_should_map_to_reasoning_effort_high_with_a_compatibility_warning() + { + let result = generate_result( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::Medium), + ) + .await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], + "high" + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[compatibility( + "reasoning", + "reasoning \"medium\" is not directly supported by this model. mapped to effort \"high\"." + )]) + ); + } -// ---- describe('logprobs') ---------------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map top-level reasoning minimal to reasoning_effort low with compatibility warning + #[tokio::test] + async fn top_level_reasoning_minimal_should_map_to_reasoning_effort_low_with_compatibility_warning() + { + let result = generate_result( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::Minimal), + ) + .await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], + "low" + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[compatibility( + "reasoning", + "reasoning \"minimal\" is not directly supported by this model. mapped to effort \"low\"." + )]) + ); + } -fn logprobs_response() -> Value { - let mut body = text_response(); - body["choices"][0]["message"] = json!({ - "role": "assistant", "content": "OK", "reasoning_content": "Reasoning" - }); - body["choices"][0]["logprobs"] = json!({ - "content": [logprob("OK", -0.00002467602, json!([79, 75]))], - "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] - }); - body -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map providerOptions reasoningEffort $input to $output + #[tokio::test] + async fn top_level_reasoning_should_map_provider_options_reasoning_effort() { + for (input, output, warns) in [ + ("low", "low", false), + ("medium", "high", true), + ("xhigh", "max", true), + ] { + let options = with_provider_options( + options(), + json!({ "deepseek": { "reasoningEffort": input } }), + ); + let result = generate_result("deepseek-reasoner", &options).await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], + output + ); + let expected = if warns { + vec![compatibility( + "reasoningEffort", + &format!( + "reasoningEffort \"{input}\" is not a canonical DeepSeek value. mapped to \"{output}\"." + ), + )] + } else { + vec![] + }; + assert_eq!(warning_values(&result.warnings), warning_values(&expected)); + } + } -#[tokio::test] -async fn logprobs_should_send_logprobs_provider_options() { - let options = with_provider_options( - options(), - json!({ "deepseek": { "logprobs": true, "topLogprobs": 1 } }), - ); - assert_eq!( - generate_body("deepseek-v4-flash", &options).await, - json!({ - "logprobs": true, - "messages": [{ "content": "Hello", "role": "user" }], - "model": "deepseek-v4-flash", - "top_logprobs": 1 - }) - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should map legacy providerOptions thinking.type=adaptive to enabled with a compatibility warning + #[tokio::test] + async fn top_level_reasoning_should_map_legacy_thinking_type_adaptive_to_enabled_with_a_compatibility_warning() + { + let options = with_provider_options( + options(), + json!({ "deepseek": { "thinking": { "type": "adaptive" } } }), + ); + let result = generate_result("deepseek-reasoner", &options).await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["thinking"], + json!({ "type": "enabled" }) + ); + assert!(warning_values(&result.warnings).contains(&serde_json::to_value(compatibility( + "thinking.type", + "thinking.type \"adaptive\" is not a canonical DeepSeek value. mapped to \"enabled\"." + )) + .unwrap())); + } -#[tokio::test] -async fn logprobs_should_enable_logprobs_when_top_logprobs_is_set() { - let options = with_provider_options(options(), json!({ "deepseek": { "topLogprobs": 1 } })); - let body = generate_body("deepseek-v4-flash", &options).await; - assert_eq!(body["logprobs"], true); - assert_eq!(body["top_logprobs"], 1); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should pass providerOptions reasoningEffort + #[tokio::test] + async fn top_level_reasoning_should_pass_provider_options_reasoning_effort() { + let options = with_provider_options( + options(), + json!({ "deepseek": { "reasoningEffort": "max" } }), + ); + let body = generate_body("deepseek-reasoner", &options).await; + assert_eq!(body["reasoning_effort"], "max"); + // When only reasoningEffort is set without thinking, thinking stays unset. + assert!(body.get("thinking").is_none()); + } -#[tokio::test] -async fn logprobs_should_extract_content_and_reasoning_logprobs() { - let server = json_server(logprobs_response()).await; - let options = with_provider_options(options(), json!({ "deepseek": { "logprobs": true } })); - let result = chat(&server, "deepseek-v4-flash") - .do_generate(&options) - .await - .unwrap(); - assert_eq!( - result.provider_metadata.unwrap()["deepseek"]["logprobs"], - json!({ - "content": [logprob("OK", -0.00002467602, json!([79, 75]))], - "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] - }) - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should prefer providerOptions thinking over top-level reasoning + #[tokio::test] + async fn top_level_reasoning_should_prefer_provider_options_thinking_over_top_level_reasoning() + { + let options = with_provider_options( + reasoning_options(ReasoningEffort::None), + json!({ "deepseek": { "thinking": { "type": "enabled" } } }), + ); + assert_eq!( + generate_body("deepseek-reasoner", &options).await["thinking"], + json!({ "type": "enabled" }) + ); + } -// ---- describe('top-level reasoning') ------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should prefer providerOptions reasoningEffort over top-level reasoning + #[tokio::test] + async fn top_level_reasoning_should_prefer_provider_options_reasoning_effort_over_top_level_reasoning() + { + let options = with_provider_options( + reasoning_options(ReasoningEffort::High), + json!({ "deepseek": { "reasoningEffort": "max" } }), + ); + assert_eq!( + generate_body("deepseek-reasoner", &options).await["reasoning_effort"], + "max" + ); + } -fn reasoning_options(effort: ReasoningEffort) -> CallOptions { - CallOptions { - reasoning: Some(effort), - ..options() - } -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › top-level reasoning › should not set thinking when reasoning is not specified + #[tokio::test] + async fn top_level_reasoning_should_not_set_thinking_when_reasoning_is_not_specified() { + assert!( + generate_body("deepseek-reasoner", &options()) + .await + .get("thinking") + .is_none() + ); + } + } -#[tokio::test] -async fn top_level_reasoning_should_map_to_thinking_enabled() { - let body = generate_body( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::High), - ) - .await; - assert_eq!(body["thinking"], json!({ "type": "enabled" })); - assert_eq!(body["reasoning_effort"], "high"); -} + mod tool_call { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › tool call › should send correct request body + #[tokio::test] + async fn tool_call_should_send_correct_request_body() { + assert_eq!( + generate_body("deepseek-reasoner", &tool_options()).await, + json!({ + "messages": [{ "content": "Hello", "role": "user" }], + "model": "deepseek-reasoner", + "thinking": { "type": "enabled" }, + "tools": [weather_wire_tool()] + }) + ); + } -#[tokio::test] -async fn top_level_reasoning_none_should_map_to_thinking_disabled() { - let body = generate_body( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::None), - ) - .await; - assert_eq!(body["thinking"], json!({ "type": "disabled" })); - assert!(body.get("reasoning_effort").is_none()); -} - -#[tokio::test] -async fn top_level_reasoning_none_should_keep_the_temperature() { - // thinking is disabled, so temperature applies and is not warned about. - let mut options = reasoning_options(ReasoningEffort::None); - options.temperature = Some(0.4); - let result = generate_result("deepseek-reasoner", &options).await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["temperature"], - 0.4 - ); - assert!(result.warnings.is_empty()); -} - -#[tokio::test] -async fn top_level_reasoning_xhigh_should_map_to_reasoning_effort_max() { - let result = generate_result( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::Xhigh), - ) - .await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], - "max" - ); - assert!(warning_values(&result.warnings).contains(&serde_json::to_value(compatibility( - "reasoning", - "reasoning \"xhigh\" is not directly supported by this model. mapped to effort \"max\"." - )) - .unwrap())); -} - -#[tokio::test] -async fn top_level_reasoning_low_should_map_to_reasoning_effort_low_without_a_compatibility_warning() - { - let result = generate_result( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::Low), - ) - .await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], - "low" - ); - assert!( - !result - .warnings - .iter() - .any(|w| matches!(w, Warning::Compatibility { feature, .. } if feature == "reasoning")) - ); -} - -#[tokio::test] -async fn top_level_reasoning_medium_should_map_to_reasoning_effort_high_with_a_compatibility_warning() - { - let result = generate_result( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::Medium), - ) - .await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], - "high" - ); - assert_eq!( - warning_values(&result.warnings), - warning_values(&[compatibility( - "reasoning", - "reasoning \"medium\" is not directly supported by this model. mapped to effort \"high\"." - )]) - ); -} - -#[tokio::test] -async fn top_level_reasoning_minimal_should_map_to_reasoning_effort_low_with_compatibility_warning() -{ - let result = generate_result( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::Minimal), - ) - .await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], - "low" - ); - assert_eq!( - warning_values(&result.warnings), - warning_values(&[compatibility( - "reasoning", - "reasoning \"minimal\" is not directly supported by this model. mapped to effort \"low\"." - )]) - ); -} - -#[tokio::test] -async fn top_level_reasoning_should_map_provider_options_reasoning_effort() { - for (input, output, warns) in [ - ("low", "low", false), - ("medium", "high", true), - ("xhigh", "max", true), - ] { - let options = with_provider_options( - options(), - json!({ "deepseek": { "reasoningEffort": input } }), - ); - let result = generate_result("deepseek-reasoner", &options).await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["reasoning_effort"], - output - ); - let expected = if warns { - vec![compatibility( - "reasoningEffort", - &format!( - "reasoningEffort \"{input}\" is not a canonical DeepSeek value. mapped to \"{output}\"." - ), - )] - } else { - vec![] - }; - assert_eq!(warning_values(&result.warnings), warning_values(&expected)); - } -} - -#[tokio::test] -async fn top_level_reasoning_should_map_legacy_thinking_type_adaptive_to_enabled_with_a_compatibility_warning() - { - let options = with_provider_options( - options(), - json!({ "deepseek": { "thinking": { "type": "adaptive" } } }), - ); - let result = generate_result("deepseek-reasoner", &options).await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["thinking"], - json!({ "type": "enabled" }) - ); - assert!(warning_values(&result.warnings).contains(&serde_json::to_value(compatibility( - "thinking.type", - "thinking.type \"adaptive\" is not a canonical DeepSeek value. mapped to \"enabled\"." - )) - .unwrap())); -} - -#[tokio::test] -async fn top_level_reasoning_should_pass_provider_options_reasoning_effort() { - let options = with_provider_options( - options(), - json!({ "deepseek": { "reasoningEffort": "max" } }), - ); - let body = generate_body("deepseek-reasoner", &options).await; - assert_eq!(body["reasoning_effort"], "max"); - // When only reasoningEffort is set without thinking, thinking stays unset. - assert!(body.get("thinking").is_none()); -} - -#[tokio::test] -async fn top_level_reasoning_should_prefer_provider_options_thinking_over_top_level_reasoning() { - let options = with_provider_options( - reasoning_options(ReasoningEffort::None), - json!({ "deepseek": { "thinking": { "type": "enabled" } } }), - ); - assert_eq!( - generate_body("deepseek-reasoner", &options).await["thinking"], - json!({ "type": "enabled" }) - ); -} - -#[tokio::test] -async fn top_level_reasoning_should_prefer_provider_options_reasoning_effort_over_top_level_reasoning() - { - let options = with_provider_options( - reasoning_options(ReasoningEffort::High), - json!({ "deepseek": { "reasoningEffort": "max" } }), - ); - assert_eq!( - generate_body("deepseek-reasoner", &options).await["reasoning_effort"], - "max" - ); -} - -#[tokio::test] -async fn top_level_reasoning_should_not_set_thinking_when_reasoning_is_not_specified() { - assert!( - generate_body("deepseek-reasoner", &options()) - .await - .get("thinking") - .is_none() - ); -} - -#[tokio::test] -async fn top_level_reasoning_provider_default_sends_neither_thinking_nor_effort() { - let body = generate_body( - "deepseek-reasoner", - &reasoning_options(ReasoningEffort::ProviderDefault), - ) - .await; - assert!(body.get("thinking").is_none() && body.get("reasoning_effort").is_none()); -} - -// ---- describe('tool call') --------------------------------------------------- - -fn tool_call_response() -> Value { - json!({ - "id": "t1", "object": "chat.completion", "created": 1764656316, "model": "deepseek-reasoner", - "choices": [{ - "index": 0, - "message": { - "role": "assistant", "content": "", - "reasoning_content": "I should look up the weather.", - "tool_calls": [{ - "id": "call_00_abc", "type": "function", - "function": { "name": "weather", "arguments": "{\"location\":\"San Francisco\"}" } - }] - }, - "finish_reason": "tool_calls" - }], - "usage": { "prompt_tokens": 50, "completion_tokens": 20, "total_tokens": 70 } - }) -} - -fn tool_options() -> CallOptions { - let mut options = with_provider_options( - options(), - json!({ "deepseek": { "thinking": { "type": "enabled" } } }), - ); - options.tools = Some(vec![Tool::Function(weather_tool())]); - options -} - -fn weather_wire_tool() -> Value { - json!({ - "type": "function", - "function": { - "name": "weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { "location": { "type": "string" } }, - "required": ["location"], - "type": "object" - } - } - }) -} - -#[tokio::test] -async fn tool_call_should_send_correct_request_body() { - assert_eq!( - generate_body("deepseek-reasoner", &tool_options()).await, - json!({ - "messages": [{ "content": "Hello", "role": "user" }], - "model": "deepseek-reasoner", - "thinking": { "type": "enabled" }, - "tools": [weather_wire_tool()] - }) - ); -} - -#[tokio::test] -async fn tool_call_should_send_the_tool_choice() { - use aimux_core::options::LanguageModelV4ToolChoice; - let mut options = tool_options(); - options.tool_choice = Some(LanguageModelV4ToolChoice::Tool { - tool_name: "weather".to_string(), - }); - assert_eq!( - generate_body("deepseek-reasoner", &options).await["tool_choice"], - json!({ "type": "function", "function": { "name": "weather" } }) - ); - options.tool_choice = Some(LanguageModelV4ToolChoice::Required); - assert_eq!( - generate_body("deepseek-reasoner", &options).await["tool_choice"], - "required" - ); -} - -#[tokio::test] -async fn tool_call_should_warn_about_provider_defined_tools() { - use aimux_core::tool::ProviderTool; - let mut options = options(); - options.tools = Some(vec![Tool::Provider(ProviderTool { - id: "test.search".parse().unwrap(), - name: "search".to_string(), - args: serde_json::Map::new(), - supports_deferred_results: None, - })]); - let result = generate_result("deepseek-chat", &options).await; - assert_eq!( - warning_values(&result.warnings), - warning_values(&[Warning::Unsupported { - feature: "provider-defined tool test.search".to_string(), - details: None, - }]) - ); - assert!( - result.request.and_then(|r| r.body).unwrap()["tools"] - .as_array() - .unwrap() - .is_empty() - ); -} - -#[tokio::test] -async fn tool_call_should_extract_tool_call_content() { - let server = json_server(tool_call_response()).await; - let result = chat(&server, "deepseek-reasoner") - .do_generate(&tool_options()) - .await - .unwrap(); - assert!( - matches!(&result.content[0], GenerateContent::Reasoning(ReasoningOutput { text, .. }) if text == "I should look up the weather.") - ); - assert!(matches!( - &result.content[1], - GenerateContent::ToolCall(RawToolCall { tool_call_id, tool_name, input, .. }) - if tool_call_id == "call_00_abc" && tool_name == "weather" - && input == "{\"location\":\"San Francisco\"}" - )); - assert_eq!(result.content.len(), 2); - assert_eq!(result.finish_reason.unified, FinishReasonUnified::ToolCalls); - assert_eq!( - result.provider_metadata.unwrap()["deepseek"]["toolCallTypes"], - json!(["function"]) - ); -} - -#[tokio::test] -async fn tool_call_should_generate_an_id_when_the_server_sends_none() { - let mut body = tool_call_response(); - body["choices"][0]["message"]["tool_calls"][0]["id"] = Value::Null; - let server = json_server(body).await; - let result = chat(&server, "deepseek-reasoner") - .do_generate(&tool_options()) - .await - .unwrap(); - assert!(matches!( - &result.content[1], - GenerateContent::ToolCall(RawToolCall { tool_call_id, .. }) if !tool_call_id.is_empty() - )); -} - -// ---- describe('json response format') ------------------------------------------- - -fn json_options(schema: Option) -> CallOptions { - let mut options = tool_options(); - options.response_format = Some(ResponseFormat::Json { - schema, - name: None, - description: None, - }); - options -} - -#[tokio::test] -async fn json_response_format_should_send_correct_request_body_without_schema() { - assert_eq!( - generate_body("deepseek-reasoner", &json_options(None)).await, - json!({ - "messages": [ - { "content": "Return JSON.", "role": "system" }, - { "content": "Hello", "role": "user" } - ], - "model": "deepseek-reasoner", - "response_format": { "type": "json_object" }, - "thinking": { "type": "enabled" }, - "tools": [weather_wire_tool()] - }) - ); -} - -#[tokio::test] -async fn json_response_format_should_send_correct_request_body_with_schema() { - let schema = json!({ - "type": "object", - "properties": { "elements": { "type": "array", "items": { "type": "object", - "properties": { "location": { "type": "string" }, "temperature": { "type": "number" }, - "condition": { "type": "string" } }, - "required": ["location", "temperature", "condition"], "additionalProperties": false } } }, - "required": ["elements"], "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }); - let result = generate_result("deepseek-reasoner", &json_options(Some(schema.clone()))).await; - let body = result.request.and_then(|r| r.body).unwrap(); - assert_eq!( - body["messages"][0], - json!({ - "role": "system", - "content": format!("Return JSON that conforms to the following schema: {schema}") - }) - ); - assert_eq!( - body["messages"][1], - json!({ "role": "user", "content": "Hello" }) - ); - assert_eq!(body["response_format"], json!({ "type": "json_object" })); - // upstream: supportsStructuredOutputs is false, so the schema is injected - // into the system message with a compatibility warning. - assert_eq!( - warning_values(&result.warnings), - warning_values(&[compatibility( - "responseFormat JSON schema", - "JSON response schema is injected into the system message." - )]) - ); -} - -#[tokio::test] -async fn json_response_format_should_extract_text_content() { - let mut body = text_response(); - body["choices"][0]["message"]["content"] = json!("{\"elements\":[]}"); - let server = json_server(body).await; - let result = chat(&server, "deepseek-reasoner") - .do_generate(&json_options(None)) - .await - .unwrap(); - assert!( - matches!(&result.content[0], GenerateContent::Text { text, .. } if text == "{\"elements\":[]}") - ); -} - -// ---- describe('assistant prefix completion') -------------------------------------- - -fn prefix_prompt(prefix_options: Value) -> LanguageModelPrompt { - vec![ - LanguageModelMessage::user_text("Complete this sentence."), - with_options( - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("The answer is").into(), - provider_options: None, - })], - provider_options: None, - }, - json!({ "deepseek": prefix_options }), - ), - ] -} - -#[tokio::test] -async fn prefix_should_send_name_and_prefix_on_the_final_assistant_message() { - let server = json_server(text_response()).await; - let body = beta_chat(&server, "deepseek-chat") - .do_generate(&options_for(prefix_prompt( - json!({ "name": "assistant", "prefix": true }), - ))) - .await - .unwrap() - .request - .and_then(|r| r.body) - .unwrap(); - assert_eq!( - body["messages"], - json!([ - { "role": "user", "content": "Complete this sentence." }, - { "role": "assistant", "content": "The answer is", "name": "assistant", "prefix": true } - ]) - ); - assert_eq!(body["model"], "deepseek-chat"); -} - -#[tokio::test] -async fn prefix_should_reject_prefix_completion_with_the_default_base_url() { - let message = generate_error( - "deepseek-chat", - &options_for(vec![with_options( - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("The answer is").into(), - provider_options: None, - })], - provider_options: None, - }, - json!({ "deepseek": { "prefix": true } }), - )]), - ) - .await; - assert!( - message.contains( - "DeepSeek assistant prefix completion requires a beta base URL ending in `/beta`." - ), - "{message}" - ); -} - -// Upstream emits provider error envelopes as error parts, including the first event. -async fn stream_error(data: &Value) -> aimux_core::error::ApiCallError { - let server = sse_server(vec![data_event(data)]).await; - let result = chat(&server, "deepseek-chat") - .do_stream(&options()) - .await - .unwrap(); - collect(result) - .await - .into_iter() - .find_map(|part| match part { - StreamPart::Error { - error: AiMuxError::ApiCall(error), - } => Some(*error), - _ => None, - }) - .expect("expected a provider error part") -} - -/// `deepSeekErrorSchema` as the chat model's failed-response handler -/// (deepseek@3.0.56 `deepseek-chat-api-types.ts:117-124`, -/// `deepseek-chat-language-model.ts:213-217`): `code` may be a number. -#[tokio::test] -async fn a_failed_response_reads_deepseeks_error_schema() { - let server = MockServer::start().await; - Mock::given(method("POST")) - .and(path_regex(".*/chat/completions$")) - .respond_with(ResponseTemplate::new(402).set_body_json(json!({ "error": { - "message": "Insufficient Balance", "type": "unknown_error", "param": null, "code": 4020 - } }))) - .mount(&server) - .await; - let Err(AiMuxError::ApiCall(error)) = - chat(&server, "deepseek-chat").do_generate(&options()).await - else { - panic!("expected an API call error"); - }; - assert_eq!(error.message, "Insufficient Balance"); - assert_eq!(error.provider_code.as_deref(), Some("4020")); - assert_eq!(error.status_code, Some(402)); -} - -#[tokio::test] -async fn stream_should_preserve_a_provider_error_envelope_in_stream_errors() { - let data = json!({ "error": { - "message": "Rate limit reached", "type": "rate_limit_error", "code": "rate_limit_exceeded" - } }); - let error = stream_error(&data).await; - assert_eq!(error.message, "Rate limit reached"); - assert_eq!(error.provider_code.as_deref(), Some("rate_limit_exceeded")); - assert_eq!(error.status_code, Some(429)); - assert!(error.is_retryable); - // upstream: `data` is the whole error envelope. - let envelope: Value = serde_json::from_str(error.response_body.as_deref().unwrap()).unwrap(); - assert_eq!(envelope, data); -} - -#[tokio::test] -async fn stream_should_classify_insufficient_quota_as_non_retryable() { - let data = json!({ "error": { - "message": "You exceeded your current quota.", "type": "rate_limit_error", - "code": "insufficient_quota" - } }); - let error = stream_error(&data).await; - assert_eq!(error.status_code, Some(429)); - assert!(!error.is_retryable); -} - -#[tokio::test] -async fn stream_should_preserve_the_provider_type_when_code_is_an_http_status() { - let data = json!({ "error": { - "message": "Rate limit reached", "type": "rate_limit_error", "code": "429" - } }); - let error = stream_error(&data).await; - assert_eq!(error.provider_code.as_deref(), Some("429")); - assert_eq!(error.status_code, Some(429)); - assert!(error.is_retryable); -} - -async fn stream_parts( - model_id: &str, - options: &CallOptions, - chunks: Vec, -) -> (Value, Vec) { - let server = sse_server(chunks).await; - let result = chat(&server, model_id).do_stream(options).await.unwrap(); - let body = result.request.clone().and_then(|r| r.body).unwrap(); - (body, collect(result).await) -} - -#[tokio::test] -async fn stream_text_should_send_model_id_settings_and_input() { - let mut options = options_for(vec![ - LanguageModelMessage::System { - content: ("You are a helpful assistant.").into(), - provider_options: None, - }, - LanguageModelMessage::user_text("Hello"), - ]); - options.temperature = Some(0.5); - options.top_p = Some(0.3); - let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; - assert_eq!( - body, - json!({ - "messages": [ - { "content": "You are a helpful assistant.", "role": "system" }, - { "content": "Hello", "role": "user" } - ], - "model": "deepseek-chat", - "stream": true, - "stream_options": { "include_usage": true }, - "temperature": 0.5, - "top_p": 0.3 - }) - ); -} - -#[tokio::test] -async fn stream_text_should_omit_deprecated_and_ineffective_sampling_options_in_default_v4_thinking_mode() - { - let mut options = options(); - options.temperature = Some(0.2); - options.top_p = Some(0.4); - options.frequency_penalty = Some(0.5); - options.presence_penalty = Some(0.6); - let (body, parts) = stream_parts("deepseek-v4-flash", &options, text_chunks()).await; - assert_eq!( - body, - json!({ - "model": "deepseek-v4-flash", - "messages": [{ "role": "user", "content": "Hello" }], - "stream": true, - "stream_options": { "include_usage": true } - }) - ); - assert_eq!( - stream_start_warnings(&parts), - warning_values(&[ - deprecated("frequencyPenalty"), - deprecated("presencePenalty"), - thinking_unsupported("temperature"), - thinking_unsupported("topP"), - ]) - ); -} - -#[tokio::test] -async fn stream_text_should_preserve_supported_sampling_options_when_v4_thinking_is_disabled() { - let mut options = with_provider_options( - options(), - json!({ "deepseek": { "thinking": { "type": "disabled" } } }), - ); - options.temperature = Some(0.2); - options.top_p = Some(0.4); - options.frequency_penalty = Some(0.5); - options.presence_penalty = Some(0.6); - let (body, parts) = stream_parts("deepseek-v4-flash", &options, text_chunks()).await; - assert_eq!( - body, - json!({ - "model": "deepseek-v4-flash", - "messages": [{ "role": "user", "content": "Hello" }], - "temperature": 0.2, - "top_p": 0.4, - "thinking": { "type": "disabled" }, - "stream": true, - "stream_options": { "include_usage": true } - }) - ); - assert_eq!( - stream_start_warnings(&parts), - warning_values(&[ - deprecated("frequencyPenalty"), - deprecated("presencePenalty") - ]) - ); -} - -#[tokio::test] -async fn stream_text_should_send_message_names() { - let name = |value: &str| json!({ "deepseek": { "name": value } }); - let options = options_for(vec![ - with_options( - LanguageModelMessage::System { - content: ("You are a helpful assistant.").into(), - provider_options: None, - }, - name("guide"), - ), - with_options(LanguageModelMessage::user_text("Hello"), name("alice")), - with_options( - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("Hello, Alice.").into(), - provider_options: None, - })], - provider_options: None, - }, - name("assistant"), - ), - ]); - let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; - assert_eq!( - body["messages"], - json!([ - { "content": "You are a helpful assistant.", "name": "guide", "role": "system" }, - { "content": "Hello", "name": "alice", "role": "user" }, - { "content": "Hello, Alice.", "name": "assistant", "role": "assistant" } - ]) - ); -} - -#[tokio::test] -async fn stream_text_should_pass_provider_options_user_id_as_user_id() { - let options = with_provider_options( - options(), - json!({ "deepseek": { "userId": "tenant_123-user" } }), - ); - let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; - assert_eq!(body["user_id"], "tenant_123-user"); - assert_eq!(body["stream"], true); -} - -#[tokio::test] -async fn stream_text_should_stream_text() { - let (_, parts) = stream_parts("deepseek-chat", &options(), text_chunks()).await; - assert!(matches!(&parts[0], StreamPart::StreamStart { warnings } if warnings.is_empty())); - assert!(matches!( - &parts[1], - StreamPart::ResponseMetadata(ResponseMetadata { id, model_id, .. }) - if id.as_deref() == Some("c1") && model_id.as_deref() == Some("deepseek-chat") - )); - let deltas: Vec<&str> = parts - .iter() - .filter_map(|part| match part { - StreamPart::TextDelta { id, delta, .. } if id == "txt-0" => Some(delta.as_str()), - _ => None, - }) - .collect(); - assert_eq!(deltas, ["Hello", ", World!"]); - assert!( - parts - .iter() - .any(|p| matches!(p, StreamPart::TextStart { id, .. } if id == "txt-0")) - ); - assert!( - parts - .iter() - .any(|p| matches!(p, StreamPart::TextEnd { id, .. } if id == "txt-0")) + mod json_response_format { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › tool call › json response format › should send correct request body without schema + #[tokio::test] + async fn json_response_format_should_send_correct_request_body_without_schema() { + assert_eq!( + generate_body("deepseek-reasoner", &json_options(None)).await, + json!({ + "messages": [ + { "content": "Return JSON.", "role": "system" }, + { "content": "Hello", "role": "user" } + ], + "model": "deepseek-reasoner", + "response_format": { "type": "json_object" }, + "thinking": { "type": "enabled" }, + "tools": [weather_wire_tool()] + }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › tool call › json response format › should send correct request body with schema + #[tokio::test] + async fn json_response_format_should_send_correct_request_body_with_schema() { + let schema = json!({ + "type": "object", + "properties": { "elements": { "type": "array", "items": { "type": "object", + "properties": { "location": { "type": "string" }, "temperature": { "type": "number" }, + "condition": { "type": "string" } }, + "required": ["location", "temperature", "condition"], "additionalProperties": false } } }, + "required": ["elements"], "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }); + let result = + generate_result("deepseek-reasoner", &json_options(Some(schema.clone()))) + .await; + let body = result.request.and_then(|r| r.body).unwrap(); + assert_eq!( + body["messages"][0], + json!({ + "role": "system", + "content": format!("Return JSON that conforms to the following schema: {schema}") + }) + ); + assert_eq!( + body["messages"][1], + json!({ "role": "user", "content": "Hello" }) + ); + assert_eq!(body["response_format"], json!({ "type": "json_object" })); + // upstream: supportsStructuredOutputs is false, so the schema is injected + // into the system message with a compatibility warning. + assert_eq!( + warning_values(&result.warnings), + warning_values(&[compatibility( + "responseFormat JSON schema", + "JSON response schema is injected into the system message." + )]) + ); + } + } + } + + mod assistant_prefix_completion { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › assistant prefix completion › should send name and prefix on the final assistant message + #[tokio::test] + async fn prefix_should_send_name_and_prefix_on_the_final_assistant_message() { + let server = json_server(text_response()).await; + let body = beta_chat(&server, "deepseek-chat") + .do_generate(&options_for(prefix_prompt( + json!({ "name": "assistant", "prefix": true }), + ))) + .await + .unwrap() + .request + .and_then(|r| r.body) + .unwrap(); + assert_eq!( + body["messages"], + json!([ + { "role": "user", "content": "Complete this sentence." }, + { "role": "assistant", "content": "The answer is", "name": "assistant", "prefix": true } + ]) + ); + assert_eq!(body["model"], "deepseek-chat"); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › assistant prefix completion › should reject prefix completion with the default base URL + #[tokio::test] + async fn prefix_should_reject_prefix_completion_with_the_default_base_url() { + let message = generate_error( + "deepseek-chat", + &options_for(vec![with_options( + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("The answer is").into(), + provider_options: None, + })], + provider_options: None, + }, + json!({ "deepseek": { "prefix": true } }), + )]), + ) + .await; + assert!( + message.contains( + "DeepSeek assistant prefix completion requires a beta base URL ending in `/beta`." + ), + "{message}" ); - match parts.last().unwrap() { - StreamPart::Finish { - finish_reason, - usage, - .. - } => { - assert_eq!(finish_reason.unified, FinishReasonUnified::Stop); - assert_eq!(finish_reason.raw.as_deref(), Some("stop")); - // usage with prompt-cache tokens - assert_eq!(usage.input_tokens.total, Some(13)); - assert_eq!(usage.input_tokens.cache_read, Some(5)); - assert_eq!(usage.input_tokens.no_cache, Some(8)); - assert_eq!(usage.output_tokens.total, Some(3)); + } } - other => panic!("expected finish, got {other:?}"), } - assert_eq!( - finish_metadata(&parts), - json!({ "deepseek": { - "promptCacheHitTokens": 5, - "promptCacheMissTokens": 8, - "responseObject": "chat.completion.chunk", - "choiceIndex": 0, - "messageRole": "assistant", - "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" - } }) - ); -} -#[tokio::test] -async fn stream_text_should_include_the_repeated_system_fingerprint_in_provider_metadata() { - let (_, parts) = stream_parts("deepseek-chat", &options(), text_chunks()).await; - assert_eq!( - finish_metadata(&parts)["deepseek"]["systemFingerprint"], - "fp_eaab8d114b_prod0820_fp8_kvcache" - ); -} + mod do_stream { + use super::*; -#[tokio::test] -async fn stream_text_should_keep_the_latest_non_null_system_fingerprint() { - let chunks = vec![ - "data: {\"system_fingerprint\":\"fp_initial\",\"choices\":[{\"delta\":{\"content\":\"OK\"},\"finish_reason\":null}],\"usage\":null}\n\n".to_string(), - "data: {\"system_fingerprint\":null,\"choices\":[{\"delta\":{},\"finish_reason\":null}],\"usage\":null}\n\n".to_string(), - "data: {\"system_fingerprint\":\"fp_latest\",\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":null}\n\n".to_string(), - ]; - let (_, parts) = stream_parts("deepseek-chat", &options(), chunks).await; - assert_eq!( - finish_metadata(&parts)["deepseek"]["systemFingerprint"], - "fp_latest" - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › should preserve a provider error envelope in stream errors + #[tokio::test] + async fn stream_should_preserve_a_provider_error_envelope_in_stream_errors() { + let data = json!({ "error": { + "message": "Rate limit reached", "type": "rate_limit_error", "code": "rate_limit_exceeded" + } }); + let error = stream_error(&data).await; + assert_eq!(error.message, "Rate limit reached"); + assert_eq!(error.provider_code.as_deref(), Some("rate_limit_exceeded")); + assert_eq!(error.status_code, Some(429)); + assert!(error.is_retryable); + // upstream: `data` is the whole error envelope. + let envelope: Value = + serde_json::from_str(error.response_body.as_deref().unwrap()).unwrap(); + assert_eq!(envelope, data); + } -#[tokio::test] -async fn stream_text_should_tolerate_a_null_or_missing_system_fingerprint() { - for fingerprint in [Some(Value::Null), None] { - let mut chunk = json!({ - "choices": [{ "delta": { "content": "OK" }, "finish_reason": "stop" }], - "usage": null - }); - if let Some(value) = fingerprint { - chunk["system_fingerprint"] = value; + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › should classify insufficient quota as non-retryable + #[tokio::test] + async fn stream_should_classify_insufficient_quota_as_non_retryable() { + let data = json!({ "error": { + "message": "You exceeded your current quota.", "type": "rate_limit_error", + "code": "insufficient_quota" + } }); + let error = stream_error(&data).await; + assert_eq!(error.status_code, Some(429)); + assert!(!error.is_retryable); } - let (_, parts) = stream_parts("deepseek-chat", &options(), vec![data_event(&chunk)]).await; - assert!( - finish_metadata(&parts)["deepseek"] - .get("systemFingerprint") - .is_none() - ); - } -} -// ---- describe('reasoning') ------------------------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › should preserve the provider type when code is an HTTP status + #[tokio::test] + async fn stream_should_preserve_the_provider_type_when_code_is_an_http_status() { + let data = json!({ "error": { + "message": "Rate limit reached", "type": "rate_limit_error", "code": "429" + } }); + let error = stream_error(&data).await; + assert_eq!(error.provider_code.as_deref(), Some("429")); + assert_eq!(error.status_code, Some(429)); + assert!(error.is_retryable); + } -#[tokio::test] -async fn stream_reasoning_should_keep_reasoning_active_when_deltas_include_empty_tool_calls() { - let chunk = |delta: Value, finish: Value| { - data_event(&json!({ - "id": "chatcmpl-test", "object": "chat.completion.chunk", "created": 1, - "model": "test-model", - "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] - })) - }; - let chunks = vec![ - chunk( - json!({ "role": "assistant", "content": "", "reasoning_content": "Think ", "tool_calls": [] }), - Value::Null, - ), - chunk( - json!({ "content": "", "reasoning_content": "more...", "tool_calls": [] }), - Value::Null, - ), - chunk( - json!({ "content": "Hello", "reasoning_content": "", "tool_calls": [] }), - json!("stop"), - ), - ]; - let (_, parts) = stream_parts("deepseek-reasoner", &options(), chunks).await; - let reasoning: Vec = parts - .iter() - .filter_map(|part| match part { - StreamPart::ReasoningStart { id, .. } => Some(format!("start {id}")), - StreamPart::ReasoningDelta { id, delta, .. } => Some(format!("delta {id} {delta}")), - StreamPart::ReasoningEnd { id, .. } => Some(format!("end {id}")), - _ => None, - }) - .collect(); - assert_eq!( - reasoning, - [ - "start reasoning-0", - "delta reasoning-0 Think ", - "delta reasoning-0 more...", - "end reasoning-0" - ] - ); -} + mod text { + use super::*; -#[tokio::test] -async fn stream_reasoning_should_map_legacy_thinking_and_generic_reasoning_to_canonical_request_values() - { - let options = with_provider_options( - reasoning_options(ReasoningEffort::Medium), - json!({ "deepseek": { "thinking": { "type": "adaptive" } } }), - ); - let (body, parts) = stream_parts("deepseek-reasoner", &options, text_chunks()).await; - assert_eq!(body["thinking"], json!({ "type": "enabled" })); - assert_eq!(body["reasoning_effort"], "high"); - assert_eq!( - stream_start_warnings(&parts), - warning_values(&[ - compatibility( - "thinking.type", - "thinking.type \"adaptive\" is not a canonical DeepSeek value. mapped to \"enabled\"." - ), - compatibility( - "reasoning", - "reasoning \"medium\" is not directly supported by this model. mapped to effort \"high\"." - ), - ]) - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should send model id, settings, and input + #[tokio::test] + async fn stream_text_should_send_model_id_settings_and_input() { + let mut options = options_for(vec![ + LanguageModelMessage::System { + content: ("You are a helpful assistant.").into(), + provider_options: None, + }, + LanguageModelMessage::user_text("Hello"), + ]); + options.temperature = Some(0.5); + options.top_p = Some(0.3); + let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; + assert_eq!( + body, + json!({ + "messages": [ + { "content": "You are a helpful assistant.", "role": "system" }, + { "content": "Hello", "role": "user" } + ], + "model": "deepseek-chat", + "stream": true, + "stream_options": { "include_usage": true }, + "temperature": 0.5, + "top_p": 0.3 + }) + ); + } -#[tokio::test] -async fn stream_reasoning_should_stream_reasoning_before_text() { - let chunk = |delta: Value, finish: Value| { - data_event(&json!({ - "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-reasoner", - "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] - })) - }; - let chunks = vec![ - chunk( - json!({ "role": "assistant", "content": null, "reasoning_content": "Let me " }), - Value::Null, - ), - chunk( - json!({ "content": null, "reasoning_content": "think." }), - Value::Null, - ), - chunk(json!({ "content": "Answer" }), Value::Null), - chunk(json!({ "content": "" }), json!("stop")), - ]; - let (_, parts) = stream_parts("deepseek-reasoner", &options(), chunks).await; - let order: Vec<&str> = parts - .iter() - .filter_map(|part| match part { - StreamPart::ReasoningStart { .. } => Some("reasoning-start"), - StreamPart::ReasoningDelta { .. } => Some("reasoning-delta"), - StreamPart::ReasoningEnd { .. } => Some("reasoning-end"), - StreamPart::TextStart { .. } => Some("text-start"), - StreamPart::TextDelta { .. } => Some("text-delta"), - StreamPart::TextEnd { .. } => Some("text-end"), - _ => None, - }) - .collect(); - assert_eq!( - order, - [ - "reasoning-start", - "reasoning-delta", - "reasoning-delta", - "text-start", - "reasoning-end", - "text-delta", - "text-end" - ] - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should omit deprecated and ineffective sampling options in default V4 thinking mode + #[tokio::test] + async fn stream_text_should_omit_deprecated_and_ineffective_sampling_options_in_default_v4_thinking_mode() + { + let mut options = options(); + options.temperature = Some(0.2); + options.top_p = Some(0.4); + options.frequency_penalty = Some(0.5); + options.presence_penalty = Some(0.6); + let (body, parts) = + stream_parts("deepseek-v4-flash", &options, text_chunks()).await; + assert_eq!( + body, + json!({ + "model": "deepseek-v4-flash", + "messages": [{ "role": "user", "content": "Hello" }], + "stream": true, + "stream_options": { "include_usage": true } + }) + ); + assert_eq!( + stream_start_warnings(&parts), + warning_values(&[ + deprecated("frequencyPenalty"), + deprecated("presencePenalty"), + thinking_unsupported("temperature"), + thinking_unsupported("topP"), + ]) + ); + } -// ---- describe('logprobs') ---------------------------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should preserve supported sampling options when V4 thinking is disabled + #[tokio::test] + async fn stream_text_should_preserve_supported_sampling_options_when_v4_thinking_is_disabled() + { + let mut options = with_provider_options( + options(), + json!({ "deepseek": { "thinking": { "type": "disabled" } } }), + ); + options.temperature = Some(0.2); + options.top_p = Some(0.4); + options.frequency_penalty = Some(0.5); + options.presence_penalty = Some(0.6); + let (body, parts) = + stream_parts("deepseek-v4-flash", &options, text_chunks()).await; + assert_eq!( + body, + json!({ + "model": "deepseek-v4-flash", + "messages": [{ "role": "user", "content": "Hello" }], + "temperature": 0.2, + "top_p": 0.4, + "thinking": { "type": "disabled" }, + "stream": true, + "stream_options": { "include_usage": true } + }) + ); + assert_eq!( + stream_start_warnings(&parts), + warning_values(&[ + deprecated("frequencyPenalty"), + deprecated("presencePenalty") + ]) + ); + } -#[tokio::test] -async fn stream_logprobs_should_send_logprobs_provider_options_and_collect_streamed_logprobs() { - let chunk = |choice: Value, usage: Value| { - data_event(&json!({ - "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-v4-flash", - "choices": [choice], "usage": usage - })) - }; - let chunks = vec![ - chunk( - json!({ "index": 0, "delta": { "role": "assistant", "reasoning_content": "Reasoning" }, - "logprobs": { "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] }, - "finish_reason": null }), - Value::Null, - ), - chunk( - json!({ "index": 0, "delta": { "content": "OK" }, - "logprobs": { "content": [logprob("OK", -0.00002467602, json!([79, 75]))] }, - "finish_reason": "stop" }), - json!({ "prompt_tokens": 9, "completion_tokens": 2, "total_tokens": 11, - "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 9 }), - ), - ]; - let options = with_provider_options( - options(), - json!({ "deepseek": { "logprobs": true, "topLogprobs": 1 } }), - ); - let (body, parts) = stream_parts("deepseek-v4-flash", &options, chunks).await; - assert_eq!(body["logprobs"], true); - assert_eq!(body["top_logprobs"], 1); - assert_eq!( - finish_metadata(&parts), - json!({ "deepseek": { - "choiceIndex": 0, - "logprobs": { - "content": [logprob("OK", -0.00002467602, json!([79, 75]))], - "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] - }, - "messageRole": "assistant", - "promptCacheHitTokens": 0, - "promptCacheMissTokens": 9, - "responseObject": "chat.completion.chunk" - } }) - ); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should send message names + #[tokio::test] + async fn stream_text_should_send_message_names() { + let name = |value: &str| json!({ "deepseek": { "name": value } }); + let options = options_for(vec![ + with_options( + LanguageModelMessage::System { + content: ("You are a helpful assistant.").into(), + provider_options: None, + }, + name("guide"), + ), + with_options(LanguageModelMessage::user_text("Hello"), name("alice")), + with_options( + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("Hello, Alice.").into(), + provider_options: None, + })], + provider_options: None, + }, + name("assistant"), + ), + ]); + let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; + assert_eq!( + body["messages"], + json!([ + { "content": "You are a helpful assistant.", "name": "guide", "role": "system" }, + { "content": "Hello", "name": "alice", "role": "user" }, + { "content": "Hello, Alice.", "name": "assistant", "role": "assistant" } + ]) + ); + } -// ---- describe('tool call') ------------------------------------------------------------------ + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should pass providerOptions userId as user_id + #[tokio::test] + async fn stream_text_should_pass_provider_options_user_id_as_user_id() { + let options = with_provider_options( + options(), + json!({ "deepseek": { "userId": "tenant_123-user" } }), + ); + let (body, _) = stream_parts("deepseek-chat", &options, text_chunks()).await; + assert_eq!(body["user_id"], "tenant_123-user"); + assert_eq!(body["stream"], true); + } -#[tokio::test] -async fn stream_tool_call_should_stream_tool_call() { - let chunk = |delta: Value, finish: Value| { - data_event(&json!({ - "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-reasoner", - "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] - })) - }; - let chunks = vec![ - chunk( - json!({ "role": "assistant", "reasoning_content": "Look it up." }), - Value::Null, - ), - chunk( - json!({ "tool_calls": [{ "index": 0, "id": "call_00_abc", "type": "function", - "function": { "name": "weather", "arguments": "" } }] }), - Value::Null, - ), - chunk( - json!({ "tool_calls": [{ "index": 0, "function": { "arguments": "{\"location\":" } }] }), - Value::Null, - ), - chunk( - json!({ "tool_calls": [{ "index": 0, "function": { "arguments": "\"San Francisco\"}" } }] }), - Value::Null, - ), - chunk(json!({ "content": "" }), json!("tool_calls")), - ]; - let (_, parts) = stream_parts("deepseek-reasoner", &tool_options(), chunks).await; - assert!(parts.iter().any(|p| matches!( - p, - StreamPart::ToolInputStart { id, tool_name, .. } if id == "call_00_abc" && tool_name == "weather" - ))); - let input: String = parts - .iter() - .filter_map(|p| match p { - StreamPart::ToolInputDelta { id, delta, .. } if id == "call_00_abc" => { - Some(delta.as_str()) + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should include the repeated system fingerprint in provider metadata + #[tokio::test] + async fn stream_text_should_include_the_repeated_system_fingerprint_in_provider_metadata() + { + let (_, parts) = stream_parts("deepseek-chat", &options(), text_chunks()).await; + assert_eq!( + finish_metadata(&parts)["deepseek"]["systemFingerprint"], + "fp_eaab8d114b_prod0820_fp8_kvcache" + ); } - _ => None, - }) - .collect(); - assert_eq!(input, "{\"location\":\"San Francisco\"}"); - assert!(parts.iter().any(|p| matches!( - p, - StreamPart::ToolCall(RawToolCall { tool_call_id, tool_name, input, .. }) - if tool_call_id == "call_00_abc" && tool_name == "weather" - && input == "{\"location\":\"San Francisco\"}" - ))); - assert!( - parts - .iter() - .any(|p| matches!(p, StreamPart::ReasoningEnd { .. })) - ); - match parts.last().unwrap() { - StreamPart::Finish { finish_reason, .. } => { - assert_eq!(finish_reason.unified, FinishReasonUnified::ToolCalls); - } - other => panic!("expected finish, got {other:?}"), - } - assert_eq!( - finish_metadata(&parts)["deepseek"]["toolCallTypes"], - json!(["function"]) - ); -} -// ---- describe('assistant prefix completion') ----------------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should keep the latest non-null system fingerprint + #[tokio::test] + async fn stream_text_should_keep_the_latest_non_null_system_fingerprint() { + let chunks = vec![ + "data: {\"system_fingerprint\":\"fp_initial\",\"choices\":[{\"delta\":{\"content\":\"OK\"},\"finish_reason\":null}],\"usage\":null}\n\n".to_string(), + "data: {\"system_fingerprint\":null,\"choices\":[{\"delta\":{},\"finish_reason\":null}],\"usage\":null}\n\n".to_string(), + "data: {\"system_fingerprint\":\"fp_latest\",\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":null}\n\n".to_string(), + ]; + let (_, parts) = stream_parts("deepseek-chat", &options(), chunks).await; + assert_eq!( + finish_metadata(&parts)["deepseek"]["systemFingerprint"], + "fp_latest" + ); + } -#[tokio::test] -async fn stream_prefix_should_send_prefix_true_on_the_final_assistant_message() { - let server = sse_server(text_chunks()).await; - let result = beta_chat(&server, "deepseek-chat") - .do_stream(&options_for(prefix_prompt(json!({ "prefix": true })))) - .await - .unwrap(); - let body = result.request.and_then(|r| r.body).unwrap(); - assert_eq!( - body["messages"], - json!([ - { "role": "user", "content": "Complete this sentence." }, - { "role": "assistant", "content": "The answer is", "prefix": true } - ]) - ); - assert_eq!(body["model"], "deepseek-chat"); - assert_eq!(body["stream"], true); -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should tolerate a %s system fingerprint + #[tokio::test] + async fn stream_text_should_tolerate_a_null_or_missing_system_fingerprint() { + for fingerprint in [Some(Value::Null), None] { + let mut chunk = json!({ + "choices": [{ "delta": { "content": "OK" }, "finish_reason": "stop" }], + "usage": null + }); + if let Some(value) = fingerprint { + chunk["system_fingerprint"] = value; + } + let (_, parts) = + stream_parts("deepseek-chat", &options(), vec![data_event(&chunk)]).await; + assert!( + finish_metadata(&parts)["deepseek"] + .get("systemFingerprint") + .is_none() + ); + } + } + } -// =========================================================================== -// convert-to-deepseek-chat-messages.test.ts (through the request body) -// =========================================================================== + mod reasoning { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › reasoning › should keep reasoning active when deltas include empty tool calls + #[tokio::test] + async fn stream_reasoning_should_keep_reasoning_active_when_deltas_include_empty_tool_calls() + { + let chunk = |delta: Value, finish: Value| { + data_event(&json!({ + "id": "chatcmpl-test", "object": "chat.completion.chunk", "created": 1, + "model": "test-model", + "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] + })) + }; + let chunks = vec![ + chunk( + json!({ "role": "assistant", "content": "", "reasoning_content": "Think ", "tool_calls": [] }), + Value::Null, + ), + chunk( + json!({ "content": "", "reasoning_content": "more...", "tool_calls": [] }), + Value::Null, + ), + chunk( + json!({ "content": "Hello", "reasoning_content": "", "tool_calls": [] }), + json!("stop"), + ), + ]; + let (_, parts) = stream_parts("deepseek-reasoner", &options(), chunks).await; + let reasoning: Vec = parts + .iter() + .filter_map(|part| match part { + StreamPart::ReasoningStart { id, .. } => Some(format!("start {id}")), + StreamPart::ReasoningDelta { id, delta, .. } => { + Some(format!("delta {id} {delta}")) + } + StreamPart::ReasoningEnd { id, .. } => Some(format!("end {id}")), + _ => None, + }) + .collect(); + assert_eq!( + reasoning, + [ + "start reasoning-0", + "delta reasoning-0 Think ", + "delta reasoning-0 more...", + "end reasoning-0" + ] + ); + } -fn png_data() -> UserPart { - UserPart::File(FilePart { - data: FileData::Data { - data: FileBytes::Base64("AAECAw==".into()), - }, - media_type: "image/png".into(), - filename: None, - provider_options: None, - }) -} + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › reasoning › should map legacy thinking and generic reasoning to canonical request values + #[tokio::test] + async fn stream_reasoning_should_map_legacy_thinking_and_generic_reasoning_to_canonical_request_values() + { + let options = with_provider_options( + reasoning_options(ReasoningEffort::Medium), + json!({ "deepseek": { "thinking": { "type": "adaptive" } } }), + ); + let (body, parts) = + stream_parts("deepseek-reasoner", &options, text_chunks()).await; + assert_eq!(body["thinking"], json!({ "type": "enabled" })); + assert_eq!(body["reasoning_effort"], "high"); + assert_eq!( + stream_start_warnings(&parts), + warning_values(&[ + compatibility( + "thinking.type", + "thinking.type \"adaptive\" is not a canonical DeepSeek value. mapped to \"enabled\"." + ), + compatibility( + "reasoning", + "reasoning \"medium\" is not directly supported by this model. mapped to effort \"high\"." + ), + ]) + ); + } + } -fn file_with_options(mut part: UserPart, options: Value) -> UserPart { - if let UserPart::File(file) = &mut part { - file.provider_options = Some(serde_json::from_value(options).unwrap()); + mod logprobs { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › logprobs › should send logprobs provider options and collect streamed logprobs + #[tokio::test] + async fn stream_logprobs_should_send_logprobs_provider_options_and_collect_streamed_logprobs() + { + let chunk = |choice: Value, usage: Value| { + data_event(&json!({ + "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-v4-flash", + "choices": [choice], "usage": usage + })) + }; + let chunks = vec![ + chunk( + json!({ "index": 0, "delta": { "role": "assistant", "reasoning_content": "Reasoning" }, + "logprobs": { "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] }, + "finish_reason": null }), + Value::Null, + ), + chunk( + json!({ "index": 0, "delta": { "content": "OK" }, + "logprobs": { "content": [logprob("OK", -0.00002467602, json!([79, 75]))] }, + "finish_reason": "stop" }), + json!({ "prompt_tokens": 9, "completion_tokens": 2, "total_tokens": 11, + "prompt_cache_hit_tokens": 0, "prompt_cache_miss_tokens": 9 }), + ), + ]; + let options = with_provider_options( + options(), + json!({ "deepseek": { "logprobs": true, "topLogprobs": 1 } }), + ); + let (body, parts) = stream_parts("deepseek-v4-flash", &options, chunks).await; + assert_eq!(body["logprobs"], true); + assert_eq!(body["top_logprobs"], 1); + assert_eq!( + finish_metadata(&parts), + json!({ "deepseek": { + "choiceIndex": 0, + "logprobs": { + "content": [logprob("OK", -0.00002467602, json!([79, 75]))], + "reasoning_content": [logprob("Reasoning", -0.1, Value::Null)] + }, + "messageRole": "assistant", + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 9, + "responseObject": "chat.completion.chunk" + } }) + ); + } + } + + mod assistant_prefix_completion { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › assistant prefix completion › should send prefix true on the final assistant message + #[tokio::test] + async fn stream_prefix_should_send_prefix_true_on_the_final_assistant_message() { + let server = sse_server(text_chunks()).await; + let result = beta_chat(&server, "deepseek-chat") + .do_stream(&options_for(prefix_prompt(json!({ "prefix": true })))) + .await + .unwrap(); + let body = result.request.and_then(|r| r.body).unwrap(); + assert_eq!( + body["messages"], + json!([ + { "role": "user", "content": "Complete this sentence." }, + { "role": "assistant", "content": "The answer is", "prefix": true } + ]) + ); + assert_eq!(body["model"], "deepseek-chat"); + assert_eq!(body["stream"], true); + } + } } - part } -// ---- describe('message names') ------------------------------------------------------------- - -#[tokio::test] -async fn convert_should_ignore_a_name_on_a_tool_message_with_an_unsupported_warning() { - let options = options_for(vec![with_options( - LanguageModelMessage::Tool { - content: vec![ToolPart::ToolResult(ToolResultPart { - tool_call_id: ("call-1").into(), - output: ToolResultOutput::Text { - value: "sunny".into(), +mod convert_to_deep_seek_chat_messages { + use super::*; + + mod message_names { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › message names › should ignore a name on a tool message with an unsupported warning + #[tokio::test] + async fn convert_should_ignore_a_name_on_a_tool_message_with_an_unsupported_warning() { + let options = options_for(vec![with_options( + LanguageModelMessage::Tool { + content: vec![ToolPart::ToolResult(ToolResultPart { + tool_call_id: ("call-1").into(), + output: ToolResultOutput::Text { + value: "sunny".into(), + provider_options: None, + }, + tool_name: "weather_tool".into(), + provider_options: None, + })], provider_options: None, }, - tool_name: "weather_tool".into(), - provider_options: None, - })], - provider_options: None, - }, - json!({ "deepseek": { "name": "weather_tool" } }), - )]); - let result = generate_result("deepseek-chat", &options).await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["messages"], - json!([{ "role": "tool", "tool_call_id": "call-1", "content": "sunny" }]) - ); - assert_eq!( - warning_values(&result.warnings), - warning_values(&[Warning::Unsupported { - feature: "message name on tool messages".to_string(), - details: None, - }]) - ); -} - -#[tokio::test] -async fn convert_should_reject_a_non_string_name() { - let options = options_for(vec![with_options( - LanguageModelMessage::user_text("Hello"), - json!({ "deepseek": { "name": 123 } }), - )]); - let message = generate_error("deepseek-chat", &options).await; - assert!(message.contains("invalid provider options"), "{message}"); -} + json!({ "deepseek": { "name": "weather_tool" } }), + )]); + let result = generate_result("deepseek-chat", &options).await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["messages"], + json!([{ "role": "tool", "tool_call_id": "call-1", "content": "sunny" }]) + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[Warning::Unsupported { + feature: "message name on tool messages".to_string(), + details: None, + }]) + ); + } -// ---- describe('user messages') --------------------------------------------------------------- + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › message names › should reject a non-string name + #[tokio::test] + async fn convert_should_reject_a_non_string_name() { + let options = options_for(vec![with_options( + LanguageModelMessage::user_text("Hello"), + json!({ "deepseek": { "name": 123 } }), + )]); + let message = generate_error("deepseek-chat", &options).await; + assert!(message.contains("invalid provider options"), "{message}"); + } + } -#[tokio::test] -async fn convert_should_convert_messages_with_only_a_text_part_to_a_string_content() { - let messages = wire_messages( - "deepseek-chat", - vec![LanguageModelMessage::user_text("Hello")], - ) - .await; - assert_eq!(messages, [json!({ "role": "user", "content": "Hello" })]); -} + mod user_messages { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should convert messages with only a text part to a string content + #[tokio::test] + async fn convert_should_convert_messages_with_only_a_text_part_to_a_string_content() { + let messages = wire_messages( + "deepseek-chat", + vec![LanguageModelMessage::user_text("Hello")], + ) + .await; + assert_eq!(messages, [json!({ "role": "user", "content": "Hello" })]); + } -#[tokio::test] -async fn convert_should_convert_image_data_to_an_image_url_content_part() { - let messages = wire_messages( - "deepseek-chat", - vec![LanguageModelMessage::User { - content: vec![ - UserPart::Text(TextPart { - text: ("Hello").into(), + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should convert image data to an image URL content part + #[tokio::test] + async fn convert_should_convert_image_data_to_an_image_url_content_part() { + let messages = wire_messages( + "deepseek-chat", + vec![LanguageModelMessage::User { + content: vec![ + UserPart::Text(TextPart { + text: ("Hello").into(), + provider_options: None, + }), + png_data(), + ], provider_options: None, - }), - png_data(), - ], - provider_options: None, - }], - ) - .await; - assert_eq!( - messages, - [json!({ "role": "user", "content": [ + }], + ) + .await; + assert_eq!( + messages, + [json!({ "role": "user", "content": [ { "type": "text", "text": "Hello" }, { "type": "image_url", "image_url": { "url": "data:image/png;base64,AAECAw==" } } ] })] - ); -} + ); + } -#[tokio::test] -async fn convert_should_convert_an_image_url_to_an_image_url_content_part() { - let messages = wire_messages( - "deepseek-chat", - vec![LanguageModelMessage::User { - content: vec![ + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should convert an image URL to an image URL content part + #[tokio::test] + async fn convert_should_convert_an_image_url_to_an_image_url_content_part() { + let messages = wire_messages( + "deepseek-chat", + vec![LanguageModelMessage::User { + content: vec![ + UserPart::Text(TextPart { + text: ("Hello").into(), + provider_options: None, + }), + UserPart::File(FilePart { + data: FileData::Url { + url: ("https://example.com/image.png").into(), + original_url: None, + }, + media_type: ("image/png").into(), + filename: None, + provider_options: None, + }), + ], + provider_options: None, + }], + ) + .await; + assert_eq!( + messages, + [json!({ "role": "user", "content": [ + { "type": "text", "text": "Hello" }, + { "type": "image_url", "image_url": { "url": "https://example.com/image.png" } } + ] })] + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should pass imageDetail to image URL content parts + #[tokio::test] + async fn convert_should_pass_image_detail_to_image_url_content_parts() { + let messages = wire_messages( + "deepseek-v4-flash-vision-exp", + vec![LanguageModelMessage::User { + content: vec![file_with_options( + UserPart::File(FilePart { + data: FileData::Url { + url: ("https://example.com/image.webp").into(), + original_url: None, + }, + media_type: ("image/webp").into(), + filename: None, + provider_options: None, + }), + json!({ "deepseek": { "imageDetail": "low" } }), + )], + provider_options: None, + }], + ) + .await; + assert_eq!( + messages, + [json!({ "role": "user", "content": [ + { "type": "image_url", "image_url": { "url": "https://example.com/image.webp", "detail": "low" } } + ] })] + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should convert inline image data to file_data and preserve its filename + #[tokio::test] + async fn convert_should_convert_inline_image_data_to_file_data_and_preserve_its_filename() { + let part = UserPart::File(FilePart { + data: FileData::Data { + data: FileBytes::Base64("AAECAw==".to_string()), + }, + media_type: "image/jpg".to_string(), + filename: Some("sample.jpg".to_string()), + provider_options: Some( + provider_namespace("deepseek", json!({ "fileData": true })).unwrap(), + ), + }); + let messages = wire_messages( + "deepseek-v4-flash-vision-exp", + vec![LanguageModelMessage::User { + content: vec![part], + provider_options: None, + }], + ) + .await; + assert_eq!( + messages, + [json!({ "role": "user", "content": [ + { "type": "file", "file_data": "data:image/jpeg;base64,AAECAw==", "filename": "sample.jpg" } + ] })] + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should reject imageDetail together with fileData + #[tokio::test] + async fn convert_should_reject_image_detail_together_with_file_data() { + let part = file_with_options( + png_data(), + json!({ "deepseek": { "fileData": true, "imageDetail": "high" } }), + ); + let message = generate_error( + "deepseek-v4-flash-vision-exp", + &options_for(vec![LanguageModelMessage::User { + content: vec![part], + provider_options: None, + }]), + ) + .await; + assert!( + message.contains("DeepSeek `imageDetail` cannot be combined with `fileData`."), + "{message}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should reject image URLs longer than 8192 characters + #[tokio::test] + async fn convert_should_reject_image_urls_longer_than_8192_characters() { + let url = format!("https://example.com/{}", "a".repeat(8192)); + let message = generate_error( + "deepseek-v4-flash-vision-exp", + &options_for(vec![LanguageModelMessage::User { + content: vec![UserPart::File(FilePart { + data: FileData::Url { + url: (url), + original_url: None, + }, + media_type: ("image/png").into(), + filename: None, + provider_options: None, + })], + provider_options: None, + }]), + ) + .await; + assert!( + message.contains("DeepSeek image URLs must not exceed 8192 characters."), + "{message}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should reject unsupported image formats + #[tokio::test] + async fn convert_should_reject_unsupported_image_formats() { + let message = generate_error( + "deepseek-v4-flash-vision-exp", + &options_for(vec![LanguageModelMessage::User { + content: vec![UserPart::File(FilePart { + data: FileData::Data { + data: FileBytes::Base64(("AAECAw==").into()), + }, + media_type: ("image/svg+xml").into(), + filename: None, + provider_options: None, + })], + provider_options: None, + }]), + ) + .await; + assert!( + message.contains("DeepSeek supports JPEG, PNG, GIF, and WebP image inputs."), + "{message}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should convert an image provider reference to a file content part + #[tokio::test] + async fn convert_should_convert_an_image_provider_reference_to_a_file_content_part() { + let messages = wire_messages( + "deepseek-v4-flash-vision-exp", + vec![LanguageModelMessage::User { + content: vec![ UserPart::Text(TextPart { text: ("Hello").into(), provider_options: None, }), UserPart::File(FilePart { - data: FileData::Url { - url: ("https://example.com/image.png").into(), - original_url: None, + data: FileData::Reference { + reference: serde_json::from_value( + json!({ "deepseek": "file-api-deepseek", "openai": "file-openai" }), + ) + .unwrap(), }, media_type: ("image/png").into(), filename: None, provider_options: None, }), ], - provider_options: None, - }], - ) - .await; - assert_eq!( - messages, - [json!({ "role": "user", "content": [ + provider_options: None, + }], + ) + .await; + assert_eq!( + messages, + [json!({ "role": "user", "content": [ { "type": "text", "text": "Hello" }, - { "type": "image_url", "image_url": { "url": "https://example.com/image.png" } } + { "type": "file", "file_id": "file-api-deepseek" } ] })] + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should throw when an image reference has no DeepSeek identifier + #[tokio::test] + async fn convert_should_throw_when_an_image_reference_has_no_deepseek_identifier() { + let message = generate_error( + "deepseek-v4-flash-vision-exp", + &options_for(vec![LanguageModelMessage::User { + content: vec![UserPart::File(FilePart { + data: FileData::Reference { + reference: serde_json::from_value(json!({ "openai": "file-openai" })) + .unwrap(), + }, + media_type: ("image/png").into(), + filename: None, + provider_options: None, + })], + provider_options: None, + }]), + ) + .await; + assert!(message.contains("deepseek"), "{message}"); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › user messages › should warn about unsupported non-image file parts + #[tokio::test] + async fn convert_should_warn_about_unsupported_non_image_file_parts() { + let result = generate_result( + "deepseek-chat", + &options_for(vec![LanguageModelMessage::User { + content: vec![ + UserPart::Text(TextPart { + text: ("Hello").into(), + provider_options: None, + }), + UserPart::File(FilePart { + data: FileData::Data { + data: FileBytes::Base64(("AAECAw==").into()), + }, + media_type: ("application/pdf").into(), + filename: None, + provider_options: None, + }), + ], + provider_options: None, + }]), + ) + .await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["messages"], + json!([{ "role": "user", "content": "Hello" }]) + ); + assert_eq!( + warning_values(&result.warnings), + warning_values(&[Warning::Unsupported { + feature: "user message part type: file".to_string(), + details: None, + }]) + ); + } + } + + mod tool_calls { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should stringify arguments to tool calls + #[tokio::test] + async fn convert_should_stringify_arguments_to_tool_calls() { + let result = generate_result("deepseek-chat", &options_for(tool_turn(false))).await; + assert_eq!( + result.request.and_then(|r| r.body).unwrap()["messages"], + json!([ + // upstream sends the empty text as `content: ""`, not null. + { "role": "assistant", "content": "", "tool_calls": wire_tool_call() }, + { "role": "tool", "tool_call_id": "quux", "content": "{\"oof\":\"321rab\"}" } + ]) + ); + assert!(result.warnings.is_empty()); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should handle text output type in tool results + #[tokio::test] + async fn convert_should_handle_text_output_type_in_tool_results() { + let messages = wire_messages( + "deepseek-chat", + vec![ + LanguageModelMessage::Assistant { + content: vec![AssistantPart::ToolCall(ToolCallPart { + tool_call_id: ("call-1").into(), + tool_name: ("getWeather").into(), + input: json!({ "query": "weather" }), + provider_executed: None, + provider_options: None, + })], + provider_options: None, + }, + LanguageModelMessage::Tool { + content: vec![ToolPart::ToolResult(ToolResultPart { + tool_call_id: ("call-1").into(), + output: ToolResultOutput::Text { + value: "It is sunny today".into(), + provider_options: None, + }, + tool_name: "getWeather".into(), + provider_options: None, + })], + provider_options: None, + }, + ], + ) + .await; + assert_eq!( + messages[1], + json!({ "role": "tool", "tool_call_id": "call-1", "content": "It is sunny today" }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should support reasoning content in tool calls + #[tokio::test] + async fn convert_should_support_reasoning_content_in_tool_calls() { + let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; + prompt.extend(tool_turn(true)); + let messages = wire_messages("deepseek-chat", prompt).await; + assert_eq!( + messages[1], + json!({ + "role": "assistant", "content": "", + "reasoning_content": "I think the tool will return the correct value.", + "tool_calls": wire_tool_call() + }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should filter out reasoning content from turns before the last user message + #[tokio::test] + async fn convert_should_filter_out_reasoning_content_from_turns_before_the_last_user_message() + { + let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; + prompt.extend(tool_turn(true)); + prompt.push(LanguageModelMessage::user_text("Goodbye")); + let messages = wire_messages("deepseek-chat", prompt).await; + assert_eq!( + messages[1], + json!({ "role": "assistant", "content": "", "tool_calls": wire_tool_call() }) + ); + } + } + + mod deepseek_v4_thinking_mode { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › deepseek-v4 thinking mode › should preserve reasoning_content from prior turns for deepseek-v4 + #[tokio::test] + async fn convert_should_preserve_reasoning_content_from_prior_turns_for_deepseek_v4() { + let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; + prompt.extend(tool_turn(true)); + prompt.push(LanguageModelMessage::user_text("Goodbye")); + let messages = wire_messages("deepseek-v4-pro", prompt).await; + assert_eq!( + messages[1], + json!({ + "role": "assistant", "content": "", + "reasoning_content": "I think the tool will return the correct value.", + "tool_calls": wire_tool_call() + }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › deepseek-v4 thinking mode › should preserve reasoning_content from prior turns for the deepseek-flash alias + #[tokio::test] + async fn convert_should_preserve_reasoning_content_from_prior_turns_for_the_deepseek_flash_alias() + { + let messages = wire_messages( + "deepseek-flash", + vec![ + LanguageModelMessage::user_text("Hello"), + LanguageModelMessage::Assistant { + content: vec![ + AssistantPart::Reasoning(ReasoningPart { + text: ("Prior-turn reasoning.").into(), + provider_options: None, + }), + AssistantPart::Text(TextPart { + text: ("Hi there").into(), + provider_options: None, + }), + ], + provider_options: None, + }, + LanguageModelMessage::user_text("Again"), + ], + ) + .await; + assert_eq!( + messages[1], + json!({ "role": "assistant", "content": "Hi there", "reasoning_content": "Prior-turn reasoning." }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › deepseek-v4 thinking mode › should back-fill empty reasoning_content for deepseek-v4 assistant messages with no reasoning part + #[tokio::test] + async fn convert_should_back_fill_empty_reasoning_content_for_deepseek_v4_assistant_messages_with_no_reasoning_part() + { + let messages = wire_messages( + "deepseek-v4-pro", + vec![ + LanguageModelMessage::user_text("Hello"), + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("Hi there").into(), + provider_options: None, + })], + provider_options: None, + }, + LanguageModelMessage::user_text("Again"), + ], + ) + .await; + assert_eq!( + messages[1], + json!({ "role": "assistant", "content": "Hi there", "reasoning_content": "" }) + ); + } + } + + mod assistant_prefix_completion { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › assistant prefix completion › should reject prefix completion on a non-assistant message + #[tokio::test] + async fn convert_should_reject_prefix_completion_on_a_non_assistant_message() { + let message = generate_error( + "deepseek-chat", + &options_for(vec![with_options( + LanguageModelMessage::user_text("Hello"), + json!({ "deepseek": { "prefix": true } }), + )]), + ) + .await; + assert!( + message.contains( + "DeepSeek assistant prefix completion requires `prefix: true` on an assistant message." + ), + "{message}" ); -} + } -#[tokio::test] -async fn convert_should_pass_image_detail_to_image_url_content_parts() { - let messages = wire_messages( - "deepseek-v4-flash-vision-exp", - vec![LanguageModelMessage::User { - content: vec![file_with_options( - UserPart::File(FilePart { - data: FileData::Url { - url: ("https://example.com/image.webp").into(), - original_url: None, + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › assistant prefix completion › should reject prefix completion on a non-final assistant message + #[tokio::test] + async fn convert_should_reject_prefix_completion_on_a_non_final_assistant_message() { + let server = json_server(text_response()).await; + let prompt = vec![ + with_options( + LanguageModelMessage::Assistant { + content: vec![AssistantPart::Text(TextPart { + text: ("The answer is").into(), + provider_options: None, + })], + provider_options: None, }, - media_type: ("image/webp").into(), - filename: None, - provider_options: None, - }), - json!({ "deepseek": { "imageDetail": "low" } }), - )], + json!({ "deepseek": { "prefix": true } }), + ), + LanguageModelMessage::user_text("Continue"), + ]; + let error = beta_chat(&server, "deepseek-chat") + .do_generate(&options_for(prompt)) + .await + .unwrap_err(); + assert!( + error.to_string().contains( + "DeepSeek assistant prefix completion requires the prefixed assistant message to be the final message." + ), + "{error}" + ); + } + } +} + +mod prepare_tools { + use super::*; + + /// A tool with `strict: false` should produce `"strict": false` in the + /// serialized tool definition. + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should pass through strict mode when strict is false + #[tokio::test] + async fn should_pass_through_strict_mode_when_strict_is_false() { + let tool = FunctionTool { + name: "testFunction".to_string(), + description: Some("A test function".to_string()), + input_schema: json!({ "type": "object", "properties": {} }), + strict: Some(false), provider_options: None, - }], - ) - .await; + input_examples: None, + }; + let mut options = options(); + options.tools = Some(vec![Tool::from(tool)]); + let body = generate_body("deepseek-chat", &options).await; + + assert_eq!( + body["tools"], + json!([{ + "type": "function", + "function": { + "description": "A test function", + "name": "testFunction", + "parameters": { "type": "object", "properties": {} }, + "strict": false + } + }]) + ); + } + + /// A tool without `strict` set should NOT have a `"strict"` field in the + /// serialized tool definition. + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should not include strict mode when strict is undefined + #[tokio::test] + async fn should_not_include_strict_mode_when_strict_is_undefined() { + let tool = FunctionTool { + name: "testFunction".to_string(), + description: Some("A test function".to_string()), + input_schema: json!({ "type": "object", "properties": {} }), + strict: None, + provider_options: None, + input_examples: None, + }; + let mut options = options(); + options.tools = Some(vec![Tool::from(tool)]); + let body = generate_body("deepseek-chat", &options).await; + + assert_eq!( + body["tools"], + json!([{ + "type": "function", + "function": { + "description": "A test function", + "name": "testFunction", + "parameters": { "type": "object", "properties": {} } + } + }]) + ); + // Verify strict is absent. + assert!( + body["tools"][0]["function"].get("strict").is_none(), + "strict should be absent" + ); + } +} + +// ---- Tests without a matching upstream case (aimux-own, or a partial port) ---- + +#[tokio::test] +async fn text_should_send_max_tokens_and_stop_sequences() { + let mut options = options(); + options.max_output_tokens = Some(64); + options.stop_sequences = Some(vec!["END".to_string()]); + let body = generate_body("deepseek-chat", &options).await; + assert_eq!(body["max_tokens"], 64); + assert_eq!(body["stop"], json!(["END"])); + assert!(body.get("max_completion_tokens").is_none()); +} + +#[tokio::test] +async fn text_should_not_send_unknown_provider_options() { + // upstream: `deepseekLanguageModelChatOptions` is a zod object, which + // strips keys it does not declare. + let options = + with_provider_options(options(), json!({ "deepseek": { "user": "u1", "foo": 1 } })); + let body = generate_body("deepseek-chat", &options).await; + assert!(body.get("user").is_none() && body.get("foo").is_none()); +} + +#[tokio::test] +async fn text_should_extract_text_content() { + let result = generate_result("deepseek-chat", &options()).await; + assert_eq!(result.content.len(), 1); + assert!( + matches!(&result.content[0], GenerateContent::Text { text, .. } if text == "Hello, World!") + ); + assert_eq!(result.finish_reason.unified, FinishReasonUnified::Stop); + assert_eq!(result.finish_reason.raw.as_deref(), Some("stop")); + assert_eq!(result.usage.input_tokens.total, Some(13)); + assert_eq!(result.usage.output_tokens.total, Some(300)); + assert_eq!( + result.response.as_ref().and_then(|r| r.id.as_deref()), + Some("00f10ecd-60b3-4707-b5db-e4bcadf7aea1") + ); assert_eq!( - messages, - [json!({ "role": "user", "content": [ - { "type": "image_url", "image_url": { "url": "https://example.com/image.webp", "detail": "low" } } - ] })] + result.response.as_ref().and_then(|r| r.model_id.as_deref()), + Some("deepseek-chat") ); } #[tokio::test] -async fn convert_should_convert_inline_image_data_to_file_data_and_preserve_its_filename() { - let part = UserPart::File(FilePart { - data: FileData::Data { - data: FileBytes::Base64("AAECAw==".to_string()), - }, - media_type: "image/jpg".to_string(), - filename: Some("sample.jpg".to_string()), - provider_options: Some( - provider_namespace("deepseek", json!({ "fileData": true })).unwrap(), - ), +async fn text_should_report_usage_with_prompt_cache_tokens() { + // upstream convert-to-deepseek-usage.ts: cache hits are the cache-read tokens. + let mut body = text_response(); + body["usage"] = json!({ + "prompt_tokens": 100, "completion_tokens": 20, "total_tokens": 120, + "prompt_cache_hit_tokens": 70, "prompt_cache_miss_tokens": 30, + "completion_tokens_details": { "reasoning_tokens": 5 } }); - let messages = wire_messages( - "deepseek-v4-flash-vision-exp", - vec![LanguageModelMessage::User { - content: vec![part], - provider_options: None, - }], - ) - .await; - assert_eq!( - messages, - [json!({ "role": "user", "content": [ - { "type": "file", "file_data": "data:image/jpeg;base64,AAECAw==", "filename": "sample.jpg" } - ] })] - ); + let server = json_server(body).await; + let usage = chat(&server, "deepseek-chat") + .do_generate(&options()) + .await + .unwrap() + .usage; + assert_eq!(usage.input_tokens.total, Some(100)); + assert_eq!(usage.input_tokens.cache_read, Some(70)); + assert_eq!(usage.input_tokens.no_cache, Some(30)); + assert_eq!(usage.output_tokens.total, Some(20)); + assert_eq!(usage.output_tokens.reasoning, Some(5)); + assert_eq!(usage.output_tokens.text, Some(15)); + assert_eq!(usage.raw.unwrap()["prompt_cache_hit_tokens"], 70); } #[tokio::test] -async fn convert_should_reject_image_detail_together_with_file_data() { - let part = file_with_options( - png_data(), - json!({ "deepseek": { "fileData": true, "imageDetail": "high" } }), - ); - let message = generate_error( - "deepseek-v4-flash-vision-exp", - &options_for(vec![LanguageModelMessage::User { - content: vec![part], - provider_options: None, - }]), - ) - .await; - assert!( - message.contains("DeepSeek `imageDetail` cannot be combined with `fileData`."), - "{message}" +async fn text_should_report_the_provider_metadata() { + // upstream doGenerate: promptCacheHitTokens, promptCacheMissTokens, + // responseObject, choiceIndex, messageRole, systemFingerprint. + let result = generate_result("deepseek-chat", &options()).await; + assert_eq!( + serde_json::to_value(result.provider_metadata.unwrap()).unwrap(), + json!({ "deepseek": { + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 13, + "responseObject": "chat.completion", + "choiceIndex": 0, + "messageRole": "assistant", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } }) ); } #[tokio::test] -async fn convert_should_reject_image_urls_longer_than_8192_characters() { - let url = format!("https://example.com/{}", "a".repeat(8192)); - let message = generate_error( - "deepseek-v4-flash-vision-exp", - &options_for(vec![LanguageModelMessage::User { - content: vec![UserPart::File(FilePart { - data: FileData::Url { - url: (url), - original_url: None, - }, - media_type: ("image/png").into(), - filename: None, - provider_options: None, - })], - provider_options: None, - }]), - ) - .await; +async fn reasoning_should_extract_reasoning_before_text() { + let server = json_server(reasoning_response()).await; + let result = chat(&server, "deepseek-reasoner") + .do_generate(&options()) + .await + .unwrap(); + assert_eq!(result.content.len(), 2); assert!( - message.contains("DeepSeek image URLs must not exceed 8192 characters."), - "{message}" + matches!(&result.content[0], GenerateContent::Reasoning(ReasoningOutput { text, .. }) if text == "Let me count the letters.") + ); + assert!( + matches!(&result.content[1], GenerateContent::Text { text, .. } if text == "There are 3 r's in strawberry.") ); + assert_eq!(result.usage.output_tokens.reasoning, Some(10)); + assert_eq!(result.usage.output_tokens.text, Some(20)); } #[tokio::test] -async fn convert_should_reject_unsupported_image_formats() { - let message = generate_error( - "deepseek-v4-flash-vision-exp", - &options_for(vec![LanguageModelMessage::User { - content: vec![UserPart::File(FilePart { - data: FileData::Data { - data: FileBytes::Base64(("AAECAw==").into()), - }, - media_type: ("image/svg+xml").into(), - filename: None, - provider_options: None, - })], - provider_options: None, - }]), - ) - .await; - assert!( - message.contains("DeepSeek supports JPEG, PNG, GIF, and WebP image inputs."), - "{message}" - ); +async fn reasoning_should_not_read_the_reasoning_field() { + // upstream deepseekChatResponseSchema only declares `reasoning_content`. + let mut body = reasoning_response(); + let message = body["choices"][0]["message"].as_object_mut().unwrap(); + let reasoning = message.remove("reasoning_content").unwrap(); + message.insert("reasoning".to_string(), reasoning); + let server = json_server(body).await; + let result = chat(&server, "deepseek-reasoner") + .do_generate(&options()) + .await + .unwrap(); + assert_eq!(result.content.len(), 1); + assert!(matches!(&result.content[0], GenerateContent::Text { .. })); } #[tokio::test] -async fn convert_should_convert_an_image_provider_reference_to_a_file_content_part() { - let messages = wire_messages( - "deepseek-v4-flash-vision-exp", - vec![LanguageModelMessage::User { - content: vec![ - UserPart::Text(TextPart { - text: ("Hello").into(), - provider_options: None, - }), - UserPart::File(FilePart { - data: FileData::Reference { - reference: serde_json::from_value( - json!({ "deepseek": "file-api-deepseek", "openai": "file-openai" }), - ) - .unwrap(), - }, - media_type: ("image/png").into(), - filename: None, - provider_options: None, - }), - ], - provider_options: None, - }], - ) - .await; +async fn top_level_reasoning_none_should_keep_the_temperature() { + // thinking is disabled, so temperature applies and is not warned about. + let mut options = reasoning_options(ReasoningEffort::None); + options.temperature = Some(0.4); + let result = generate_result("deepseek-reasoner", &options).await; assert_eq!( - messages, - [json!({ "role": "user", "content": [ - { "type": "text", "text": "Hello" }, - { "type": "file", "file_id": "file-api-deepseek" } - ] })] + result.request.and_then(|r| r.body).unwrap()["temperature"], + 0.4 ); + assert!(result.warnings.is_empty()); } #[tokio::test] -async fn convert_should_throw_when_an_image_reference_has_no_deepseek_identifier() { - let message = generate_error( - "deepseek-v4-flash-vision-exp", - &options_for(vec![LanguageModelMessage::User { - content: vec![UserPart::File(FilePart { - data: FileData::Reference { - reference: serde_json::from_value(json!({ "openai": "file-openai" })).unwrap(), - }, - media_type: ("image/png").into(), - filename: None, - provider_options: None, - })], - provider_options: None, - }]), +async fn top_level_reasoning_provider_default_sends_neither_thinking_nor_effort() { + let body = generate_body( + "deepseek-reasoner", + &reasoning_options(ReasoningEffort::ProviderDefault), ) .await; - assert!(message.contains("deepseek"), "{message}"); + assert!(body.get("thinking").is_none() && body.get("reasoning_effort").is_none()); } #[tokio::test] -async fn convert_should_warn_about_unsupported_non_image_file_parts() { - let result = generate_result( - "deepseek-chat", - &options_for(vec![LanguageModelMessage::User { - content: vec![ - UserPart::Text(TextPart { - text: ("Hello").into(), - provider_options: None, - }), - UserPart::File(FilePart { - data: FileData::Data { - data: FileBytes::Base64(("AAECAw==").into()), - }, - media_type: ("application/pdf").into(), - filename: None, - provider_options: None, - }), - ], - provider_options: None, - }]), - ) - .await; - assert_eq!( - result.request.and_then(|r| r.body).unwrap()["messages"], - json!([{ "role": "user", "content": "Hello" }]) - ); +async fn tool_call_should_send_the_tool_choice() { + use aimux_core::options::LanguageModelV4ToolChoice; + let mut options = tool_options(); + options.tool_choice = Some(LanguageModelV4ToolChoice::Tool { + tool_name: "weather".to_string(), + }); assert_eq!( - warning_values(&result.warnings), - warning_values(&[Warning::Unsupported { - feature: "user message part type: file".to_string(), - details: None, - }]) + generate_body("deepseek-reasoner", &options).await["tool_choice"], + json!({ "type": "function", "function": { "name": "weather" } }) ); -} - -// ---- describe('tool calls') ------------------------------------------------------------------ - -fn tool_turn(reasoning: bool) -> Vec { - let mut assistant = vec![]; - if reasoning { - assistant.push(AssistantPart::Reasoning(ReasoningPart { - text: ("I think the tool will return the correct value.").into(), - provider_options: None, - })); - } - assistant.push(AssistantPart::ToolCall(ToolCallPart { - tool_call_id: ("quux").into(), - tool_name: ("thwomp").into(), - input: json!({ "foo": "bar123" }), - provider_executed: None, - provider_options: None, - })); - vec![ - LanguageModelMessage::Assistant { - content: assistant, - provider_options: None, - }, - LanguageModelMessage::Tool { - content: vec![ToolPart::ToolResult(ToolResultPart { - tool_call_id: ("quux").into(), - output: ToolResultOutput::Json { - value: json!({ "oof": "321rab" }), - provider_options: None, - }, - tool_name: "thwomp".into(), - provider_options: None, - })], - provider_options: None, - }, - ] -} - -fn wire_tool_call() -> Value { - json!([{ - "id": "quux", "type": "function", - "function": { "name": "thwomp", "arguments": "{\"foo\":\"bar123\"}" } - }]) -} - -#[tokio::test] -async fn convert_should_stringify_arguments_to_tool_calls() { - let result = generate_result("deepseek-chat", &options_for(tool_turn(false))).await; + options.tool_choice = Some(LanguageModelV4ToolChoice::Required); assert_eq!( - result.request.and_then(|r| r.body).unwrap()["messages"], - json!([ - // upstream sends the empty text as `content: ""`, not null. - { "role": "assistant", "content": "", "tool_calls": wire_tool_call() }, - { "role": "tool", "tool_call_id": "quux", "content": "{\"oof\":\"321rab\"}" } - ]) + generate_body("deepseek-reasoner", &options).await["tool_choice"], + "required" ); - assert!(result.warnings.is_empty()); } #[tokio::test] -async fn convert_should_handle_text_output_type_in_tool_results() { - let messages = wire_messages( - "deepseek-chat", - vec![ - LanguageModelMessage::Assistant { - content: vec![AssistantPart::ToolCall(ToolCallPart { - tool_call_id: ("call-1").into(), - tool_name: ("getWeather").into(), - input: json!({ "query": "weather" }), - provider_executed: None, - provider_options: None, - })], - provider_options: None, - }, - LanguageModelMessage::Tool { - content: vec![ToolPart::ToolResult(ToolResultPart { - tool_call_id: ("call-1").into(), - output: ToolResultOutput::Text { - value: "It is sunny today".into(), - provider_options: None, - }, - tool_name: "getWeather".into(), - provider_options: None, - })], - provider_options: None, - }, - ], - ) - .await; +async fn tool_call_should_warn_about_provider_defined_tools() { + use aimux_core::tool::ProviderTool; + let mut options = options(); + options.tools = Some(vec![Tool::Provider(ProviderTool { + id: "test.search".parse().unwrap(), + name: "search".to_string(), + args: serde_json::Map::new(), + supports_deferred_results: None, + })]); + let result = generate_result("deepseek-chat", &options).await; assert_eq!( - messages[1], - json!({ "role": "tool", "tool_call_id": "call-1", "content": "It is sunny today" }) + warning_values(&result.warnings), + warning_values(&[Warning::Unsupported { + feature: "provider-defined tool test.search".to_string(), + details: None, + }]) ); -} - -#[tokio::test] -async fn convert_should_support_reasoning_content_in_tool_calls() { - let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; - prompt.extend(tool_turn(true)); - let messages = wire_messages("deepseek-chat", prompt).await; - assert_eq!( - messages[1], - json!({ - "role": "assistant", "content": "", - "reasoning_content": "I think the tool will return the correct value.", - "tool_calls": wire_tool_call() - }) + assert!( + result.request.and_then(|r| r.body).unwrap()["tools"] + .as_array() + .unwrap() + .is_empty() ); } #[tokio::test] -async fn convert_should_filter_out_reasoning_content_from_turns_before_the_last_user_message() { - let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; - prompt.extend(tool_turn(true)); - prompt.push(LanguageModelMessage::user_text("Goodbye")); - let messages = wire_messages("deepseek-chat", prompt).await; +async fn tool_call_should_extract_tool_call_content() { + let server = json_server(tool_call_response()).await; + let result = chat(&server, "deepseek-reasoner") + .do_generate(&tool_options()) + .await + .unwrap(); + assert!( + matches!(&result.content[0], GenerateContent::Reasoning(ReasoningOutput { text, .. }) if text == "I should look up the weather.") + ); + assert!(matches!( + &result.content[1], + GenerateContent::ToolCall(RawToolCall { tool_call_id, tool_name, input, .. }) + if tool_call_id == "call_00_abc" && tool_name == "weather" + && input == "{\"location\":\"San Francisco\"}" + )); + assert_eq!(result.content.len(), 2); + assert_eq!(result.finish_reason.unified, FinishReasonUnified::ToolCalls); assert_eq!( - messages[1], - json!({ "role": "assistant", "content": "", "tool_calls": wire_tool_call() }) + result.provider_metadata.unwrap()["deepseek"]["toolCallTypes"], + json!(["function"]) ); } -// ---- describe('deepseek-v4 thinking mode') ------------------------------------------------------- +#[tokio::test] +async fn tool_call_should_generate_an_id_when_the_server_sends_none() { + let mut body = tool_call_response(); + body["choices"][0]["message"]["tool_calls"][0]["id"] = Value::Null; + let server = json_server(body).await; + let result = chat(&server, "deepseek-reasoner") + .do_generate(&tool_options()) + .await + .unwrap(); + assert!(matches!( + &result.content[1], + GenerateContent::ToolCall(RawToolCall { tool_call_id, .. }) if !tool_call_id.is_empty() + )); +} #[tokio::test] -async fn convert_should_preserve_reasoning_content_from_prior_turns_for_deepseek_v4() { - let mut prompt = vec![LanguageModelMessage::user_text("Hello")]; - prompt.extend(tool_turn(true)); - prompt.push(LanguageModelMessage::user_text("Goodbye")); - let messages = wire_messages("deepseek-v4-pro", prompt).await; - assert_eq!( - messages[1], - json!({ - "role": "assistant", "content": "", - "reasoning_content": "I think the tool will return the correct value.", - "tool_calls": wire_tool_call() - }) +async fn json_response_format_should_extract_text_content() { + let mut body = text_response(); + body["choices"][0]["message"]["content"] = json!("{\"elements\":[]}"); + let server = json_server(body).await; + let result = chat(&server, "deepseek-reasoner") + .do_generate(&json_options(None)) + .await + .unwrap(); + assert!( + matches!(&result.content[0], GenerateContent::Text { text, .. } if text == "{\"elements\":[]}") ); } +/// `deepSeekErrorSchema` as the chat model's failed-response handler +/// (deepseek@3.0.56 `deepseek-chat-api-types.ts:117-124`, +/// `deepseek-chat-language-model.ts:213-217`): `code` may be a number. #[tokio::test] -async fn convert_should_preserve_reasoning_content_from_prior_turns_for_the_deepseek_flash_alias() { - let messages = wire_messages( - "deepseek-flash", - vec![ - LanguageModelMessage::user_text("Hello"), - LanguageModelMessage::Assistant { - content: vec![ - AssistantPart::Reasoning(ReasoningPart { - text: ("Prior-turn reasoning.").into(), - provider_options: None, - }), - AssistantPart::Text(TextPart { - text: ("Hi there").into(), - provider_options: None, - }), - ], - provider_options: None, - }, - LanguageModelMessage::user_text("Again"), - ], - ) - .await; - assert_eq!( - messages[1], - json!({ "role": "assistant", "content": "Hi there", "reasoning_content": "Prior-turn reasoning." }) - ); +async fn a_failed_response_reads_deepseeks_error_schema() { + let server = MockServer::start().await; + Mock::given(method("POST")) + .and(path_regex(".*/chat/completions$")) + .respond_with(ResponseTemplate::new(402).set_body_json(json!({ "error": { + "message": "Insufficient Balance", "type": "unknown_error", "param": null, "code": 4020 + } }))) + .mount(&server) + .await; + let Err(AiMuxError::ApiCall(error)) = + chat(&server, "deepseek-chat").do_generate(&options()).await + else { + panic!("expected an API call error"); + }; + assert_eq!(error.message, "Insufficient Balance"); + assert_eq!(error.provider_code.as_deref(), Some("4020")); + assert_eq!(error.status_code, Some(402)); } #[tokio::test] -async fn convert_should_back_fill_empty_reasoning_content_for_deepseek_v4_assistant_messages_with_no_reasoning_part() - { - let messages = wire_messages( - "deepseek-v4-pro", - vec![ - LanguageModelMessage::user_text("Hello"), - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("Hi there").into(), - provider_options: None, - })], - provider_options: None, - }, - LanguageModelMessage::user_text("Again"), - ], - ) - .await; +async fn stream_text_should_stream_text() { + let (_, parts) = stream_parts("deepseek-chat", &options(), text_chunks()).await; + assert!(matches!(&parts[0], StreamPart::StreamStart { warnings } if warnings.is_empty())); + assert!(matches!( + &parts[1], + StreamPart::ResponseMetadata(ResponseMetadata { id, model_id, .. }) + if id.as_deref() == Some("c1") && model_id.as_deref() == Some("deepseek-chat") + )); + let deltas: Vec<&str> = parts + .iter() + .filter_map(|part| match part { + StreamPart::TextDelta { id, delta, .. } if id == "txt-0" => Some(delta.as_str()), + _ => None, + }) + .collect(); + assert_eq!(deltas, ["Hello", ", World!"]); + assert!( + parts + .iter() + .any(|p| matches!(p, StreamPart::TextStart { id, .. } if id == "txt-0")) + ); + assert!( + parts + .iter() + .any(|p| matches!(p, StreamPart::TextEnd { id, .. } if id == "txt-0")) + ); + match parts.last().unwrap() { + StreamPart::Finish { + finish_reason, + usage, + .. + } => { + assert_eq!(finish_reason.unified, FinishReasonUnified::Stop); + assert_eq!(finish_reason.raw.as_deref(), Some("stop")); + // usage with prompt-cache tokens + assert_eq!(usage.input_tokens.total, Some(13)); + assert_eq!(usage.input_tokens.cache_read, Some(5)); + assert_eq!(usage.input_tokens.no_cache, Some(8)); + assert_eq!(usage.output_tokens.total, Some(3)); + } + other => panic!("expected finish, got {other:?}"), + } assert_eq!( - messages[1], - json!({ "role": "assistant", "content": "Hi there", "reasoning_content": "" }) + finish_metadata(&parts), + json!({ "deepseek": { + "promptCacheHitTokens": 5, + "promptCacheMissTokens": 8, + "responseObject": "chat.completion.chunk", + "choiceIndex": 0, + "messageRole": "assistant", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } }) ); } -// ---- describe('assistant prefix completion') ----------------------------------------------------- - #[tokio::test] -async fn convert_should_reject_prefix_completion_on_a_non_assistant_message() { - let message = generate_error( - "deepseek-chat", - &options_for(vec![with_options( - LanguageModelMessage::user_text("Hello"), - json!({ "deepseek": { "prefix": true } }), - )]), - ) - .await; - assert!( - message.contains( - "DeepSeek assistant prefix completion requires `prefix: true` on an assistant message." +async fn stream_reasoning_should_stream_reasoning_before_text() { + let chunk = |delta: Value, finish: Value| { + data_event(&json!({ + "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-reasoner", + "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] + })) + }; + let chunks = vec![ + chunk( + json!({ "role": "assistant", "content": null, "reasoning_content": "Let me " }), + Value::Null, ), - "{message}" + chunk( + json!({ "content": null, "reasoning_content": "think." }), + Value::Null, + ), + chunk(json!({ "content": "Answer" }), Value::Null), + chunk(json!({ "content": "" }), json!("stop")), + ]; + let (_, parts) = stream_parts("deepseek-reasoner", &options(), chunks).await; + let order: Vec<&str> = parts + .iter() + .filter_map(|part| match part { + StreamPart::ReasoningStart { .. } => Some("reasoning-start"), + StreamPart::ReasoningDelta { .. } => Some("reasoning-delta"), + StreamPart::ReasoningEnd { .. } => Some("reasoning-end"), + StreamPart::TextStart { .. } => Some("text-start"), + StreamPart::TextDelta { .. } => Some("text-delta"), + StreamPart::TextEnd { .. } => Some("text-end"), + _ => None, + }) + .collect(); + assert_eq!( + order, + [ + "reasoning-start", + "reasoning-delta", + "reasoning-delta", + "text-start", + "reasoning-end", + "text-delta", + "text-end" + ] ); } #[tokio::test] -async fn convert_should_reject_prefix_completion_on_a_non_final_assistant_message() { - let server = json_server(text_response()).await; - let prompt = vec![ - with_options( - LanguageModelMessage::Assistant { - content: vec![AssistantPart::Text(TextPart { - text: ("The answer is").into(), - provider_options: None, - })], - provider_options: None, - }, - json!({ "deepseek": { "prefix": true } }), +async fn stream_tool_call_should_stream_tool_call() { + let chunk = |delta: Value, finish: Value| { + data_event(&json!({ + "id": "c", "object": "chat.completion.chunk", "created": 1, "model": "deepseek-reasoner", + "choices": [{ "index": 0, "delta": delta, "finish_reason": finish }] + })) + }; + let chunks = vec![ + chunk( + json!({ "role": "assistant", "reasoning_content": "Look it up." }), + Value::Null, + ), + chunk( + json!({ "tool_calls": [{ "index": 0, "id": "call_00_abc", "type": "function", + "function": { "name": "weather", "arguments": "" } }] }), + Value::Null, ), - LanguageModelMessage::user_text("Continue"), + chunk( + json!({ "tool_calls": [{ "index": 0, "function": { "arguments": "{\"location\":" } }] }), + Value::Null, + ), + chunk( + json!({ "tool_calls": [{ "index": 0, "function": { "arguments": "\"San Francisco\"}" } }] }), + Value::Null, + ), + chunk(json!({ "content": "" }), json!("tool_calls")), ]; - let error = beta_chat(&server, "deepseek-chat") - .do_generate(&options_for(prompt)) - .await - .unwrap_err(); + let (_, parts) = stream_parts("deepseek-reasoner", &tool_options(), chunks).await; + assert!(parts.iter().any(|p| matches!( + p, + StreamPart::ToolInputStart { id, tool_name, .. } if id == "call_00_abc" && tool_name == "weather" + ))); + let input: String = parts + .iter() + .filter_map(|p| match p { + StreamPart::ToolInputDelta { id, delta, .. } if id == "call_00_abc" => { + Some(delta.as_str()) + } + _ => None, + }) + .collect(); + assert_eq!(input, "{\"location\":\"San Francisco\"}"); + assert!(parts.iter().any(|p| matches!( + p, + StreamPart::ToolCall(RawToolCall { tool_call_id, tool_name, input, .. }) + if tool_call_id == "call_00_abc" && tool_name == "weather" + && input == "{\"location\":\"San Francisco\"}" + ))); assert!( - error.to_string().contains( - "DeepSeek assistant prefix completion requires the prefixed assistant message to be the final message." - ), - "{error}" + parts + .iter() + .any(|p| matches!(p, StreamPart::ReasoningEnd { .. })) + ); + match parts.last().unwrap() { + StreamPart::Finish { finish_reason, .. } => { + assert_eq!(finish_reason.unified, FinishReasonUnified::ToolCalls); + } + other => panic!("expected finish, got {other:?}"), + } + assert_eq!( + finish_metadata(&parts)["deepseek"]["toolCallTypes"], + json!(["function"]) ); } From bc715547256dade19194fc8afdcd3ab2f0c89840 Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 04:51:53 +0000 Subject: [PATCH 2/6] test(deepseek): port the remaining upstream internal-function tests 13 cases of convert-to-deepseek-chat-messages, deepseek-prepare-tools, convert-to-deepseek-usage and is-deepseek-v4-model in foo/tests.rs next to each source file. One prepare-tools case needs an unset strict-tools capability that only a directly built model has; it is ts-only in layout.toml. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- aimux-providers/src/deepseek/convert.rs | 3 + aimux-providers/src/deepseek/convert/tests.rs | 313 ++++++++++++++++++ aimux-providers/src/deepseek/is_v4_model.rs | 3 + .../src/deepseek/is_v4_model/tests.rs | 34 ++ aimux-providers/src/deepseek/prepare_tools.rs | 3 + .../src/deepseek/prepare_tools/tests.rs | 133 ++++++++ aimux-providers/src/deepseek/usage.rs | 3 + aimux-providers/src/deepseek/usage/tests.rs | 35 ++ 8 files changed, 527 insertions(+) create mode 100644 aimux-providers/src/deepseek/convert/tests.rs create mode 100644 aimux-providers/src/deepseek/is_v4_model/tests.rs create mode 100644 aimux-providers/src/deepseek/prepare_tools/tests.rs create mode 100644 aimux-providers/src/deepseek/usage/tests.rs diff --git a/aimux-providers/src/deepseek/convert.rs b/aimux-providers/src/deepseek/convert.rs index 353bc875a..33fb80c2e 100644 --- a/aimux-providers/src/deepseek/convert.rs +++ b/aimux-providers/src/deepseek/convert.rs @@ -377,3 +377,6 @@ fn convert_tool_result_content( } Ok(Value::Array(content)) } + +#[cfg(test)] +mod tests; diff --git a/aimux-providers/src/deepseek/convert/tests.rs b/aimux-providers/src/deepseek/convert/tests.rs new file mode 100644 index 000000000..2469bfb40 --- /dev/null +++ b/aimux-providers/src/deepseek/convert/tests.rs @@ -0,0 +1,313 @@ +//! Port of `src/chat/convert-to-deepseek-chat-messages.test.ts` +//! (`@ai-sdk/deepseek` 3.0.56): the six cases of "message names", "tool calls" +//! and "assistant prefix completion" that the other cases of this file do not +//! already cover. +//! +//! Upstream writes prompts as object literals. Here they are JSON, read through +//! the request path's own serde shapes. The one `Uint8Array` payload is built as +//! `FileBytes::Binary` directly: JSON can only carry base64, which parses to the +//! other variant. + +use super::*; +use serde_json::{Value, json}; + +/// A prompt, read as the request path reads it. +fn prompt_from(value: Value) -> LanguageModelPrompt { + serde_json::from_value(value).expect("the prompt literal is valid") +} + +/// A tool-result content part, read as the request path reads it. +fn tool_result_content(value: Value) -> ToolResultContent { + serde_json::from_value(value).expect("the tool result content literal is valid") +} + +mod convert_to_deep_seek_chat_messages { + use super::*; + + mod message_names { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › message names › should serialize names for system, user, and assistant messages + #[test] + fn should_serialize_names_for_system_user_and_assistant_messages() { + let prompt = prompt_from(json!([ + { + "role": "system", + "content": "You are a helpful assistant.", + "providerOptions": { "deepseek": { "name": "guide" } }, + }, + { + "role": "user", + "content": [ + { "type": "text", "text": "Describe this image." }, + { + "type": "file", + "data": { "type": "url", "url": "https://example.com/image.png" }, + "mediaType": "image/png", + }, + ], + "providerOptions": { "deepseek": { "name": "alice" } }, + }, + { + "role": "assistant", + "content": [ + { "type": "text", "text": "I will inspect it." }, + { + "type": "tool-call", + "toolCallId": "call-1", + "toolName": "inspectImage", + "input": { "detail": "high" }, + }, + ], + "providerOptions": { "deepseek": { "name": "vision_assistant" } }, + }, + ])); + + let result = convert_to_deepseek_chat_messages( + &prompt, + None, + "deepseek-v4-flash-vision-exp", + "deepseek", + false, + ) + .expect("the prompt converts"); + + assert_eq!( + Value::Array(result.messages), + json!([ + { + "role": "system", + "content": "You are a helpful assistant.", + "name": "guide", + }, + { + "role": "user", + "content": [ + { "type": "text", "text": "Describe this image." }, + { + "type": "image_url", + "image_url": { "url": "https://example.com/image.png" }, + }, + ], + "name": "alice", + }, + { + "role": "assistant", + "content": "I will inspect it.", + "name": "vision_assistant", + // V4 requires the field on every assistant turn. + "reasoning_content": "", + "tool_calls": [ + { + "id": "call-1", + "type": "function", + "function": { + "name": "inspectImage", + "arguments": "{\"detail\":\"high\"}", + }, + }, + ], + }, + ]) + ); + assert!(result.warnings.is_empty()); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › message names › should serialize a name from a custom provider options namespace + #[test] + fn should_serialize_a_name_from_a_custom_provider_options_namespace() { + let prompt = prompt_from(json!([{ + "role": "user", + "content": [{ "type": "text", "text": "Hello" }], + "providerOptions": { "azure": { "name": "alice" } }, + }])); + + let result = + convert_to_deepseek_chat_messages(&prompt, None, "deepseek-chat", "azure", false) + .expect("the prompt converts"); + + assert_eq!( + Value::Array(result.messages), + json!([{ "role": "user", "content": "Hello", "name": "alice" }]) + ); + } + } + + mod tool_calls { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should convert inline, URL, and referenced images in tool results to content parts + #[test] + fn should_convert_inline_url_and_referenced_images_in_tool_results_to_content_parts() { + let inline_png = ToolResultContent::File(FilePart { + data: FileData::Data { + data: FileBytes::Binary(vec![0, 1, 2, 3]), + }, + media_type: "image/png".to_string(), + filename: None, + provider_options: None, + }); + let prompt = vec![LanguageModelMessage::Tool { + content: vec![ToolPart::ToolResult(ToolResultPart { + tool_call_id: "call-1".to_string(), + tool_name: "inspectImage".to_string(), + output: ToolResultOutput::Content { + value: vec![ + tool_result_content( + json!({ "type": "text", "text": "Screenshot captured." }), + ), + inline_png, + tool_result_content(json!({ + "type": "file", + "data": { "type": "url", "url": "https://example.com/image.webp" }, + "mediaType": "image/webp", + "providerOptions": { "deepseek": { "imageDetail": "low" } }, + })), + tool_result_content(json!({ + "type": "file", + "data": { + "type": "reference", + "reference": { + "deepseek": "file-api-deepseek", + "openai": "file-openai", + }, + }, + "mediaType": "image/png", + })), + ], + }, + provider_options: None, + })], + provider_options: None, + }]; + + let result = convert_to_deepseek_chat_messages( + &prompt, + None, + "deepseek-flash", + "deepseek", + false, + ) + .expect("the prompt converts"); + + assert_eq!( + Value::Array(result.messages), + json!([{ + "role": "tool", + "tool_call_id": "call-1", + "content": [ + { "type": "text", "text": "Screenshot captured." }, + { + "type": "image_url", + "image_url": { "url": "data:image/png;base64,AAECAw==" }, + }, + { + "type": "image_url", + "image_url": { + "url": "https://example.com/image.webp", + "detail": "low", + }, + }, + { "type": "file", "file_id": "file-api-deepseek" }, + ], + }]) + ); + assert!(result.warnings.is_empty()); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › tool calls › should keep tool result content without images JSON-stringified + #[test] + fn should_keep_tool_result_content_without_images_json_stringified() { + let prompt = prompt_from(json!([{ + "role": "tool", + "content": [{ + "type": "tool-result", + "toolCallId": "call-1", + "toolName": "getWeather", + "output": { + "type": "content", + "value": [{ "type": "text", "text": "It is sunny today" }], + }, + }], + }])); + + let result = convert_to_deepseek_chat_messages( + &prompt, + None, + "deepseek-chat", + "deepseek", + false, + ) + .expect("the prompt converts"); + + assert_eq!( + Value::Array(result.messages), + json!([{ + "role": "tool", + "tool_call_id": "call-1", + "content": "[{\"type\":\"text\",\"text\":\"It is sunny today\"}]", + }]) + ); + } + } + + mod assistant_prefix_completion { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › assistant prefix completion › should serialize prefix true on the final assistant message + #[test] + fn should_serialize_prefix_true_on_the_final_assistant_message() { + let prompt = prompt_from(json!([ + { + "role": "user", + "content": [{ "type": "text", "text": "Complete this sentence." }], + }, + { + "role": "assistant", + "content": [{ "type": "text", "text": "The answer is" }], + "providerOptions": { "deepseek": { "prefix": true } }, + }, + ])); + + let result = + convert_to_deepseek_chat_messages(&prompt, None, "deepseek-chat", "deepseek", true) + .expect("the prompt converts"); + + // Upstream's `reasoning_content` and `tool_calls` are undefined, so + // they are absent from the JSON. + assert_eq!( + Value::Array(result.messages), + json!([ + { "role": "user", "content": "Complete this sentence." }, + { "role": "assistant", "content": "The answer is", "prefix": true }, + ]) + ); + assert!(result.warnings.is_empty()); + } + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-chat-messages.test.ts › convertToDeepSeekChatMessages › assistant prefix completion › should reject prefix completion without a beta base URL capability + #[test] + fn should_reject_prefix_completion_without_a_beta_base_url_capability() { + let prompt = prompt_from(json!([{ + "role": "assistant", + "content": [{ "type": "text", "text": "The answer is" }], + "providerOptions": { "deepseek": { "prefix": true } }, + }])); + + let error = convert_to_deepseek_chat_messages( + &prompt, + None, + "deepseek-chat", + "deepseek", + false, + ) + .err() + .expect("prefix completion is rejected without the beta endpoint"); + + assert!( + matches!(error, AiMuxError::UnsupportedFunctionality(_)), + "{error}" + ); + } + } +} diff --git a/aimux-providers/src/deepseek/is_v4_model.rs b/aimux-providers/src/deepseek/is_v4_model.rs index f7dbf8a26..deb292463 100644 --- a/aimux-providers/src/deepseek/is_v4_model.rs +++ b/aimux-providers/src/deepseek/is_v4_model.rs @@ -8,3 +8,6 @@ pub(crate) fn is_deepseek_v4_model(model_id: &str) -> bool { || model_id.starts_with("deepseek-flash") || model_id.starts_with("deepseek-pro") } + +#[cfg(test)] +mod tests; diff --git a/aimux-providers/src/deepseek/is_v4_model/tests.rs b/aimux-providers/src/deepseek/is_v4_model/tests.rs new file mode 100644 index 000000000..e75da4622 --- /dev/null +++ b/aimux-providers/src/deepseek/is_v4_model/tests.rs @@ -0,0 +1,34 @@ +//! Port of `src/chat/is-deepseek-v4-model.test.ts` (`@ai-sdk/deepseek` 3.0.56). + +use super::*; + +mod is_deep_seek_v4_model { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/is-deepseek-v4-model.test.ts › isDeepSeekV4Model › should treat %s as V4 + #[test] + fn should_treat_as_v4() { + for model_id in [ + "deepseek-v4-pro", + "deepseek-v4-pro-0813", + "deepseek-v4-flash", + "deepseek-v4-flash-0731", + "deepseek-v4-flash-vision-exp", + "deepseek-flash", + "deepseek-pro", + ] { + assert!(is_deepseek_v4_model(model_id), "{model_id} should be V4"); + } + } + + /// upstream: @ai-sdk/deepseek src/chat/is-deepseek-v4-model.test.ts › isDeepSeekV4Model › should not treat legacy %s as V4 + #[test] + fn should_not_treat_legacy_as_v4() { + for model_id in ["deepseek-chat", "deepseek-reasoner"] { + assert!( + !is_deepseek_v4_model(model_id), + "{model_id} should not be V4" + ); + } + } +} diff --git a/aimux-providers/src/deepseek/prepare_tools.rs b/aimux-providers/src/deepseek/prepare_tools.rs index 9ac4cc386..3319b3f7d 100644 --- a/aimux-providers/src/deepseek/prepare_tools.rs +++ b/aimux-providers/src/deepseek/prepare_tools.rs @@ -94,3 +94,6 @@ pub(crate) fn prepare_tools( tool_warnings, }) } + +#[cfg(test)] +mod tests; diff --git a/aimux-providers/src/deepseek/prepare_tools/tests.rs b/aimux-providers/src/deepseek/prepare_tools/tests.rs new file mode 100644 index 000000000..e00760cb5 --- /dev/null +++ b/aimux-providers/src/deepseek/prepare_tools/tests.rs @@ -0,0 +1,133 @@ +//! Port of `src/chat/deepseek-prepare-tools.test.ts` (`@ai-sdk/deepseek` 3.0.56). +//! +//! Upstream's `supportsStrictToolCalls` is three-state: `undefined` skips both +//! strict checks. The chat model always passes a boolean derived from the base +//! URL (`baseURL.endsWith('/beta')`), so there is no `undefined` to pass. The +//! upstream case "should pass through strict mode for multiple tools with +//! different strict settings" depends on that state and is not ported (no +//! boolean reproduces it; see the report). + +use super::*; +use serde_json::{Value, json}; + +/// The tools of a request, read as the request path reads them. +fn tools_from(value: Value) -> Vec { + serde_json::from_value(value).expect("the tools literal is valid") +} + +mod prepare_tools { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should pass through strict mode when strict is true + #[test] + fn should_pass_through_strict_mode_when_strict_is_true() { + let tools = tools_from(json!([{ + "type": "function", + "name": "testFunction", + "description": "A test function", + "inputSchema": { "type": "object", "properties": {} }, + "strict": true, + }])); + + // Upstream passes no endpoint flag. `true` gives the same result here: + // an all-strict request raises neither beta-only rejection. + let result = + prepare_tools(Some(&tools), None, true).expect("all-strict tools are accepted"); + + assert_eq!( + result.tools, + Some(vec![json!({ + "type": "function", + "function": { + "name": "testFunction", + "description": "A test function", + "parameters": { "type": "object", "properties": {} }, + "strict": true, + }, + })]) + ); + assert_eq!(result.tool_choice, None); + assert!(result.tool_warnings.is_empty()); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should reject strict tools when the endpoint does not support them + #[test] + fn should_reject_strict_tools_when_the_endpoint_does_not_support_them() { + let tools = tools_from(json!([{ + "type": "function", + "name": "strictTool", + "inputSchema": { "type": "object", "properties": {} }, + "strict": true, + }])); + + let error = prepare_tools(Some(&tools), None, false) + .err() + .expect("strict tools are rejected without the beta endpoint"); + + assert!( + error + .to_string() + .contains("DeepSeek strict tool calls require a beta base URL ending in `/beta`."), + "{error}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should reject mixed strict and non-strict tools on the beta endpoint + #[test] + fn should_reject_mixed_strict_and_non_strict_tools_on_the_beta_endpoint() { + let tools = tools_from(json!([ + { + "type": "function", + "name": "strictTool", + "inputSchema": { "type": "object", "properties": {} }, + "strict": true, + }, + { + "type": "function", + "name": "nonStrictTool", + "inputSchema": { "type": "object", "properties": {} }, + }, + ])); + + let error = prepare_tools(Some(&tools), None, true) + .err() + .expect("mixed strictness is rejected on the beta endpoint"); + + assert!( + error.to_string().contains( + "DeepSeek strict mode requires every function tool in the request to set `strict: true`." + ), + "{error}" + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-prepare-tools.test.ts › prepareTools › should accept all-strict tools on the beta endpoint + #[test] + fn should_accept_all_strict_tools_on_the_beta_endpoint() { + let tools = tools_from(json!([ + { + "type": "function", + "name": "firstStrictTool", + "inputSchema": { "type": "object", "properties": {} }, + "strict": true, + }, + { + "type": "function", + "name": "secondStrictTool", + "inputSchema": { "type": "object", "properties": {} }, + "strict": true, + }, + ])); + + let result = prepare_tools(Some(&tools), None, true) + .expect("all-strict tools are accepted on the beta endpoint"); + + let prepared = result.tools.expect("the tools are present"); + assert!( + prepared + .iter() + .all(|tool| tool["function"]["strict"] == json!(true)), + "every tool keeps strict: true" + ); + } +} diff --git a/aimux-providers/src/deepseek/usage.rs b/aimux-providers/src/deepseek/usage.rs index 6c9ca7ae5..380d0ecdd 100644 --- a/aimux-providers/src/deepseek/usage.rs +++ b/aimux-providers/src/deepseek/usage.rs @@ -37,3 +37,6 @@ pub(crate) fn convert_deepseek_usage(usage: Option<&Value>) -> Usage { raw: usage.as_object().cloned(), } } + +#[cfg(test)] +mod tests; diff --git a/aimux-providers/src/deepseek/usage/tests.rs b/aimux-providers/src/deepseek/usage/tests.rs new file mode 100644 index 000000000..decec783b --- /dev/null +++ b/aimux-providers/src/deepseek/usage/tests.rs @@ -0,0 +1,35 @@ +//! Port of `src/chat/convert-to-deepseek-usage.test.ts` (`@ai-sdk/deepseek` 3.0.56). + +use super::*; +use serde_json::{Value, json}; + +mod convert_deep_seek_usage { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/convert-to-deepseek-usage.test.ts › convertDeepSeekUsage › clamps text tokens at 0 when reasoning exceeds completion + #[test] + fn clamps_text_tokens_at_0_when_reasoning_exceeds_completion() { + let usage: Value = json!({ + "prompt_tokens": 951, + "completion_tokens": 6000, + "completion_tokens_details": { "reasoning_tokens": 6001 }, + }); + + let converted = + serde_json::to_value(convert_deepseek_usage(Some(&usage))).expect("usage serializes"); + + // `cacheWrite` is undefined upstream, so it is absent from the JSON. + assert_eq!( + converted, + json!({ + "inputTokens": { + "total": 951, + "noCache": 951, + "cacheRead": 0, + }, + "outputTokens": { "total": 6000, "text": 0, "reasoning": 6001 }, + "raw": usage, + }) + ); + } +} From 31834ae9618278434aeb1f4658335edc7b13bbb7 Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 04:51:53 +0000 Subject: [PATCH 3/6] test(deepseek): port the remaining upstream chat and raw-usage tests 10 cases under tests/it/deepseek/, built as the single tests/it/main.rs program, with the upstream fixtures and snapshots beside them. The three structured-outputs cases need a model the provider never builds and are ts-only. 'continues validating known usage field %s' fails: aimux reads a string usage count as 0. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- .../tests/it/deepseek/chat_language_model.rs | 316 +++ .../it/deepseek/fixtures/deepseek-json.json | 32 + .../fixtures/deepseek-reasoning.chunks.txt | 220 ++ .../deepseek/fixtures/deepseek-reasoning.json | 32 + .../fixtures/deepseek-text.chunks.txt | 402 ++++ .../it/deepseek/fixtures/deepseek-text.json | 28 + .../fixtures/deepseek-tool-call.chunks.txt | 52 + .../deepseek/fixtures/deepseek-tool-call.json | 43 + aimux-providers/tests/it/deepseek/mod.rs | 2 + .../tests/it/deepseek/raw_usage.rs | 213 ++ .../do_generate_json_response_format.json | 135 ++ .../snapshots/do_generate_reasoning.json | 104 + .../deepseek/snapshots/do_generate_text.json | 93 + .../snapshots/do_generate_tool_call.json | 144 ++ .../snapshots/do_stream_reasoning.json | 1160 ++++++++++ .../it/deepseek/snapshots/do_stream_text.json | 2059 +++++++++++++++++ .../snapshots/do_stream_tool_call.json | 325 +++ aimux-providers/tests/it/main.rs | 7 + 18 files changed, 5367 insertions(+) create mode 100644 aimux-providers/tests/it/deepseek/chat_language_model.rs create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt create mode 100644 aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json create mode 100644 aimux-providers/tests/it/deepseek/mod.rs create mode 100644 aimux-providers/tests/it/deepseek/raw_usage.rs create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json create mode 100644 aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json create mode 100644 aimux-providers/tests/it/main.rs diff --git a/aimux-providers/tests/it/deepseek/chat_language_model.rs b/aimux-providers/tests/it/deepseek/chat_language_model.rs new file mode 100644 index 000000000..477008b92 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/chat_language_model.rs @@ -0,0 +1,316 @@ +//! Ports of `src/chat/deepseek-chat-language-model.test.ts` from `@ai-sdk/deepseek@3.0.56`: +//! the snapshot cases of `doGenerate` (`text`, `reasoning`, `tool call`, and +//! `tool call › json response format`) and of `doStream` (`text`, `reasoning`, +//! `tool call`). +//! +//! Harness differences from the TypeScript (expectations are unchanged): +//! - `toMatchSnapshot()` values are the `.snap` entries in `snapshots/`, as JSON: +//! `undefined` properties are absent (as `JSON.stringify` drops them) and the +//! `Date` timestamps are ISO strings. The result, or each stream part, is +//! serialized and compared as a `serde_json::Value`. +//! - The `prepare*FixtureResponse` helpers are the `fixtures/` files, served by +//! [`MockFetch`]. The TypeScript test server adds `content-length` to JSON +//! responses and `cache-control` / `connection` to event streams; the mock does +//! the same. +//! - A `stream-chunks` response is one `data:` frame per line of the +//! `.chunks.txt` fixture, then `data: [DONE]`. Blank lines carry no frame and +//! are skipped. +//! +//! Not ported: `json response format with structured outputs` (three cases: +//! `should send json_schema response format and skip schema injection`, +//! `should honor strictJsonSchema provider option` and `should fall back to +//! json_object without a schema`). The upstream builds that model directly with +//! `supportsStructuredOutputs: true`. The public API has no such setting: +//! `create_deepseek(..).chat(..)` only turns on the beta-endpoint flags, and the +//! chat model never sends a `json_schema` response format. + +use std::sync::Arc; + +use aimux_core::language_model::LanguageModel; +use aimux_core::language_model_message::LanguageModelMessage; +use aimux_core::options::{CallOptions, ResponseFormat}; +use aimux_core::result::StreamResult; +use aimux_core::tool::{FunctionTool, Tool}; +use aimux_providers::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; +use futures::StreamExt; +use serde_json::{Value, json}; + +use crate::mock_fetch::{Canned, MockFetch}; + +const TEXT_FIXTURE: &str = include_str!("fixtures/deepseek-text.json"); +const REASONING_FIXTURE: &str = include_str!("fixtures/deepseek-reasoning.json"); +const JSON_FIXTURE: &str = include_str!("fixtures/deepseek-json.json"); +const TOOL_CALL_FIXTURE: &str = include_str!("fixtures/deepseek-tool-call.json"); +const TEXT_CHUNKS: &str = include_str!("fixtures/deepseek-text.chunks.txt"); +const REASONING_CHUNKS: &str = include_str!("fixtures/deepseek-reasoning.chunks.txt"); +const TOOL_CALL_CHUNKS: &str = include_str!("fixtures/deepseek-tool-call.chunks.txt"); + +const SNAPSHOT_DO_GENERATE_TEXT: &str = include_str!("snapshots/do_generate_text.json"); +const SNAPSHOT_DO_GENERATE_REASONING: &str = include_str!("snapshots/do_generate_reasoning.json"); +const SNAPSHOT_DO_GENERATE_JSON_RESPONSE_FORMAT: &str = + include_str!("snapshots/do_generate_json_response_format.json"); +const SNAPSHOT_DO_GENERATE_TOOL_CALL: &str = include_str!("snapshots/do_generate_tool_call.json"); +const SNAPSHOT_DO_STREAM_TEXT: &str = include_str!("snapshots/do_stream_text.json"); +const SNAPSHOT_DO_STREAM_REASONING: &str = include_str!("snapshots/do_stream_reasoning.json"); +const SNAPSHOT_DO_STREAM_TOOL_CALL: &str = include_str!("snapshots/do_stream_tool_call.json"); + +// ── helpers ───────────────────────────────────────────────────────────────── + +fn json_value(source: &str) -> Value { + serde_json::from_str(source).expect("fixture or snapshot is JSON") +} + +/// `createDeepSeek({ apiKey })` with a mock transport. +fn provider(fetch: &Arc) -> DeepSeekProvider { + create_deepseek(DeepSeekProviderSettings { + api_key: Some("test-api-key".into()), + fetch: Some(fetch.transport()), + ..Default::default() + }) + .unwrap() +} + +/// `prompt: TEST_PROMPT`. +fn options() -> CallOptions { + CallOptions::new(vec![LanguageModelMessage::user_text("Hello")]) +} + +fn weather_tool() -> Tool { + Tool::Function(FunctionTool::new( + "weather", + json!({ + "type": "object", + "properties": { "location": { "type": "string" } }, + "required": ["location"], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }), + )) +} + +/// The `tools: [weather]` and `providerOptions.deepseek.thinking: enabled` of +/// the upstream tool-call cases. +fn tool_options() -> CallOptions { + let mut call = options(); + call.tools = Some(vec![weather_tool()]); + call.provider_options = Some( + serde_json::from_value(json!({ "deepseek": { "thinking": { "type": "enabled" } } })) + .unwrap(), + ); + call +} + +/// `responseFormat: { type: 'json' }` (no schema) on top of [`tool_options`]. +fn json_options() -> CallOptions { + let mut call = tool_options(); + call.response_format = Some(ResponseFormat::Json { + schema: None, + name: None, + description: None, + }); + call +} + +/// A non-streaming response with the headers the TypeScript test server sends. +fn json_response(body: &Value) -> Canned { + let bytes = serde_json::to_vec(body).expect("serializable"); + Canned { + status: 200, + headers: vec![ + ("content-type".to_string(), "application/json".to_string()), + ("content-length".to_string(), bytes.len().to_string()), + ], + body: bytes, + } +} + +/// A single-response mock for a JSON body. +fn json_fetch(body: &Value) -> Arc { + MockFetch::new(vec![json_response(body)]) +} + +/// A single event-stream mock: one `data:` frame per payload, then `[DONE]`. +fn stream_fetch(payloads: &[String]) -> Arc { + let mut body: String = payloads + .iter() + .map(|payload| format!("data: {payload}\n\n")) + .collect(); + body.push_str("data: [DONE]\n\n"); + MockFetch::new(vec![Canned { + status: 200, + headers: vec![ + ("content-type".to_string(), "text/event-stream".to_string()), + ("cache-control".to_string(), "no-cache".to_string()), + ("connection".to_string(), "keep-alive".to_string()), + ], + body: body.into_bytes(), + }]) +} + +/// A `stream-chunks` mock from a `.chunks.txt` fixture: one frame per non-blank line. +fn chunks_fetch(source: &str) -> Arc { + let payloads: Vec = source + .lines() + .filter(|line| !line.trim().is_empty()) + .map(str::to_string) + .collect(); + stream_fetch(&payloads) +} + +/// All parts of a stream, each serialized as the AI SDK writes it. +async fn stream_parts(result: StreamResult) -> Value { + Value::Array( + result + .stream + .map(|part| serde_json::to_value(part.expect("stream part")).unwrap()) + .collect() + .await, + ) +} + +// ── doGenerate and doStream ──────────────────────────────────────────────── + +mod deep_seek_chat_language_model { + use super::*; + + mod do_generate { + use super::*; + + mod text { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › text › should extract text content + #[tokio::test] + async fn should_extract_text_content() { + let fetch = json_fetch(&json_value(TEXT_FIXTURE)); + let result = provider(&fetch) + .chat("deepseek-chat") + .do_generate(&options()) + .await + .unwrap(); + assert_eq!( + serde_json::to_value(&result).unwrap(), + json_value(SNAPSHOT_DO_GENERATE_TEXT) + ); + } + } + + mod reasoning { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › reasoning › should extract text content + #[tokio::test] + async fn should_extract_text_content() { + let fetch = json_fetch(&json_value(REASONING_FIXTURE)); + let result = provider(&fetch) + .chat("deepseek-chat") + .do_generate(&options()) + .await + .unwrap(); + assert_eq!( + serde_json::to_value(&result).unwrap(), + json_value(SNAPSHOT_DO_GENERATE_REASONING) + ); + } + } + + mod tool_call { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › tool call › should extract tool call content + #[tokio::test] + async fn should_extract_tool_call_content() { + let fetch = json_fetch(&json_value(TOOL_CALL_FIXTURE)); + let result = provider(&fetch) + .chat("deepseek-reasoner") + .do_generate(&tool_options()) + .await + .unwrap(); + assert_eq!( + serde_json::to_value(&result).unwrap(), + json_value(SNAPSHOT_DO_GENERATE_TOOL_CALL) + ); + } + + mod json_response_format { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doGenerate › tool call › json response format › should extract text content + #[tokio::test] + async fn should_extract_text_content() { + let fetch = json_fetch(&json_value(JSON_FIXTURE)); + let result = provider(&fetch) + .chat("deepseek-reasoner") + .do_generate(&json_options()) + .await + .unwrap(); + assert_eq!( + serde_json::to_value(&result).unwrap(), + json_value(SNAPSHOT_DO_GENERATE_JSON_RESPONSE_FORMAT) + ); + } + } + } + } + + mod do_stream { + use super::*; + + mod text { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › text › should stream text + #[tokio::test] + async fn should_stream_text() { + let fetch = chunks_fetch(TEXT_CHUNKS); + let result = provider(&fetch) + .chat("deepseek-chat") + .do_stream(&options()) + .await + .unwrap(); + assert_eq!( + stream_parts(result).await, + json_value(SNAPSHOT_DO_STREAM_TEXT) + ); + } + } + + mod reasoning { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › reasoning › should stream reasoning + #[tokio::test] + async fn should_stream_reasoning() { + let fetch = chunks_fetch(REASONING_CHUNKS); + let result = provider(&fetch) + .chat("deepseek-reasoning") + .do_stream(&options()) + .await + .unwrap(); + assert_eq!( + stream_parts(result).await, + json_value(SNAPSHOT_DO_STREAM_REASONING) + ); + } + } + + mod tool_call { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-chat-language-model.test.ts › DeepSeekChatLanguageModel › doStream › tool call › should stream tool call + #[tokio::test] + async fn should_stream_tool_call() { + let fetch = chunks_fetch(TOOL_CALL_CHUNKS); + let result = provider(&fetch) + .chat("deepseek-reasoner") + .do_stream(&tool_options()) + .await + .unwrap(); + assert_eq!( + stream_parts(result).await, + json_value(SNAPSHOT_DO_STREAM_TOOL_CALL) + ); + } + } + } +} diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json b/aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json new file mode 100644 index 000000000..e756ef37e --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json @@ -0,0 +1,32 @@ +{ + "id": "f03bc170-b375-4561-9685-35182c8152c5", + "object": "chat.completion", + "created": 1764681341, + "model": "deepseek-reasoner", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "{\n \"location\": \"San Francisco\",\n \"condition\": \"cloudy\",\n \"temperature\": 7\n}", + "reasoning_content": "I have the result from the weather tool. It returned a JSON object with location, condition, and temperature. I need to reply with JSON object ONLY. The assistant's response should be the JSON output. So I should output exactly that JSON? Or perhaps wrap it in some way? The instruction says \"Reply with JSON object ONLY.\" Probably I should output the result as is, or maybe a structured response. But the tool result is already JSON. I think I should output that JSON directly. However, I need to ensure it's a valid response. Let me output the JSON object." + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 495, + "completion_tokens": 144, + "total_tokens": 639, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "completion_tokens_details": { + "reasoning_tokens": 118 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 175 + }, + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" +} diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt b/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt new file mode 100644 index 000000000..59d80eba2 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt @@ -0,0 +1,220 @@ +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"role":"assistant","content":null,"reasoning_content":""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"We"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" need"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" count"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" number"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" letter"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" word"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"st"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"raw"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"berry"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" word"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" spelled"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-t"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-w"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-e"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-y"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" Let"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" list"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" letters"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" count"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"Position"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"1"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"2"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" t"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"first"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":")\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"4"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"5"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" w"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"6"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" b"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"7"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" e"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"8"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"second"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":")\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"9"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"third"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":")\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"10"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" y"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"So"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" there"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" three"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" But"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" wait"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" let"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" double"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"-check"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"st"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"raw"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"berry"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" indeed"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" has"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" three"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" one"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" after"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"t"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" then"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" two"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" consecutive"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" before"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"y"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" So"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" answer"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" question"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" straightforward"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" However"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" sometimes"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" people"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" might"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" misc"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"ount"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" Let"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" ensure"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" word"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" has"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"10"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" letters"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"r"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" at"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" positions"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"8"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"9"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" So"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" yes"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"Thus"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" answer"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"The","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" word","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" \"","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"st","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"raw","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"berry","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"\"","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" contains","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" three","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" \"","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"r","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"\"s","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".","reasoning_content":null},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cac7192e-e619-40c6-96b0-ed4276bc03ac","object":"chat.completion.chunk","created":1764661832,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"","reasoning_content":null},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":18,"completion_tokens":219,"total_tokens":237,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":205},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":18}} \ No newline at end of file diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json b/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json new file mode 100644 index 000000000..3c78da546 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json @@ -0,0 +1,32 @@ +{ + "id": "945bb10c-9bf3-47ff-a2a2-43bbe9705c72", + "object": "chat.completion", + "created": 1764660903, + "model": "deepseek-reasoner", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The word \"strawberry\" contains three instances of the letter \"r\": one after the \"t\" and two before the \"y\".", + "reasoning_content": "We are asked: \"How many 'r's are in the word 'strawberry'?\" We need to count the occurrences of the letter 'r' in the word \"strawberry\". Let's write the word: s-t-r-a-w-b-e-r-r-y. Let's count: positions: 1: s, 2: t, 3: r, 4: a, 5: w, 6: b, 7: e, 8: r, 9: r, 10: y. So there are three 'r's? Wait: at position 3: r, position 8: r, position 9: r. That's three. But sometimes people might think it's two because of pronunciation? But strictly counting letters, it's three. However, let's double-check spelling: \"strawberry\" is spelled S-T-R-A-W-B-E-R-R-Y. Yes, it has two 'r's at the end? Actually, after 'b' comes 'e', then 'r', then 'r', then 'y'. So indeed three 'r's total. But wait, the initial 'r' is the first one? The word starts with 's', then 't', then 'r'. So that's one, then later two consecutive 'r's. So total three. So answer is 3.\n\nBut the question is phrased: \"How many 'r's are in the word 'strawberry'?\" So answer is 3." + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 18, + "completion_tokens": 345, + "total_tokens": 363, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 315 + }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 18 + }, + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" +} diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt b/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt new file mode 100644 index 000000000..9e4af4a2c --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt @@ -0,0 +1,402 @@ +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"##"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"H"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"olid"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ay"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Name"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" St"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"arl"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ight"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Rem"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"embrance"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"Date"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Saturday"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" nearest"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" new"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" moon"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" October"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" when"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" night"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" sky"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" at"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" its"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" darkest"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" stars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" vivid"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"Origin"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" &"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Meaning"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"St"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"arl"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ight"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Rem"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"embrance"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" emerged"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" from"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" modern"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" collective"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" yearning"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" honor"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" personal"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" loss"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" while"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" celebrating"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" enduring"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" connections"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" transcend"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" it"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" It"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" tied"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" any"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" one"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" religion"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" culture"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" but"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" universal"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" human"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" experience"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" core"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" philosophy"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" people"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" we"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" love"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ideas"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" inspired"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" us"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" even"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" versions"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ourselves"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" gone"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" vanished"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" but"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" have"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" become"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" part"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" universe"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"'s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" fabric"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"—"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"like"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" st"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ard"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ust"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" holiday"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" asks"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" participants"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" look"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" at"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" vast"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" shared"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" sky"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" find"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" comfort"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" both"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" darkness"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" loss"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":")"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" enduring"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" points"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" light"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" memory"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":").\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"---\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"###"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"T"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"rad"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"itions"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" &"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Rit"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"uals"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":**\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Dim"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ming"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Lights"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"As"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" night"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" falls"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" households"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" communities"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" turn"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" off"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" non"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"essential"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" electric"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" lights"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Streets"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" lit"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" only"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" subtle"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" blue"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"-ton"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ed"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" lantern"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"s"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"symbol"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"izing"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" night"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" sky"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":")."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" This"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" collective"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" act"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" reduces"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" light"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" pollution"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" literally"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" metaphor"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ically"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" making"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" space"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" stars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"—"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" reflection"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"2"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Craft"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ing"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Constellation"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" J"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"In"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" days"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" leading"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" up"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" holiday"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" people"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" create"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"Const"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ellation"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" J"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"ars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".\""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" They"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" take"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" clear"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" jar"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" using"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" small"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" drill"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" adhesive"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" create"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" pattern"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" holes"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" its"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" lid"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" representing"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" stars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" specific"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" constellation"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" On"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" night"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" holiday"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" small"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" battery"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"-operated"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" tea"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" light"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" placed"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" inside"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Each"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" jar"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" dedicated"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" someone"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" something"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" remembered"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" constellation"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" might"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" be"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" their"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" birth"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" sign"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" shape"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" that"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" reminds"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" maker"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" them"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" simply"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" pattern"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" sky"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" night"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" they"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" passed"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Silent"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Vig"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"il"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" &"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Story"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" Stars"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"**\n"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"At"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" designated"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" time"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" ("},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"e"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".g"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":".,"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"9"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" PM"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" local"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" time"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"),"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" people"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" step"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" outside"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" alone"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" quiet"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" groups"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" observe"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"15"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" minutes"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" silent"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" looking"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":" at"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"f6117a0b-129d-46fa-b239-78f01c2c5df9","object":"chat.completion.chunk","created":1764657993,"model":"deepseek-chat","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"length"}],"usage":{"prompt_tokens":13,"completion_tokens":400,"total_tokens":413,"prompt_tokens_details":{"cached_tokens":0},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":13}} \ No newline at end of file diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json b/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json new file mode 100644 index 000000000..7763e5596 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json @@ -0,0 +1,28 @@ +{ + "id": "00f10ecd-60b3-4707-b5db-e4bcadf7aea1", + "object": "chat.completion", + "created": 1764656316, + "model": "deepseek-chat", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "## **Holiday Name: Gratitude of Small Things Day (GST Day)**\n\n**Date:** The first Saturday after the Spring Equinox (in the Northern Hemisphere) / the first Saturday after the Autumn Equinox (in the Southern Hemisphere). It is intentionally placed at a time of seasonal transition, encouraging people to pause and appreciate the subtle, often overlooked details of life.\n\n**Core Philosophy:** In a world focused on grand achievements and major events, GST Day celebrates the tiny, mundane, yet profoundly beautiful moments, objects, and sensations that form the quiet fabric of our existence. The motto is: *\"Notice the thread, and you see the tapestry.\"*\n\n---\n\n### **Traditions & Practices:**\n\n**1. The \"Quiet Hour\" & Offering Bowl:**\n The holiday begins at sunrise with a \"Quiet Hour.\" Individuals or households sit in a comfortable space with a small, empty bowl (the \"Offering Bowl\"). In silence, they reflect on the past year, noting not major events, but **small, specific gratitudes**: the sound of rain on a specific afternoon, the warmth of a mug in their hands, the scent of a loved one's laundry detergent, the way light fell through a window. They write each on a tiny slip of paper and place it in the bowl. These are not shared aloud; the act is purely personal and reflective.\n\n**2. The \"Micro-Gift\" Exchange:**\n Instead of lavish presents, people exchange" + }, + "logprobs": null, + "finish_reason": "length" + } + ], + "usage": { + "prompt_tokens": 13, + "completion_tokens": 300, + "total_tokens": 313, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 13 + }, + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" +} diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt b/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt new file mode 100644 index 000000000..5f498ee2c --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt @@ -0,0 +1,52 @@ +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"role":"assistant","content":null,"reasoning_content":""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"The"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" user"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" is"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" asking"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" for"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" in"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" San"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" I"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" need"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" use"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" tool"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" get"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" this"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" information"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" Let"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" me"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" invoke"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" weather"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" tool"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" with"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" the"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" location"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" parameter"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" set"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" to"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" \""},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"San"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":" Francisco"},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":null,"reasoning_content":"\"."},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_00_ioIn7yN9p1ZOMNpDLwd4MgAF","type":"function","function":{"name":"weather","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"San"}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" Francisco"}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} +{"id":"cca85624-4056-401f-b220-d77601d1f70d","object":"chat.completion.chunk","created":1764664568,"model":"deepseek-reasoner","system_fingerprint":"fp_eaab8d114b_prod0820_fp8_kvcache","choices":[{"index":0,"delta":{"content":"","reasoning_content":null},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":339,"completion_tokens":83,"total_tokens":422,"prompt_tokens_details":{"cached_tokens":320},"completion_tokens_details":{"reasoning_tokens":39},"prompt_cache_hit_tokens":320,"prompt_cache_miss_tokens":19}} \ No newline at end of file diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json b/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json new file mode 100644 index 000000000..d3bc7c919 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json @@ -0,0 +1,43 @@ +{ + "id": "7a630f5b-b7e6-4878-82f8-d77db164d42b", + "object": "chat.completion", + "created": 1764665845, + "model": "deepseek-reasoner", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "", + "reasoning_content": "The user is asking for the weather in San Francisco. I have a weather tool available that can get weather information for a location. I should use this tool with the location parameter set to \"San Francisco\". Let me call the weather function.", + "tool_calls": [ + { + "index": 0, + "id": "call_00_9V0vrf86Pc9aelHCJMZqnJBo", + "type": "function", + "function": { + "name": "weather", + "arguments": "{\"location\": \"San Francisco\"}" + } + } + ] + }, + "logprobs": null, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 339, + "completion_tokens": 92, + "total_tokens": 431, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "completion_tokens_details": { + "reasoning_tokens": 48 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 19 + }, + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" +} diff --git a/aimux-providers/tests/it/deepseek/mod.rs b/aimux-providers/tests/it/deepseek/mod.rs new file mode 100644 index 000000000..7911476c0 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/mod.rs @@ -0,0 +1,2 @@ +mod chat_language_model; +mod raw_usage; diff --git a/aimux-providers/tests/it/deepseek/raw_usage.rs b/aimux-providers/tests/it/deepseek/raw_usage.rs new file mode 100644 index 000000000..e7151c07a --- /dev/null +++ b/aimux-providers/tests/it/deepseek/raw_usage.rs @@ -0,0 +1,213 @@ +//! Ports of `src/chat/deepseek-raw-usage.test.ts` from `@ai-sdk/deepseek@3.0.56`: +//! the complete provider usage object is kept as `usage.raw` through +//! `doGenerate` and through the final `doStream` usage event, and the known +//! usage fields are still validated. +//! +//! Harness differences from the TypeScript (expectations are unchanged): +//! - The `cacheWrite: undefined` of the TypeScript `toStrictEqual` is `None` in +//! Rust, and the JSON form of the token counts omits it. The usage counts are +//! therefore compared as JSON, where an absent `cacheWrite` is the `undefined`. +//! - The `it.each` rows are one function that checks each row in turn. +//! - Responses are served by [`MockFetch`]. + +use std::sync::Arc; + +use aimux_core::language_model::LanguageModel; +use aimux_core::language_model_message::LanguageModelMessage; +use aimux_core::options::CallOptions; +use aimux_core::stream_part::StreamPart; +use aimux_providers::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; +use futures::StreamExt; +use serde_json::{Value, json}; + +use crate::mock_fetch::{Canned, MockFetch}; + +const MODEL: &str = "deepseek-v4-flash"; + +// ── helpers ───────────────────────────────────────────────────────────────── + +/// `createDeepSeek({ apiKey })` with a mock transport. +fn provider(fetch: &Arc) -> DeepSeekProvider { + create_deepseek(DeepSeekProviderSettings { + api_key: Some("test-api-key".into()), + fetch: Some(fetch.transport()), + ..Default::default() + }) + .unwrap() +} + +/// `prompt: TEST_PROMPT`. +fn options() -> CallOptions { + CallOptions::new(vec![LanguageModelMessage::user_text("Hello")]) +} + +/// `completeUsage`: every field the raw usage must keep. +fn complete_usage() -> Value { + json!({ + "prompt_tokens": 100, + "completion_tokens": 50, + "total_tokens": 150, + "prompt_cache_hit_tokens": 20, + "prompt_cache_miss_tokens": 80, + "prompt_tokens_details": { + "cached_tokens": 20, + "provider_prompt_metadata": { "preserved": true } + }, + "completion_tokens_details": { + "reasoning_tokens": 10, + "provider_completion_metadata": ["preserved", 1] + }, + "provider_usage_metadata": { + "nested": { "preserved": true } + } + }) +} + +/// `createResponse(usage)`: a non-streaming completion carrying `usage`. +fn create_response(usage: Value) -> Value { + json!({ + "id": "chatcmpl-raw-usage", + "object": "chat.completion", + "created": 1785880000, + "model": MODEL, + "choices": [{ + "index": 0, + "message": { "role": "assistant", "content": "Hello" }, + "finish_reason": "stop" + }], + "usage": usage + }) +} + +/// An event-stream mock: one `data:` frame per payload, then `[DONE]`. +fn stream_fetch(payloads: &[String]) -> Arc { + let mut body: String = payloads + .iter() + .map(|payload| format!("data: {payload}\n\n")) + .collect(); + body.push_str("data: [DONE]\n\n"); + MockFetch::new(vec![Canned { + status: 200, + headers: vec![ + ("content-type".to_string(), "text/event-stream".to_string()), + ("cache-control".to_string(), "no-cache".to_string()), + ("connection".to_string(), "keep-alive".to_string()), + ], + body: body.into_bytes(), + }]) +} + +mod deep_seek_chat_language_model_raw_usage { + use super::*; + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-raw-usage.test.ts › DeepSeekChatLanguageModel raw usage › preserves complete raw usage through doGenerate parsing + #[tokio::test] + async fn preserves_complete_raw_usage_through_do_generate_parsing() { + let fetch = MockFetch::new(vec![Canned::json(&create_response(complete_usage()))]); + let result = provider(&fetch) + .chat(MODEL) + .do_generate(&options()) + .await + .unwrap(); + + assert_eq!(result.usage.raw, complete_usage().as_object().cloned()); + assert_eq!( + serde_json::to_value(&result.usage.input_tokens).unwrap(), + json!({ "total": 100, "noCache": 80, "cacheRead": 20 }) + ); + assert_eq!( + serde_json::to_value(&result.usage.output_tokens).unwrap(), + json!({ "total": 50, "text": 40, "reasoning": 10 }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-raw-usage.test.ts › DeepSeekChatLanguageModel raw usage › preserves complete raw usage from the final doStream usage event + #[tokio::test] + async fn preserves_complete_raw_usage_from_the_final_do_stream_usage_event() { + let initial_usage = json!({ + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0, + "provider_usage_metadata": { "stage": "initial" } + }); + let payloads = [ + json!({ + "id": "chatcmpl-raw-usage", + "object": "chat.completion.chunk", + "choices": [], + "usage": initial_usage + }) + .to_string(), + json!({ + "id": "chatcmpl-raw-usage", + "object": "chat.completion.chunk", + "choices": [{ "index": 0, "delta": {}, "finish_reason": "stop" }], + "usage": complete_usage() + }) + .to_string(), + ]; + let fetch = stream_fetch(&payloads); + let result = provider(&fetch) + .chat(MODEL) + .do_stream(&options()) + .await + .unwrap(); + let parts: Vec = result + .stream + .map(|part| part.expect("stream part")) + .collect() + .await; + let usage = parts + .iter() + .find_map(|part| match part { + StreamPart::Finish { usage, .. } => Some(usage.clone()), + _ => None, + }) + .expect("a finish part"); + + assert_eq!(usage.raw, complete_usage().as_object().cloned()); + assert_eq!( + serde_json::to_value(&usage.input_tokens).unwrap(), + json!({ "total": 100, "noCache": 80, "cacheRead": 20 }) + ); + assert_eq!( + serde_json::to_value(&usage.output_tokens).unwrap(), + json!({ "total": 50, "text": 40, "reasoning": 10 }) + ); + } + + /// upstream: @ai-sdk/deepseek src/chat/deepseek-raw-usage.test.ts › DeepSeekChatLanguageModel raw usage › continues validating known usage field %s + #[tokio::test] + async fn continues_validating_known_usage_field() { + let mut prompt_tokens = complete_usage(); + prompt_tokens["prompt_tokens"] = json!("100"); + + let mut cached_tokens = complete_usage(); + cached_tokens["prompt_tokens_details"]["cached_tokens"] = json!("20"); + + let mut reasoning_tokens = complete_usage(); + reasoning_tokens["completion_tokens_details"]["reasoning_tokens"] = json!("10"); + + let rows = [ + ("prompt_tokens", prompt_tokens), + ("prompt_tokens_details.cached_tokens", cached_tokens), + ( + "completion_tokens_details.reasoning_tokens", + reasoning_tokens, + ), + ]; + // Every row runs; the rows the provider accepts are reported together. + let mut accepted = Vec::new(); + for (field, usage) in rows { + let fetch = MockFetch::new(vec![Canned::json(&create_response(usage))]); + let outcome = provider(&fetch).chat(MODEL).do_generate(&options()).await; + if outcome.is_ok() { + accepted.push(field); + } + } + assert!( + accepted.is_empty(), + "a string count was accepted for: {accepted:?}" + ); + } +} diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json b/aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json new file mode 100644 index 000000000..3e73b4711 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json @@ -0,0 +1,135 @@ +{ + "content": [ + { + "text": "I have the result from the weather tool. It returned a JSON object with location, condition, and temperature. I need to reply with JSON object ONLY. The assistant's response should be the JSON output. So I should output exactly that JSON? Or perhaps wrap it in some way? The instruction says \"Reply with JSON object ONLY.\" Probably I should output the result as is, or maybe a structured response. But the tool result is already JSON. I think I should output that JSON directly. However, I need to ensure it's a valid response. Let me output the JSON object.", + "type": "reasoning" + }, + { + "text": "{\n \"location\": \"San Francisco\",\n \"condition\": \"cloudy\",\n \"temperature\": 7\n}", + "type": "text" + } + ], + "finishReason": { + "raw": "stop", + "unified": "stop" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 320, + "promptCacheMissTokens": 175, + "responseObject": "chat.completion", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } + }, + "request": { + "body": { + "messages": [ + { + "content": "Return JSON.", + "role": "system" + }, + { + "content": "Hello", + "role": "user" + } + ], + "model": "deepseek-reasoner", + "response_format": { + "type": "json_object" + }, + "thinking": { + "type": "enabled" + }, + "tools": [ + { + "function": { + "name": "weather", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "location": { + "type": "string" + } + }, + "required": [ + "location" + ], + "type": "object" + } + }, + "type": "function" + } + ] + } + }, + "response": { + "body": { + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "logprobs": null, + "message": { + "content": "{\n \"location\": \"San Francisco\",\n \"condition\": \"cloudy\",\n \"temperature\": 7\n}", + "reasoning_content": "I have the result from the weather tool. It returned a JSON object with location, condition, and temperature. I need to reply with JSON object ONLY. The assistant's response should be the JSON output. So I should output exactly that JSON? Or perhaps wrap it in some way? The instruction says \"Reply with JSON object ONLY.\" Probably I should output the result as is, or maybe a structured response. But the tool result is already JSON. I think I should output that JSON directly. However, I need to ensure it's a valid response. Let me output the JSON object.", + "role": "assistant" + } + } + ], + "created": 1764681341, + "id": "f03bc170-b375-4561-9685-35182c8152c5", + "model": "deepseek-reasoner", + "object": "chat.completion", + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "usage": { + "completion_tokens": 144, + "completion_tokens_details": { + "reasoning_tokens": 118 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 175, + "prompt_tokens": 495, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "total_tokens": 639 + } + }, + "headers": { + "content-length": "1194", + "content-type": "application/json" + }, + "id": "f03bc170-b375-4561-9685-35182c8152c5", + "modelId": "deepseek-reasoner", + "timestamp": "2025-12-02T13:15:41.000Z" + }, + "usage": { + "inputTokens": { + "cacheRead": 320, + "noCache": 175, + "total": 495 + }, + "outputTokens": { + "reasoning": 118, + "text": 26, + "total": 144 + }, + "raw": { + "completion_tokens": 144, + "completion_tokens_details": { + "reasoning_tokens": 118 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 175, + "prompt_tokens": 495, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "total_tokens": 639 + } + }, + "warnings": [] +} diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json b/aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json new file mode 100644 index 000000000..311e66acc --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json @@ -0,0 +1,104 @@ +{ + "content": [ + { + "text": "We are asked: \"How many 'r's are in the word 'strawberry'?\" We need to count the occurrences of the letter 'r' in the word \"strawberry\". Let's write the word: s-t-r-a-w-b-e-r-r-y. Let's count: positions: 1: s, 2: t, 3: r, 4: a, 5: w, 6: b, 7: e, 8: r, 9: r, 10: y. So there are three 'r's? Wait: at position 3: r, position 8: r, position 9: r. That's three. But sometimes people might think it's two because of pronunciation? But strictly counting letters, it's three. However, let's double-check spelling: \"strawberry\" is spelled S-T-R-A-W-B-E-R-R-Y. Yes, it has two 'r's at the end? Actually, after 'b' comes 'e', then 'r', then 'r', then 'y'. So indeed three 'r's total. But wait, the initial 'r' is the first one? The word starts with 's', then 't', then 'r'. So that's one, then later two consecutive 'r's. So total three. So answer is 3.\n\nBut the question is phrased: \"How many 'r's are in the word 'strawberry'?\" So answer is 3.", + "type": "reasoning" + }, + { + "text": "The word \"strawberry\" contains three instances of the letter \"r\": one after the \"t\" and two before the \"y\".", + "type": "text" + } + ], + "finishReason": { + "raw": "stop", + "unified": "stop" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 18, + "responseObject": "chat.completion", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } + }, + "request": { + "body": { + "messages": [ + { + "content": "Hello", + "role": "user" + } + ], + "model": "deepseek-chat" + } + }, + "response": { + "body": { + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "logprobs": null, + "message": { + "content": "The word \"strawberry\" contains three instances of the letter \"r\": one after the \"t\" and two before the \"y\".", + "reasoning_content": "We are asked: \"How many 'r's are in the word 'strawberry'?\" We need to count the occurrences of the letter 'r' in the word \"strawberry\". Let's write the word: s-t-r-a-w-b-e-r-r-y. Let's count: positions: 1: s, 2: t, 3: r, 4: a, 5: w, 6: b, 7: e, 8: r, 9: r, 10: y. So there are three 'r's? Wait: at position 3: r, position 8: r, position 9: r. That's three. But sometimes people might think it's two because of pronunciation? But strictly counting letters, it's three. However, let's double-check spelling: \"strawberry\" is spelled S-T-R-A-W-B-E-R-R-Y. Yes, it has two 'r's at the end? Actually, after 'b' comes 'e', then 'r', then 'r', then 'y'. So indeed three 'r's total. But wait, the initial 'r' is the first one? The word starts with 's', then 't', then 'r'. So that's one, then later two consecutive 'r's. So total three. So answer is 3.\n\nBut the question is phrased: \"How many 'r's are in the word 'strawberry'?\" So answer is 3.", + "role": "assistant" + } + } + ], + "created": 1764660903, + "id": "945bb10c-9bf3-47ff-a2a2-43bbe9705c72", + "model": "deepseek-reasoner", + "object": "chat.completion", + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "usage": { + "completion_tokens": 345, + "completion_tokens_details": { + "reasoning_tokens": 315 + }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 18, + "prompt_tokens": 18, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 363 + } + }, + "headers": { + "content-length": "1596", + "content-type": "application/json" + }, + "id": "945bb10c-9bf3-47ff-a2a2-43bbe9705c72", + "modelId": "deepseek-reasoner", + "timestamp": "2025-12-02T07:35:03.000Z" + }, + "usage": { + "inputTokens": { + "cacheRead": 0, + "noCache": 18, + "total": 18 + }, + "outputTokens": { + "reasoning": 315, + "text": 30, + "total": 345 + }, + "raw": { + "completion_tokens": 345, + "completion_tokens_details": { + "reasoning_tokens": 315 + }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 18, + "prompt_tokens": 18, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 363 + } + }, + "warnings": [] +} diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json b/aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json new file mode 100644 index 000000000..4f8b79fbd --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json @@ -0,0 +1,93 @@ +{ + "content": [ + { + "text": "## **Holiday Name: Gratitude of Small Things Day (GST Day)**\n\n**Date:** The first Saturday after the Spring Equinox (in the Northern Hemisphere) / the first Saturday after the Autumn Equinox (in the Southern Hemisphere). It is intentionally placed at a time of seasonal transition, encouraging people to pause and appreciate the subtle, often overlooked details of life.\n\n**Core Philosophy:** In a world focused on grand achievements and major events, GST Day celebrates the tiny, mundane, yet profoundly beautiful moments, objects, and sensations that form the quiet fabric of our existence. The motto is: *\"Notice the thread, and you see the tapestry.\"*\n\n---\n\n### **Traditions & Practices:**\n\n**1. The \"Quiet Hour\" & Offering Bowl:**\n The holiday begins at sunrise with a \"Quiet Hour.\" Individuals or households sit in a comfortable space with a small, empty bowl (the \"Offering Bowl\"). In silence, they reflect on the past year, noting not major events, but **small, specific gratitudes**: the sound of rain on a specific afternoon, the warmth of a mug in their hands, the scent of a loved one's laundry detergent, the way light fell through a window. They write each on a tiny slip of paper and place it in the bowl. These are not shared aloud; the act is purely personal and reflective.\n\n**2. The \"Micro-Gift\" Exchange:**\n Instead of lavish presents, people exchange", + "type": "text" + } + ], + "finishReason": { + "raw": "length", + "unified": "length" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 13, + "responseObject": "chat.completion", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } + }, + "request": { + "body": { + "messages": [ + { + "content": "Hello", + "role": "user" + } + ], + "model": "deepseek-chat" + } + }, + "response": { + "body": { + "choices": [ + { + "finish_reason": "length", + "index": 0, + "logprobs": null, + "message": { + "content": "## **Holiday Name: Gratitude of Small Things Day (GST Day)**\n\n**Date:** The first Saturday after the Spring Equinox (in the Northern Hemisphere) / the first Saturday after the Autumn Equinox (in the Southern Hemisphere). It is intentionally placed at a time of seasonal transition, encouraging people to pause and appreciate the subtle, often overlooked details of life.\n\n**Core Philosophy:** In a world focused on grand achievements and major events, GST Day celebrates the tiny, mundane, yet profoundly beautiful moments, objects, and sensations that form the quiet fabric of our existence. The motto is: *\"Notice the thread, and you see the tapestry.\"*\n\n---\n\n### **Traditions & Practices:**\n\n**1. The \"Quiet Hour\" & Offering Bowl:**\n The holiday begins at sunrise with a \"Quiet Hour.\" Individuals or households sit in a comfortable space with a small, empty bowl (the \"Offering Bowl\"). In silence, they reflect on the past year, noting not major events, but **small, specific gratitudes**: the sound of rain on a specific afternoon, the warmth of a mug in their hands, the scent of a loved one's laundry detergent, the way light fell through a window. They write each on a tiny slip of paper and place it in the bowl. These are not shared aloud; the act is purely personal and reflective.\n\n**2. The \"Micro-Gift\" Exchange:**\n Instead of lavish presents, people exchange", + "role": "assistant" + } + } + ], + "created": 1764656316, + "id": "00f10ecd-60b3-4707-b5db-e4bcadf7aea1", + "model": "deepseek-chat", + "object": "chat.completion", + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "usage": { + "completion_tokens": 300, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 13, + "prompt_tokens": 13, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 313 + } + }, + "headers": { + "content-length": "1857", + "content-type": "application/json" + }, + "id": "00f10ecd-60b3-4707-b5db-e4bcadf7aea1", + "modelId": "deepseek-chat", + "timestamp": "2025-12-02T06:18:36.000Z" + }, + "usage": { + "inputTokens": { + "cacheRead": 0, + "noCache": 13, + "total": 13 + }, + "outputTokens": { + "reasoning": 0, + "text": 300, + "total": 300 + }, + "raw": { + "completion_tokens": 300, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 13, + "prompt_tokens": 13, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 313 + } + }, + "warnings": [] +} diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json b/aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json new file mode 100644 index 000000000..58efc7931 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json @@ -0,0 +1,144 @@ +{ + "content": [ + { + "text": "The user is asking for the weather in San Francisco. I have a weather tool available that can get weather information for a location. I should use this tool with the location parameter set to \"San Francisco\". Let me call the weather function.", + "type": "reasoning" + }, + { + "input": "{\"location\": \"San Francisco\"}", + "toolCallId": "call_00_9V0vrf86Pc9aelHCJMZqnJBo", + "toolName": "weather", + "type": "tool-call" + } + ], + "finishReason": { + "raw": "tool_calls", + "unified": "tool-calls" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 320, + "promptCacheMissTokens": 19, + "responseObject": "chat.completion", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "toolCallTypes": [ + "function" + ] + } + }, + "request": { + "body": { + "messages": [ + { + "content": "Hello", + "role": "user" + } + ], + "model": "deepseek-reasoner", + "thinking": { + "type": "enabled" + }, + "tools": [ + { + "function": { + "name": "weather", + "parameters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "location": { + "type": "string" + } + }, + "required": [ + "location" + ], + "type": "object" + } + }, + "type": "function" + } + ] + } + }, + "response": { + "body": { + "choices": [ + { + "finish_reason": "tool_calls", + "index": 0, + "logprobs": null, + "message": { + "content": "", + "reasoning_content": "The user is asking for the weather in San Francisco. I have a weather tool available that can get weather information for a location. I should use this tool with the location parameter set to \"San Francisco\". Let me call the weather function.", + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": "{\"location\": \"San Francisco\"}", + "name": "weather" + }, + "id": "call_00_9V0vrf86Pc9aelHCJMZqnJBo", + "index": 0, + "type": "function" + } + ] + } + } + ], + "created": 1764665845, + "id": "7a630f5b-b7e6-4878-82f8-d77db164d42b", + "model": "deepseek-reasoner", + "object": "chat.completion", + "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "usage": { + "completion_tokens": 92, + "completion_tokens_details": { + "reasoning_tokens": 48 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 19, + "prompt_tokens": 339, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "total_tokens": 431 + } + }, + "headers": { + "content-length": "952", + "content-type": "application/json" + }, + "id": "7a630f5b-b7e6-4878-82f8-d77db164d42b", + "modelId": "deepseek-reasoner", + "timestamp": "2025-12-02T08:57:25.000Z" + }, + "usage": { + "inputTokens": { + "cacheRead": 320, + "noCache": 19, + "total": 339 + }, + "outputTokens": { + "reasoning": 48, + "text": 44, + "total": 92 + }, + "raw": { + "completion_tokens": 92, + "completion_tokens_details": { + "reasoning_tokens": 48 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 19, + "prompt_tokens": 339, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "total_tokens": 431 + } + }, + "warnings": [] +} diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json b/aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json new file mode 100644 index 000000000..c3bce7447 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json @@ -0,0 +1,1160 @@ +[ + { + "type": "stream-start", + "warnings": [] + }, + { + "id": "cac7192e-e619-40c6-96b0-ed4276bc03ac", + "modelId": "deepseek-reasoner", + "timestamp": "2025-12-02T07:50:32.000Z", + "type": "response-metadata" + }, + { + "id": "reasoning-0", + "type": "reasoning-start" + }, + { + "delta": "We", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " need", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " to", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " count", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " number", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " of", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " letter", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " in", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " word", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "st", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "raw", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "berry", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " The", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " word", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " is", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " spelled", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-t", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-a", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-w", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-b", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-e", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-y", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " Let", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "'s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " list", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " letters", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " and", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " count", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":\n\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "Position", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "1", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "2", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " t", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "3", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " (", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "first", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ")\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "4", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " a", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "5", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " w", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "6", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " b", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "7", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " e", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "8", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " (", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "second", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ")\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "9", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " (", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "third", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ")\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "10", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " y", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\n\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "So", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " there", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " are", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " three", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " But", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " wait", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " let", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "'s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " double", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "-check", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "st", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "raw", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "berry", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " indeed", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " has", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " three", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " one", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " after", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "t", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " and", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " then", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " two", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " consecutive", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " before", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "y", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " So", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " answer", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " is", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "3", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".\n\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "The", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " question", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " is", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " straightforward", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " However", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " sometimes", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " people", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " might", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " misc", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "ount", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " Let", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "'s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ensure", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ":", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " The", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " word", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " has", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "10", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " letters", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " The", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "r", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\"s", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " are", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " at", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " positions", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "3", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "8", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " and", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "9", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " So", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " yes", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "3", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".\n\n", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "Thus", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ",", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " answer", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " is", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " ", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "3", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "id": "txt-0", + "type": "text-start" + }, + { + "id": "reasoning-0", + "type": "reasoning-end" + }, + { + "delta": "The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " word", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " \"", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "st", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "raw", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "berry", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "\"", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " contains", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " three", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " \"", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "r", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "\"s", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "id": "txt-0", + "type": "text-end" + }, + { + "finishReason": { + "raw": "stop", + "unified": "stop" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 18, + "responseObject": "chat.completion.chunk", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } + }, + "type": "finish", + "usage": { + "inputTokens": { + "cacheRead": 0, + "noCache": 18, + "total": 18 + }, + "outputTokens": { + "reasoning": 205, + "text": 14, + "total": 219 + }, + "raw": { + "completion_tokens": 219, + "completion_tokens_details": { + "reasoning_tokens": 205 + }, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 18, + "prompt_tokens": 18, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 237 + } + } + } +] diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json b/aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json new file mode 100644 index 000000000..4c44fd4f4 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json @@ -0,0 +1,2059 @@ +[ + { + "type": "stream-start", + "warnings": [] + }, + { + "id": "f6117a0b-129d-46fa-b239-78f01c2c5df9", + "modelId": "deepseek-chat", + "timestamp": "2025-12-02T06:46:33.000Z", + "type": "response-metadata" + }, + { + "id": "txt-0", + "type": "text-start" + }, + { + "delta": "##", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " **", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "H", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "olid", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ay", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Name", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " St", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "arl", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ight", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Rem", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "embrance", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "Date", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Saturday", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " nearest", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " new", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " moon", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " in", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " October", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " when", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " night", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " sky", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " is", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " at", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " its", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " darkest", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " stars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " are", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " most", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " vivid", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "Origin", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " &", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Meaning", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "St", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "arl", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ight", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Rem", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "embrance", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " emerged", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " from", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " modern", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " collective", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " yearning", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " honor", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " personal", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " loss", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " while", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " celebrating", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " enduring", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " connections", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " that", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " transcend", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " it", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " It", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " is", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " not", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " tied", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " any", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " one", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " religion", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " or", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " culture", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " but", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " universal", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " human", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " experience", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " core", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " philosophy", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " is", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " that", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " people", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " we", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " love", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " ideas", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " that", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " inspired", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " us", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " even", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " versions", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " ourselves", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " that", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " are", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " gone", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " are", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " not", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " vanished", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " but", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " have", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " become", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " part", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " universe", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "'s", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " fabric", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "—", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "like", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " st", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ard", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ust", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " holiday", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " asks", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " participants", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " look", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " up", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " at", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " vast", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " shared", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " sky", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " find", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " comfort", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " in", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " both", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " darkness", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " (", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " loss", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ")", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " enduring", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " points", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " light", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " (", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " memory", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ").\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "---\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "###", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " **", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "T", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "rad", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "itions", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " &", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Rit", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "uals", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":**\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "1", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Dim", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ming", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Lights", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "As", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " night", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " falls", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " households", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " communities", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " turn", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " off", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " non", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "-", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "essential", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " electric", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " lights", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Streets", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " are", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " lit", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " only", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " by", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " subtle", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " blue", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "-ton", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ed", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " lantern", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "s", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " (", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "symbol", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "izing", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " night", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " sky", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ").", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " This", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " collective", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " act", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " reduces", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " light", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " pollution", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " literally", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " metaphor", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ically", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " making", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " space", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " for", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " stars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "—", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " for", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " reflection", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "2", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Craft", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ing", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Constellation", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " J", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "In", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " days", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " leading", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " up", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " holiday", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " people", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " create", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " \"", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "Const", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ellation", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " J", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "ars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".\"", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " They", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " take", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " clear", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " jar", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " using", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " small", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " drill", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " or", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " adhesive", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " create", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " pattern", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " holes", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " on", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " its", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " lid", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " representing", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " stars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " specific", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " constellation", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " On", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " night", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " holiday", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " small", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " battery", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "-operated", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " tea", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " light", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " is", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " placed", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " inside", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Each", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " jar", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " is", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " dedicated", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " to", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " someone", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " or", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " something", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " remembered", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " constellation", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " might", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " be", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " their", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " birth", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " sign", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " shape", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " that", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " reminds", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " maker", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " them", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " or", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " simply", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " pattern", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " sky", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " on", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " the", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " night", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " they", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " passed", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".\n\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "3", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " The", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Silent", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Vig", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "il", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " &", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Story", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " Stars", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ":", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "**\n", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "At", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " a", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " designated", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " time", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " (", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "e", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".g", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ".,", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " ", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "9", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " PM", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " local", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " time", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "),", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " people", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " step", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " outside", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " alone", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " or", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " in", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " quiet", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " groups", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": ",", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " and", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " observe", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " ", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": "15", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " minutes", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " of", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " silent", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " looking", + "id": "txt-0", + "type": "text-delta" + }, + { + "delta": " at", + "id": "txt-0", + "type": "text-delta" + }, + { + "id": "txt-0", + "type": "text-end" + }, + { + "finishReason": { + "raw": "length", + "unified": "length" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 0, + "promptCacheMissTokens": 13, + "responseObject": "chat.completion.chunk", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache" + } + }, + "type": "finish", + "usage": { + "inputTokens": { + "cacheRead": 0, + "noCache": 13, + "total": 13 + }, + "outputTokens": { + "reasoning": 0, + "text": 400, + "total": 400 + }, + "raw": { + "completion_tokens": 400, + "prompt_cache_hit_tokens": 0, + "prompt_cache_miss_tokens": 13, + "prompt_tokens": 13, + "prompt_tokens_details": { + "cached_tokens": 0 + }, + "total_tokens": 413 + } + } + } +] diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json b/aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json new file mode 100644 index 000000000..3e558e6d7 --- /dev/null +++ b/aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json @@ -0,0 +1,325 @@ +[ + { + "type": "stream-start", + "warnings": [] + }, + { + "id": "cca85624-4056-401f-b220-d77601d1f70d", + "modelId": "deepseek-reasoner", + "timestamp": "2025-12-02T08:36:08.000Z", + "type": "response-metadata" + }, + { + "id": "reasoning-0", + "type": "reasoning-start" + }, + { + "delta": "The", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " user", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " is", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " asking", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " for", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " weather", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " in", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " San", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " Francisco", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " I", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " need", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " to", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " use", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " weather", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " tool", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " to", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " get", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " this", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " information", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": ".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " Let", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " me", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " invoke", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " weather", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " tool", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " with", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " the", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " location", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " parameter", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " set", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " to", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " \"", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "San", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": " Francisco", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "delta": "\".", + "id": "reasoning-0", + "type": "reasoning-delta" + }, + { + "id": "reasoning-0", + "type": "reasoning-end" + }, + { + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "toolName": "weather", + "type": "tool-input-start" + }, + { + "delta": "{", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "\"", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "location", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "\"", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": ": ", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "\"", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "San", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": " Francisco", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "\"", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "delta": "}", + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-delta" + }, + { + "id": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "type": "tool-input-end" + }, + { + "input": "{\"location\": \"San Francisco\"}", + "toolCallId": "call_00_ioIn7yN9p1ZOMNpDLwd4MgAF", + "toolName": "weather", + "type": "tool-call" + }, + { + "finishReason": { + "raw": "tool_calls", + "unified": "tool-calls" + }, + "providerMetadata": { + "deepseek": { + "choiceIndex": 0, + "messageRole": "assistant", + "promptCacheHitTokens": 320, + "promptCacheMissTokens": 19, + "responseObject": "chat.completion.chunk", + "systemFingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache", + "toolCallTypes": [ + "function" + ] + } + }, + "type": "finish", + "usage": { + "inputTokens": { + "cacheRead": 320, + "noCache": 19, + "total": 339 + }, + "outputTokens": { + "reasoning": 39, + "text": 44, + "total": 83 + }, + "raw": { + "completion_tokens": 83, + "completion_tokens_details": { + "reasoning_tokens": 39 + }, + "prompt_cache_hit_tokens": 320, + "prompt_cache_miss_tokens": 19, + "prompt_tokens": 339, + "prompt_tokens_details": { + "cached_tokens": 320 + }, + "total_tokens": 422 + } + } + } +] diff --git a/aimux-providers/tests/it/main.rs b/aimux-providers/tests/it/main.rs new file mode 100644 index 000000000..cf73ca33f --- /dev/null +++ b/aimux-providers/tests/it/main.rs @@ -0,0 +1,7 @@ +//! Provider tests that go through the public API: provider → model → mock +//! fetch, one directory per vendor (RFC-0046 §5). + +#[path = "../common/mock_fetch.rs"] +mod mock_fetch; + +mod deepseek; From 725820ba43ad117ada52a17b150ea762cde10c68 Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 04:51:53 +0000 Subject: [PATCH 4/6] fix(deepseek): usage counts must be numbers, as @ai-sdk/deepseek@3.0.56 tokenUsageSchema (deepseek-chat-api-types.ts:95-113) keeps usage as a loose object but requires its known counts to be numbers or null, so a response with "prompt_tokens": "100" is rejected. aimux kept usage as a raw Value and read such a count as 0. The response and chunk types now validate the seven counts when they deserialize; the generate path reads usage from the parsed response. Test: raw_usage::continues_validating_known_usage_field. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- CHANGELOG.md | 4 +++ aimux-providers/src/deepseek/model.rs | 4 +-- aimux-providers/src/deepseek/types.rs | 42 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e03fed7f..d39d4cf9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1148,6 +1148,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- DeepSeek rejects a response whose usage counts are not numbers, as + `@ai-sdk/deepseek` 3.0.56 does (`tokenUsageSchema`); it used to read a + string such as `"prompt_tokens": "100"` as 0. Applies to generate and + stream. - `default_providers()`, `openai()` and `anthropic()` no longer panic on an invalid `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL`. The variable is still read when the provider is created, as the AI SDK does; an invalid value now diff --git a/aimux-providers/src/deepseek/model.rs b/aimux-providers/src/deepseek/model.rs index 6675d1b98..e3fad6895 100644 --- a/aimux-providers/src/deepseek/model.rs +++ b/aimux-providers/src/deepseek/model.rs @@ -386,7 +386,7 @@ impl LanguageModel for DeepSeekChatLanguageModel { })?; let mut metadata = Map::new(); - let usage_value = raw.get("usage").filter(|usage| !usage.is_null()); + let usage_value = data.usage.as_ref(); for (key, name) in [ ("prompt_cache_hit_tokens", "promptCacheHitTokens"), ("prompt_cache_miss_tokens", "promptCacheMissTokens"), @@ -461,7 +461,7 @@ impl LanguageModel for DeepSeekChatLanguageModel { unified: map_deepseek_finish_reason(choice.finish_reason.as_deref()), raw: choice.finish_reason, }, - usage: convert_deepseek_usage(raw.get("usage")), + usage: convert_deepseek_usage(data.usage.as_ref()), warnings: built.warnings, provider_metadata: Some(provider_namespace( self.provider_options_name(), diff --git a/aimux-providers/src/deepseek/types.rs b/aimux-providers/src/deepseek/types.rs index 534cb7445..dbddc5ecc 100644 --- a/aimux-providers/src/deepseek/types.rs +++ b/aimux-providers/src/deepseek/types.rs @@ -41,6 +41,8 @@ pub(crate) struct ChatResponse { pub object: Option, pub system_fingerprint: Option, pub choices: Vec, + #[serde(default, deserialize_with = "deserialize_usage")] + pub usage: Option, } #[derive(Debug, Deserialize)] @@ -83,6 +85,7 @@ pub(crate) struct ChatChunk { pub object: Option, pub system_fingerprint: Option, pub choices: Vec, + #[serde(default, deserialize_with = "deserialize_usage")] pub usage: Option, } @@ -141,6 +144,45 @@ fn deserialize_chunk_object<'de, D: Deserializer<'de>>( deserialize_literal(deserializer, "chat.completion.chunk") } +/// `tokenUsageSchema` (deepseek@3.0.56 `deepseek-chat-api-types.ts:95-113`): +/// a loose object kept whole as `usage.raw`, whose known counts must still be +/// numbers or null. +fn deserialize_usage<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + const COUNTS: [&str; 7] = [ + "/prompt_tokens", + "/completion_tokens", + "/prompt_cache_hit_tokens", + "/prompt_cache_miss_tokens", + "/total_tokens", + "/prompt_tokens_details/cached_tokens", + "/completion_tokens_details/reasoning_tokens", + ]; + let usage = Option::::deserialize(deserializer)?.filter(|usage| !usage.is_null()); + let Some(usage) = usage else { + return Ok(None); + }; + let is_object_or_null = + |value: Option<&Value>| value.is_none_or(|v| v.is_object() || v.is_null()); + if !usage.is_object() + || !is_object_or_null(usage.get("prompt_tokens_details")) + || !is_object_or_null(usage.get("completion_tokens_details")) + { + return Err(serde::de::Error::custom("usage must be an object")); + } + if let Some(field) = COUNTS.iter().find(|pointer| { + usage + .pointer(pointer) + .is_some_and(|value| !value.is_number() && !value.is_null()) + }) { + return Err(serde::de::Error::custom(format!( + "usage{field} must be a number" + ))); + } + Ok(Some(usage)) +} + fn deserialize_role<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result, D::Error> { From d92a81a725e3ba2cbfd40b1a59dae4d87502c9c0 Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 04:51:53 +0000 Subject: [PATCH 5/6] chore(deepseek): add the layout.toml entries for @ai-sdk/deepseek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maps each upstream source file of @ai-sdk/deepseek@3.0.56 to its Rust file (RFC-0046 §4.4) with the RFC-0045 tests table. The Files API is missing in aimux (#275), so its upstream tests are not ported. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- aimux-providers/layout.toml | 125 ++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 aimux-providers/layout.toml diff --git a/aimux-providers/layout.toml b/aimux-providers/layout.toml new file mode 100644 index 000000000..bd83e71aa --- /dev/null +++ b/aimux-providers/layout.toml @@ -0,0 +1,125 @@ +# Upstream file map for aimux-providers (RFC-0046 §4.4, RFC-0045 §3). +# One table per vendor with an upstream package; versions come from +# fixtures/aisdk/VERSIONS.json. `rust` paths are relative to src/. + +[deepseek] +package = "@ai-sdk/deepseek" + +[[deepseek.files]] +upstream = "src/chat/convert-to-deepseek-chat-messages.ts" +rust = "deepseek/convert.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/convert-to-deepseek-usage.ts" +rust = "deepseek/usage.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/deepseek-chat-api-types.ts" +rust = "deepseek/types.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/deepseek-chat-language-model-options.ts" +rust = "deepseek/options.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/deepseek-chat-language-model.ts" +rust = "deepseek/model.rs" +status = "mapped" + +[deepseek.files.tests] +ts-only = [ + { case = "DeepSeekChatLanguageModel › doGenerate › tool call › json response format with structured outputs › should send json_schema response format and skip schema injection", reason = "unreachable through the provider: because the provider never sets supportsStructuredOutputs (deepseek-provider.ts:88-97); the case needs a model built directly" }, + { case = "DeepSeekChatLanguageModel › doGenerate › tool call › json response format with structured outputs › should honor strictJsonSchema provider option", reason = "unreachable through the provider: because the provider never sets supportsStructuredOutputs (deepseek-provider.ts:88-97); the case needs a model built directly" }, + { case = "DeepSeekChatLanguageModel › doGenerate › tool call › json response format with structured outputs › should fall back to json_object without a schema", reason = "unreachable through the provider: because the provider never sets supportsStructuredOutputs (deepseek-provider.ts:88-97); the case needs a model built directly" }, +] + +[[deepseek.files]] +upstream = "src/chat/deepseek-file-part-options.ts" +rust = "deepseek/options.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/deepseek-prepare-tools.ts" +rust = "deepseek/prepare_tools.rs" +status = "mapped" + +[deepseek.files.tests] +ts-only = [ + { case = "prepareTools › should pass through strict mode for multiple tools with different strict settings", reason = "needs supportsStrictToolCalls undefined, which only a directly built model has; the provider always sets it from the base URL (deepseek-provider.ts:95), as aimux does" }, +] + +[[deepseek.files]] +upstream = "src/chat/get-response-metadata.ts" +rust = "deepseek/model.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/is-deepseek-v4-model.ts" +rust = "deepseek/is_v4_model.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/chat/map-deepseek-finish-reason.ts" +rust = "deepseek/finish_reason.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/deepseek-provider.ts" +rust = "deepseek/mod.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/index.ts" +rust = "deepseek/mod.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/internal/index.ts" +rust = "deepseek/mod.rs" +status = "mapped" + +[deepseek.files.tests] + +[[deepseek.files]] +upstream = "src/files/deepseek-files-api.ts" +status = "missing" +issue = 275 + +[[deepseek.files]] +upstream = "src/files/deepseek-files-options.ts" +status = "missing" +issue = 275 + +[[deepseek.files]] +upstream = "src/files/deepseek-files.ts" +status = "missing" +issue = 275 + +[[deepseek.files]] +upstream = "src/version.ts" +status = "not-applicable" +reason = "the crate version is used instead (RFC-0046 §4.2)" From 5931c724faee1979b742aa49ea03e8579f603a3e Mon Sep 17 00:00:00 2001 From: cunninghamcard-bit Date: Tue, 20 Oct 2026 06:40:10 +0000 Subject: [PATCH 6/6] test(deepseek): keep the vendor tests in src/deepseek/tests/ All DeepSeek tests of this port now live in one directory that keeps the upstream src/chat/ subdirectory, one file per upstream test file: chat/chat_language_model, convert_to_chat_messages, convert_to_usage, is_v4_model, prepare_tools and raw_usage, with their fixtures/ and snapshots/. The src/deepseek/*/tests.rs files and the tests/it/ binary are gone; the shared mock fetch is reached through src/test_support/. The boundary script skips files under tests/ dirs as it skips #[cfg(test)] blocks. Test bodies and markers are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5 --- aimux-providers/src/deepseek/convert.rs | 3 --- aimux-providers/src/deepseek/is_v4_model.rs | 3 --- aimux-providers/src/deepseek/mod.rs | 3 +++ aimux-providers/src/deepseek/prepare_tools.rs | 3 --- .../deepseek/tests/chat}/chat_language_model.rs | 4 ++-- .../tests.rs => tests/chat/convert_to_chat_messages.rs} | 9 ++++++++- .../{usage/tests.rs => tests/chat/convert_to_usage.rs} | 3 ++- .../deepseek/tests/chat}/fixtures/deepseek-json.json | 0 .../tests/chat}/fixtures/deepseek-reasoning.chunks.txt | 0 .../tests/chat}/fixtures/deepseek-reasoning.json | 0 .../tests/chat}/fixtures/deepseek-text.chunks.txt | 0 .../deepseek/tests/chat}/fixtures/deepseek-text.json | 0 .../tests/chat}/fixtures/deepseek-tool-call.chunks.txt | 0 .../tests/chat}/fixtures/deepseek-tool-call.json | 0 .../{is_v4_model/tests.rs => tests/chat/is_v4_model.rs} | 2 +- aimux-providers/src/deepseek/tests/chat/mod.rs | 8 ++++++++ .../tests.rs => tests/chat/prepare_tools.rs} | 6 +++++- .../it/deepseek => src/deepseek/tests/chat}/raw_usage.rs | 4 ++-- .../snapshots/do_generate_json_response_format.json | 0 .../tests/chat}/snapshots/do_generate_reasoning.json | 0 .../deepseek/tests/chat}/snapshots/do_generate_text.json | 0 .../tests/chat}/snapshots/do_generate_tool_call.json | 0 .../tests/chat}/snapshots/do_stream_reasoning.json | 0 .../deepseek/tests/chat}/snapshots/do_stream_text.json | 0 .../tests/chat}/snapshots/do_stream_tool_call.json | 0 aimux-providers/src/deepseek/tests/mod.rs | 3 +++ aimux-providers/src/deepseek/usage.rs | 3 --- aimux-providers/src/lib.rs | 3 +++ aimux-providers/src/test_support/mod.rs | 4 ++++ aimux-providers/tests/it/deepseek/mod.rs | 2 -- aimux-providers/tests/it/main.rs | 7 ------- scripts/check_provider_boundaries.sh | 4 ++-- 32 files changed, 43 insertions(+), 31 deletions(-) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/chat_language_model.rs (98%) rename aimux-providers/src/deepseek/{convert/tests.rs => tests/chat/convert_to_chat_messages.rs} (97%) rename aimux-providers/src/deepseek/{usage/tests.rs => tests/chat/convert_to_usage.rs} (95%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-json.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-reasoning.chunks.txt (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-reasoning.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-text.chunks.txt (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-text.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-tool-call.chunks.txt (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/fixtures/deepseek-tool-call.json (100%) rename aimux-providers/src/deepseek/{is_v4_model/tests.rs => tests/chat/is_v4_model.rs} (95%) create mode 100644 aimux-providers/src/deepseek/tests/chat/mod.rs rename aimux-providers/src/deepseek/{prepare_tools/tests.rs => tests/chat/prepare_tools.rs} (96%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/raw_usage.rs (98%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_generate_json_response_format.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_generate_reasoning.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_generate_text.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_generate_tool_call.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_stream_reasoning.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_stream_text.json (100%) rename aimux-providers/{tests/it/deepseek => src/deepseek/tests/chat}/snapshots/do_stream_tool_call.json (100%) create mode 100644 aimux-providers/src/deepseek/tests/mod.rs create mode 100644 aimux-providers/src/test_support/mod.rs delete mode 100644 aimux-providers/tests/it/deepseek/mod.rs delete mode 100644 aimux-providers/tests/it/main.rs diff --git a/aimux-providers/src/deepseek/convert.rs b/aimux-providers/src/deepseek/convert.rs index 33fb80c2e..353bc875a 100644 --- a/aimux-providers/src/deepseek/convert.rs +++ b/aimux-providers/src/deepseek/convert.rs @@ -377,6 +377,3 @@ fn convert_tool_result_content( } Ok(Value::Array(content)) } - -#[cfg(test)] -mod tests; diff --git a/aimux-providers/src/deepseek/is_v4_model.rs b/aimux-providers/src/deepseek/is_v4_model.rs index deb292463..f7dbf8a26 100644 --- a/aimux-providers/src/deepseek/is_v4_model.rs +++ b/aimux-providers/src/deepseek/is_v4_model.rs @@ -8,6 +8,3 @@ pub(crate) fn is_deepseek_v4_model(model_id: &str) -> bool { || model_id.starts_with("deepseek-flash") || model_id.starts_with("deepseek-pro") } - -#[cfg(test)] -mod tests; diff --git a/aimux-providers/src/deepseek/mod.rs b/aimux-providers/src/deepseek/mod.rs index b07e1a1af..d67bf6709 100644 --- a/aimux-providers/src/deepseek/mod.rs +++ b/aimux-providers/src/deepseek/mod.rs @@ -191,3 +191,6 @@ impl ProviderDiscovery for DeepSeekProvider { }) } } + +#[cfg(test)] +mod tests; diff --git a/aimux-providers/src/deepseek/prepare_tools.rs b/aimux-providers/src/deepseek/prepare_tools.rs index 3319b3f7d..9ac4cc386 100644 --- a/aimux-providers/src/deepseek/prepare_tools.rs +++ b/aimux-providers/src/deepseek/prepare_tools.rs @@ -94,6 +94,3 @@ pub(crate) fn prepare_tools( tool_warnings, }) } - -#[cfg(test)] -mod tests; diff --git a/aimux-providers/tests/it/deepseek/chat_language_model.rs b/aimux-providers/src/deepseek/tests/chat/chat_language_model.rs similarity index 98% rename from aimux-providers/tests/it/deepseek/chat_language_model.rs rename to aimux-providers/src/deepseek/tests/chat/chat_language_model.rs index 477008b92..2557dc2eb 100644 --- a/aimux-providers/tests/it/deepseek/chat_language_model.rs +++ b/aimux-providers/src/deepseek/tests/chat/chat_language_model.rs @@ -26,16 +26,16 @@ use std::sync::Arc; +use crate::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; use aimux_core::language_model::LanguageModel; use aimux_core::language_model_message::LanguageModelMessage; use aimux_core::options::{CallOptions, ResponseFormat}; use aimux_core::result::StreamResult; use aimux_core::tool::{FunctionTool, Tool}; -use aimux_providers::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; use futures::StreamExt; use serde_json::{Value, json}; -use crate::mock_fetch::{Canned, MockFetch}; +use crate::test_support::mock_fetch::{Canned, MockFetch}; const TEXT_FIXTURE: &str = include_str!("fixtures/deepseek-text.json"); const REASONING_FIXTURE: &str = include_str!("fixtures/deepseek-reasoning.json"); diff --git a/aimux-providers/src/deepseek/convert/tests.rs b/aimux-providers/src/deepseek/tests/chat/convert_to_chat_messages.rs similarity index 97% rename from aimux-providers/src/deepseek/convert/tests.rs rename to aimux-providers/src/deepseek/tests/chat/convert_to_chat_messages.rs index 2469bfb40..6c65e9dba 100644 --- a/aimux-providers/src/deepseek/convert/tests.rs +++ b/aimux-providers/src/deepseek/tests/chat/convert_to_chat_messages.rs @@ -8,9 +8,16 @@ //! `FileBytes::Binary` directly: JSON can only carry base64, which parses to the //! other variant. -use super::*; +use aimux_core::error::AiMuxError; +use aimux_core::language_model_message::{ + FilePart, LanguageModelMessage, LanguageModelPrompt, ToolPart, ToolResultContent, + ToolResultOutput, ToolResultPart, +}; +use aimux_core::shared::{FileBytes, FileData}; use serde_json::{Value, json}; +use crate::deepseek::convert::convert_to_deepseek_chat_messages; + /// A prompt, read as the request path reads it. fn prompt_from(value: Value) -> LanguageModelPrompt { serde_json::from_value(value).expect("the prompt literal is valid") diff --git a/aimux-providers/src/deepseek/usage/tests.rs b/aimux-providers/src/deepseek/tests/chat/convert_to_usage.rs similarity index 95% rename from aimux-providers/src/deepseek/usage/tests.rs rename to aimux-providers/src/deepseek/tests/chat/convert_to_usage.rs index decec783b..5ea2b34dd 100644 --- a/aimux-providers/src/deepseek/usage/tests.rs +++ b/aimux-providers/src/deepseek/tests/chat/convert_to_usage.rs @@ -1,8 +1,9 @@ //! Port of `src/chat/convert-to-deepseek-usage.test.ts` (`@ai-sdk/deepseek` 3.0.56). -use super::*; use serde_json::{Value, json}; +use crate::deepseek::usage::convert_deepseek_usage; + mod convert_deep_seek_usage { use super::*; diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-json.json similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-json.json rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-json.json diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-reasoning.chunks.txt similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.chunks.txt rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-reasoning.chunks.txt diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-reasoning.json similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-reasoning.json rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-reasoning.json diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-text.chunks.txt similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-text.chunks.txt rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-text.chunks.txt diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-text.json similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-text.json rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-text.json diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-tool-call.chunks.txt similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.chunks.txt rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-tool-call.chunks.txt diff --git a/aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json b/aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-tool-call.json similarity index 100% rename from aimux-providers/tests/it/deepseek/fixtures/deepseek-tool-call.json rename to aimux-providers/src/deepseek/tests/chat/fixtures/deepseek-tool-call.json diff --git a/aimux-providers/src/deepseek/is_v4_model/tests.rs b/aimux-providers/src/deepseek/tests/chat/is_v4_model.rs similarity index 95% rename from aimux-providers/src/deepseek/is_v4_model/tests.rs rename to aimux-providers/src/deepseek/tests/chat/is_v4_model.rs index e75da4622..eef84ab64 100644 --- a/aimux-providers/src/deepseek/is_v4_model/tests.rs +++ b/aimux-providers/src/deepseek/tests/chat/is_v4_model.rs @@ -1,6 +1,6 @@ //! Port of `src/chat/is-deepseek-v4-model.test.ts` (`@ai-sdk/deepseek` 3.0.56). -use super::*; +use crate::deepseek::is_v4_model::is_deepseek_v4_model; mod is_deep_seek_v4_model { use super::*; diff --git a/aimux-providers/src/deepseek/tests/chat/mod.rs b/aimux-providers/src/deepseek/tests/chat/mod.rs new file mode 100644 index 000000000..545e1e65b --- /dev/null +++ b/aimux-providers/src/deepseek/tests/chat/mod.rs @@ -0,0 +1,8 @@ +//! Ports of the upstream `src/chat/` tests of `@ai-sdk/deepseek` 3.0.56. + +mod chat_language_model; +mod convert_to_chat_messages; +mod convert_to_usage; +mod is_v4_model; +mod prepare_tools; +mod raw_usage; diff --git a/aimux-providers/src/deepseek/prepare_tools/tests.rs b/aimux-providers/src/deepseek/tests/chat/prepare_tools.rs similarity index 96% rename from aimux-providers/src/deepseek/prepare_tools/tests.rs rename to aimux-providers/src/deepseek/tests/chat/prepare_tools.rs index e00760cb5..34845fa96 100644 --- a/aimux-providers/src/deepseek/prepare_tools/tests.rs +++ b/aimux-providers/src/deepseek/tests/chat/prepare_tools.rs @@ -7,14 +7,18 @@ //! different strict settings" depends on that state and is not ported (no //! boolean reproduces it; see the report). -use super::*; +use aimux_core::tool::Tool; use serde_json::{Value, json}; +use crate::deepseek::prepare_tools::prepare_tools; + /// The tools of a request, read as the request path reads them. fn tools_from(value: Value) -> Vec { serde_json::from_value(value).expect("the tools literal is valid") } +// The describe level `prepareTools` keeps its name; the file is `prepare_tools.rs`. +#[allow(clippy::module_inception)] mod prepare_tools { use super::*; diff --git a/aimux-providers/tests/it/deepseek/raw_usage.rs b/aimux-providers/src/deepseek/tests/chat/raw_usage.rs similarity index 98% rename from aimux-providers/tests/it/deepseek/raw_usage.rs rename to aimux-providers/src/deepseek/tests/chat/raw_usage.rs index e7151c07a..0302797ad 100644 --- a/aimux-providers/tests/it/deepseek/raw_usage.rs +++ b/aimux-providers/src/deepseek/tests/chat/raw_usage.rs @@ -12,15 +12,15 @@ use std::sync::Arc; +use crate::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; use aimux_core::language_model::LanguageModel; use aimux_core::language_model_message::LanguageModelMessage; use aimux_core::options::CallOptions; use aimux_core::stream_part::StreamPart; -use aimux_providers::deepseek::{DeepSeekProvider, DeepSeekProviderSettings, create_deepseek}; use futures::StreamExt; use serde_json::{Value, json}; -use crate::mock_fetch::{Canned, MockFetch}; +use crate::test_support::mock_fetch::{Canned, MockFetch}; const MODEL: &str = "deepseek-v4-flash"; diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_json_response_format.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_generate_json_response_format.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_json_response_format.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_reasoning.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_generate_reasoning.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_reasoning.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_text.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_generate_text.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_text.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_tool_call.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_generate_tool_call.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_generate_tool_call.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_reasoning.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_stream_reasoning.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_reasoning.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_text.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_stream_text.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_text.json diff --git a/aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json b/aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_tool_call.json similarity index 100% rename from aimux-providers/tests/it/deepseek/snapshots/do_stream_tool_call.json rename to aimux-providers/src/deepseek/tests/chat/snapshots/do_stream_tool_call.json diff --git a/aimux-providers/src/deepseek/tests/mod.rs b/aimux-providers/src/deepseek/tests/mod.rs new file mode 100644 index 000000000..26b2c32fc --- /dev/null +++ b/aimux-providers/src/deepseek/tests/mod.rs @@ -0,0 +1,3 @@ +//! Tests ported from the upstream `@ai-sdk/deepseek` 3.0.56 package. + +mod chat; diff --git a/aimux-providers/src/deepseek/usage.rs b/aimux-providers/src/deepseek/usage.rs index 380d0ecdd..6c9ca7ae5 100644 --- a/aimux-providers/src/deepseek/usage.rs +++ b/aimux-providers/src/deepseek/usage.rs @@ -37,6 +37,3 @@ pub(crate) fn convert_deepseek_usage(usage: Option<&Value>) -> Usage { raw: usage.as_object().cloned(), } } - -#[cfg(test)] -mod tests; diff --git a/aimux-providers/src/lib.rs b/aimux-providers/src/lib.rs index 970691328..e8eb8d125 100644 --- a/aimux-providers/src/lib.rs +++ b/aimux-providers/src/lib.rs @@ -403,3 +403,6 @@ mod decision_support; pub use decision_support::DecisionProbabilitySource; mod runtime_decision; mod systemone; + +#[cfg(test)] +mod test_support; diff --git a/aimux-providers/src/test_support/mod.rs b/aimux-providers/src/test_support/mod.rs new file mode 100644 index 000000000..9bbf0e551 --- /dev/null +++ b/aimux-providers/src/test_support/mod.rs @@ -0,0 +1,4 @@ +//! Shared test helpers for the in-crate vendor tests. + +#[path = "../../tests/common/mock_fetch.rs"] +pub(crate) mod mock_fetch; diff --git a/aimux-providers/tests/it/deepseek/mod.rs b/aimux-providers/tests/it/deepseek/mod.rs deleted file mode 100644 index 7911476c0..000000000 --- a/aimux-providers/tests/it/deepseek/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod chat_language_model; -mod raw_usage; diff --git a/aimux-providers/tests/it/main.rs b/aimux-providers/tests/it/main.rs deleted file mode 100644 index cf73ca33f..000000000 --- a/aimux-providers/tests/it/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Provider tests that go through the public API: provider → model → mock -//! fetch, one directory per vendor (RFC-0046 §5). - -#[path = "../common/mock_fetch.rs"] -mod mock_fetch; - -mod deepseek; diff --git a/scripts/check_provider_boundaries.sh b/scripts/check_provider_boundaries.sh index df1578cd2..54d3fb7fc 100755 --- a/scripts/check_provider_boundaries.sh +++ b/scripts/check_provider_boundaries.sh @@ -214,7 +214,7 @@ rule7b_hits="$( violation 'bedrock::sigv4 is back (use SigV4Fetch)' "$rule7b_hits" rule7c_hits="$( # Non-test, non-comment lines of every file but the two helpers. - find $gvb_family -name '*.rs' ! -name options.rs -print0 | + find $gvb_family -name '*.rs' ! -name options.rs ! -path '*/tests/*' -print0 | xargs -0 awk ' FNR == 1 { in_tests = 0 } /^#\[cfg\(test\)\]/ { in_tests = 1 } @@ -245,7 +245,7 @@ rule8_hits="$( violation 'Azure / xAI / Mistral / Cohere / Hugging Face / Codex / Open Responses / Voyage / ElevenLabs reads builder-era config state' "$rule8_hits" rule8b_hits="$( # Non-test, non-comment lines of every file but the options helpers. - find $vendor_family -name '*.rs' ! -name options.rs -print0 | + find $vendor_family -name '*.rs' ! -name options.rs ! -path '*/tests/*' -print0 | xargs -0 awk ' FNR == 1 { in_tests = 0 } /^#\[cfg\(test\)\]/ { in_tests = 1 }