fix(llm): 官方 OpenAI gpt-5 系列不下发 temperature,修复验证 400(#857) (#888)

* fix(llm): omit temperature for OpenAI gpt-5 family (fixes #857)

OpenAI 官方 gpt-5 系列在 Chat Completions 只接受默认 temperature=1,
传 0.3(内置默认)会被 400 拒绝,导致验证/润色全部失败。
官方渠道的 gpt-5* 不再下发 temperature,由服务端用默认值;
gpt-4 等其余模型保持原行为。custom 预设显式温度不受影响。

* fix(android): skip set_ignore_cursor_events on Android capsule path

Android 胶囊走原生 overlay,tauri::WebviewWindow 无
set_ignore_cursor_events(桌面专用),导致 Android cargo check
编译失败(E0599)。cfg 门控跳过,触摸交互由系统处理。
This commit is contained in:
Chris233 authored and GitHub committed 2026-08-03 16:21:14 +08:00
1 parent 73e65a4077
commit 26cd012433
1 file changed
+77 -1
+77 -1
View File
@@ -578,7 +578,14 @@ impl OpenAICompatibleLLMProvider {
"messages": messages,
});
if let Some(temperature) = self.config.temperature {
body["temperature"] = json!(temperature);
// OpenAI 官方 gpt-5 系列在 Chat Completions 只接受默认 temperature=1,
// 传 0.3 会被 400 拒绝(issue #857)。官方渠道的 gpt-5* 不下发该字段,
// 让服务端用默认值;其余模型保持原行为。
if !(self.config.provider_id.trim() == "openai"
&& openai_model_is_gpt5_family(&self.config.model))
{
body["temperature"] = json!(temperature);
}
}
apply_openai_compatible_thinking_control(&mut body, &self.config);
body
@@ -1695,6 +1702,18 @@ fn openai_compatible_thinking_control_for_base_url(base_url: &str) -> Option<Thi
None
}
/// OpenAI 官方 gpt-5 系列(gpt-5 / gpt-5-mini / gpt-5-nano / gpt-5.5 等)在
/// Chat Completions 中只接受默认 temperature=1,传其它值会返回 400(issue #857)。
/// 模型名归一化规则与 `openai_chat_reasoning_effort` 保持一致。
fn openai_model_is_gpt5_family(model: &str) -> bool {
model
.trim()
.strip_prefix("openai/")
.unwrap_or_else(|| model.trim())
.to_ascii_lowercase()
.starts_with("gpt-5")
}
fn openai_chat_reasoning_effort(model: &str, thinking_enabled: bool) -> Option<&'static str> {
let normalized = model
.trim()
@@ -2594,6 +2613,63 @@ mod tests {
assert_eq!(body["temperature"], json!(DEFAULT_TEMPERATURE));
}
#[test]
fn chat_body_omits_temperature_for_openai_gpt5_family() {
for model in ["gpt-5", "gpt-5-mini", "gpt-5-nano", "gpt-5.5", "openai/gpt-5"] {
let provider = OpenAICompatibleLLMProvider::new(OpenAICompatibleConfig::new(
"openai",
"OpenAI",
"https://api.openai.com/v1",
"k",
model,
));
let body = provider.chat_body(false, vec![json!({ "role": "user", "content": "hi" })]);
assert!(
body.get("temperature").is_none(),
"{model} must not receive temperature (issue #857)"
);
}
}
#[test]
fn chat_body_keeps_default_temperature_for_openai_non_gpt5_models() {
for model in ["gpt-4o", "gpt-4o-mini", "gpt-4.1"] {
let provider = OpenAICompatibleLLMProvider::new(OpenAICompatibleConfig::new(
"openai",
"OpenAI",
"https://api.openai.com/v1",
"k",
model,
));
let body = provider.chat_body(false, vec![json!({ "role": "user", "content": "hi" })]);
assert_eq!(body["temperature"], json!(DEFAULT_TEMPERATURE));
}
}
#[test]
fn chat_body_keeps_custom_temperature_for_gpt5_on_custom_provider() {
// custom 预设由用户显式配温度(issue #857 的绕过路径:custom + temperature=1),
// 不该被内置渠道的 gpt-5 特判误伤。
let provider = OpenAICompatibleLLMProvider::new(
OpenAICompatibleConfig::new(
"custom",
"Custom",
"https://api.openai.com/v1",
"k",
"gpt-5",
)
.with_temperature(Some(1.0)),
);
let body = provider.chat_body(false, vec![json!({ "role": "user", "content": "hi" })]);
assert_eq!(body["temperature"], json!(1.0));
}
#[test]
fn provider_temperature_policy_makes_custom_opt_in() {
assert_eq!(