feat(llm): 统一思考模式配置,支持显式禁用状态
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
None
|
"disabled".to_string()
|
||||||
};
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 思考模式下,temperature/top_p 等参数不应传递
|
// 思考模式下,temperature/top_p 等参数不应传递
|
||||||
// 非思考模式下可以正常传递
|
// 非思考模式下可以正常传递
|
||||||
|
|||||||
@@ -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 {
|
||||||
None
|
"disabled".to_string()
|
||||||
};
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 对于 kimi-k2.6 等支持思考模式的模型,使用默认 temperature 即可
|
// 对于 kimi-k2.6 等支持思考模式的模型,使用默认 temperature 即可
|
||||||
// 思考模式下不显式指定 temperature
|
// 思考模式下不显式指定 temperature
|
||||||
|
|||||||
@@ -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::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user