mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 14:24:15 +08:00
fix(ai): DeepSeek thinking 禁用参数被 400 拒绝时去参重试 (#240 跟进)
_openai_kwargs 对 DeepSeek V4 官方域名注入的 thinking 禁用参数 (extra_body) 属外部 API 契约, 模型/接口版本差异可能报 400。照 temperature / reasoning_effort 既有模式补第三条定向回退: 400 且 错误信息指向 thinking 时移除 extra_body 重建请求 (回退默认思考 模式; 若正文因此被推理挤占, 由 _iter_openai_text 显式报错指引)。 与 thinking 无关的 400 不触发回退, 直接抛出。
This commit is contained in:
@@ -504,6 +504,7 @@ def _openai_client(api_key: str, timeout: float):
|
||||
# 只在 400 明确指出对应参数时移除该参数并重试; 每个参数最多移除一次。
|
||||
_TEMP_REJECT_HINTS = ("temperature", "only 1 is allowed")
|
||||
_REASONING_EFFORT_REJECT_HINTS = ("reasoning_effort", "reasoning effort")
|
||||
_THINKING_BODY_REJECT_HINTS = ("thinking",)
|
||||
|
||||
|
||||
def _is_temperature_rejected(exc: Exception) -> bool:
|
||||
@@ -526,6 +527,16 @@ def _is_reasoning_effort_rejected(exc: Exception) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _is_thinking_body_rejected(exc: Exception) -> bool:
|
||||
"""True if the upstream 400 specifically rejects the thinking extra_body."""
|
||||
if getattr(exc, "status_code", None) != 400:
|
||||
return False
|
||||
text = _openai_error_detail(exc) or str(exc)
|
||||
return _openai_error_param(exc) == "thinking" or any(
|
||||
h in text.lower() for h in _THINKING_BODY_REJECT_HINTS
|
||||
)
|
||||
|
||||
|
||||
def _openai_error_param(exc: Exception) -> str:
|
||||
body = getattr(exc, "body", None)
|
||||
if not isinstance(body, dict):
|
||||
@@ -545,6 +556,11 @@ def _openai_retry_kwargs(exc: Exception, kwargs: dict) -> dict | None:
|
||||
if "reasoning_effort" in retry_kwargs and _is_reasoning_effort_rejected(exc):
|
||||
retry_kwargs.pop("reasoning_effort")
|
||||
return retry_kwargs
|
||||
if "extra_body" in retry_kwargs and _is_thinking_body_rejected(exc):
|
||||
# DeepSeek thinking 禁用参数被拒 (模型/API 版本差异): 回退默认思考模式
|
||||
# 重试; 报告若因此被推理挤占正文, 由 _iter_openai_text 显式报错。
|
||||
retry_kwargs.pop("extra_body")
|
||||
return retry_kwargs
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -198,6 +198,36 @@ def test_is_temperature_rejected_false_for_other_400():
|
||||
assert _is_temperature_rejected(exc) is False
|
||||
|
||||
|
||||
def test_thinking_body_rejected_falls_back_to_default_mode():
|
||||
"""DeepSeek thinking 禁用参数被 400 拒绝时, 去参重试而非直接失败。"""
|
||||
response = httpx.Response(
|
||||
400,
|
||||
json={"error": {"message": "unknown parameter: thinking"}},
|
||||
request=httpx.Request("POST", "https://api.deepseek.com/v1/chat/completions"),
|
||||
)
|
||||
exc = openai.BadRequestError(
|
||||
"bad request", response=response,
|
||||
body={"error": {"message": "unknown parameter: thinking"}},
|
||||
)
|
||||
kwargs = {"max_tokens": None, "extra_body": {"thinking": {"type": "disabled"}}}
|
||||
retry = ai_provider._openai_retry_kwargs(exc, kwargs)
|
||||
assert retry == {"max_tokens": None}
|
||||
# 原 kwargs 不被修改
|
||||
assert kwargs["extra_body"] == {"thinking": {"type": "disabled"}}
|
||||
|
||||
# 与 thinking 无关的 400 不触发该回退
|
||||
response_other = httpx.Response(
|
||||
400,
|
||||
json={"error": {"message": "model not found"}},
|
||||
request=httpx.Request("POST", "https://api.deepseek.com/v1/chat/completions"),
|
||||
)
|
||||
exc_other = openai.BadRequestError(
|
||||
"bad request", response=response_other,
|
||||
body={"error": {"message": "model not found"}},
|
||||
)
|
||||
assert ai_provider._openai_retry_kwargs(exc_other, kwargs) is None
|
||||
|
||||
|
||||
def test_is_temperature_rejected_false_for_non_400():
|
||||
response = httpx.Response(
|
||||
401,
|
||||
|
||||
Reference in New Issue
Block a user