feat(llm): 统一思考模式配置,支持显式禁用状态

This commit is contained in:
2026-06-01 17:39:36 +08:00
parent 928ebb61b4
commit 7636d0b5a6
4 changed files with 41 additions and 19 deletions

View File

@@ -46,7 +46,8 @@ struct SystemContent {
struct ThinkingConfig { struct ThinkingConfig {
#[serde(rename = "type")] #[serde(rename = "type")]
thinking_type: String, thinking_type: String,
budget_tokens: u32, #[serde(skip_serializing_if = "Option::is_none")]
budget_tokens: Option<u32>,
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -354,7 +355,10 @@ impl AnthropicClient {
top_p: self.top_p, top_p: self.top_p,
messages, messages,
system, system,
thinking: None, thinking: Some(ThinkingConfig {
thinking_type: "disabled".to_string(),
budget_tokens: None,
}),
stream: false, stream: false,
}; };
@@ -409,7 +413,7 @@ impl AnthropicClient {
let thinking = ThinkingConfig { let thinking = ThinkingConfig {
thinking_type: "enabled".to_string(), thinking_type: "enabled".to_string(),
budget_tokens: self.thinking_budget_tokens, budget_tokens: Some(self.thinking_budget_tokens),
}; };
// max_tokens must exceed budget_tokens // max_tokens must exceed budget_tokens
@@ -596,13 +600,23 @@ mod tests {
fn test_thinking_config_serialization() { fn test_thinking_config_serialization() {
let config = ThinkingConfig { let config = ThinkingConfig {
thinking_type: "enabled".to_string(), thinking_type: "enabled".to_string(),
budget_tokens: 2048, budget_tokens: Some(2048),
}; };
let json = serde_json::to_string(&config).unwrap(); let json = serde_json::to_string(&config).unwrap();
assert!(json.contains(r#""type":"enabled""#)); assert!(json.contains(r#""type":"enabled""#));
assert!(json.contains(r#""budget_tokens":2048"#)); 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] #[test]
fn test_system_content_serialization() { fn test_system_content_serialization() {
let content = SystemContent { let content = SystemContent {

View File

@@ -291,13 +291,13 @@ impl DeepSeekClient {
async fn chat_completion(&self, messages: Vec<Message>) -> Result<String> { async fn chat_completion(&self, messages: Vec<Message>) -> Result<String> {
let url = format!("{}/chat/completions", self.base_url); let url = format!("{}/chat/completions", self.base_url);
let thinking = if self.thinking_enabled { let thinking = Some(ThinkingConfig {
Some(ThinkingConfig { thinking_type: if self.thinking_enabled {
thinking_type: "enabled".to_string(), "enabled".to_string()
}) } else {
} else { "disabled".to_string()
None },
}; });
// 思考模式下temperature/top_p 等参数不应传递 // 思考模式下temperature/top_p 等参数不应传递
// 非思考模式下可以正常传递 // 非思考模式下可以正常传递

View File

@@ -273,13 +273,13 @@ impl KimiClient {
async fn chat_completion(&self, messages: Vec<Message>) -> Result<String> { async fn chat_completion(&self, messages: Vec<Message>) -> Result<String> {
let url = format!("{}/chat/completions", self.base_url); let url = format!("{}/chat/completions", self.base_url);
let thinking = if self.thinking_enabled { let thinking = Some(ThinkingConfig {
Some(ThinkingConfig { thinking_type: if self.thinking_enabled {
thinking_type: "enabled".to_string(), "enabled".to_string()
}) } else {
} else { "disabled".to_string()
None },
}; });
// 对于 kimi-k2.6 等支持思考模式的模型,使用默认 temperature 即可 // 对于 kimi-k2.6 等支持思考模式的模型,使用默认 temperature 即可
// 思考模式下不显式指定 temperature // 思考模式下不显式指定 temperature

View File

@@ -275,7 +275,11 @@ impl OpenAiClient {
max_tokens: Some(self.max_tokens), max_tokens: Some(self.max_tokens),
temperature: Some(self.temperature), temperature: Some(self.temperature),
top_p: self.top_p, top_p: self.top_p,
reasoning_effort: None, reasoning_effort: if is_reasoning_model(&self.model) {
Some("none".to_string())
} else {
None
},
stream: false, stream: false,
}; };
@@ -619,6 +623,10 @@ pub fn is_valid_model(model: &str) -> bool {
OPENAI_MODELS.contains(&model) OPENAI_MODELS.contains(&model)
} }
fn is_reasoning_model(model: &str) -> bool {
model.starts_with("o")
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;