diff --git a/src/llm/anthropic.rs b/src/llm/anthropic.rs index ba5f358..0779a5e 100644 --- a/src/llm/anthropic.rs +++ b/src/llm/anthropic.rs @@ -46,7 +46,8 @@ struct SystemContent { struct ThinkingConfig { #[serde(rename = "type")] thinking_type: String, - budget_tokens: u32, + #[serde(skip_serializing_if = "Option::is_none")] + budget_tokens: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -354,7 +355,10 @@ impl AnthropicClient { top_p: self.top_p, messages, system, - thinking: None, + thinking: Some(ThinkingConfig { + thinking_type: "disabled".to_string(), + budget_tokens: None, + }), stream: false, }; @@ -409,7 +413,7 @@ impl AnthropicClient { let thinking = ThinkingConfig { thinking_type: "enabled".to_string(), - budget_tokens: self.thinking_budget_tokens, + budget_tokens: Some(self.thinking_budget_tokens), }; // max_tokens must exceed budget_tokens @@ -596,13 +600,23 @@ mod tests { fn test_thinking_config_serialization() { let config = ThinkingConfig { thinking_type: "enabled".to_string(), - budget_tokens: 2048, + budget_tokens: Some(2048), }; let json = serde_json::to_string(&config).unwrap(); assert!(json.contains(r#""type":"enabled""#)); assert!(json.contains(r#""budget_tokens":2048"#)); } + #[test] + fn test_thinking_config_disabled_serialization() { + let config = ThinkingConfig { + thinking_type: "disabled".to_string(), + budget_tokens: None, + }; + let json = serde_json::to_string(&config).unwrap(); + assert_eq!(json, r#"{"type":"disabled"}"#); + } + #[test] fn test_system_content_serialization() { let content = SystemContent { diff --git a/src/llm/deepseek.rs b/src/llm/deepseek.rs index 2ab1d73..f287917 100644 --- a/src/llm/deepseek.rs +++ b/src/llm/deepseek.rs @@ -291,13 +291,13 @@ impl DeepSeekClient { async fn chat_completion(&self, messages: Vec) -> Result { let url = format!("{}/chat/completions", self.base_url); - let thinking = if self.thinking_enabled { - Some(ThinkingConfig { - thinking_type: "enabled".to_string(), - }) - } else { - None - }; + let thinking = Some(ThinkingConfig { + thinking_type: if self.thinking_enabled { + "enabled".to_string() + } else { + "disabled".to_string() + }, + }); // 思考模式下,temperature/top_p 等参数不应传递 // 非思考模式下可以正常传递 diff --git a/src/llm/kimi.rs b/src/llm/kimi.rs index 7339ceb..47dc940 100644 --- a/src/llm/kimi.rs +++ b/src/llm/kimi.rs @@ -273,13 +273,13 @@ impl KimiClient { async fn chat_completion(&self, messages: Vec) -> Result { let url = format!("{}/chat/completions", self.base_url); - let thinking = if self.thinking_enabled { - Some(ThinkingConfig { - thinking_type: "enabled".to_string(), - }) - } else { - None - }; + let thinking = Some(ThinkingConfig { + thinking_type: if self.thinking_enabled { + "enabled".to_string() + } else { + "disabled".to_string() + }, + }); // 对于 kimi-k2.6 等支持思考模式的模型,使用默认 temperature 即可 // 思考模式下不显式指定 temperature diff --git a/src/llm/openai.rs b/src/llm/openai.rs index 0a7df63..21c7c12 100644 --- a/src/llm/openai.rs +++ b/src/llm/openai.rs @@ -275,7 +275,11 @@ impl OpenAiClient { max_tokens: Some(self.max_tokens), temperature: Some(self.temperature), top_p: self.top_p, - reasoning_effort: None, + reasoning_effort: if is_reasoning_model(&self.model) { + Some("none".to_string()) + } else { + None + }, stream: false, }; @@ -619,6 +623,10 @@ pub fn is_valid_model(model: &str) -> bool { OPENAI_MODELS.contains(&model) } +fn is_reasoning_model(model: &str) -> bool { + model.starts_with("o") +} + #[cfg(test)] mod tests { use super::*;