Skip to content

Commit 3d4c5a6

Browse files
fix: 增加 Moonshot 降级回退日志与可观测性测试
1 parent a0fc2b4 commit 3d4c5a6

2 files changed

Lines changed: 73 additions & 5 deletions

File tree

astrbot/core/provider/sources/openai_source.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,14 +1104,20 @@ async def _handle_api_error(
11041104
"invalid_attachment",
11051105
image_fallback_used=True,
11061106
)
1107-
if (
1108-
payloads.get("tool_choice") == "required"
1107+
required_tool_choice = payloads.get("tool_choice") == "required"
1108+
thinking_conflict_error = (
1109+
required_tool_choice
11091110
and self._is_tool_choice_required_incompatible_with_thinking_error(e)
1110-
):
1111+
)
1112+
if thinking_conflict_error:
11111113
provider_name = self.provider_config.get("provider", "unknown")
1114+
logger.info(
1115+
"触发工具调用策略降级:检测到 thinking 模式不兼容,"
1116+
f"provider={provider_name},tool_choice=required->auto"
1117+
)
11121118
logger.warning(
1113-
"Detected `tool_choice=required` incompatible with thinking mode. "
1114-
f"Downgrading to `auto` and retrying. provider={provider_name}"
1119+
"检测到 `tool_choice=required` thinking 模式不兼容,"
1120+
f"自动降级为 `auto` 并重试,provider={provider_name}"
11151121
)
11161122
retry_payloads = {**payloads, "tool_choice": "auto"}
11171123
return (
@@ -1123,6 +1129,20 @@ async def _handle_api_error(
11231129
func_tool,
11241130
image_fallback_used,
11251131
)
1132+
if required_tool_choice:
1133+
candidates = [
1134+
candidate.lower()
1135+
for candidate in self._extract_error_text_candidates(e)
1136+
]
1137+
has_related_keywords = any(
1138+
"tool_choice" in candidate and "thinking" in candidate
1139+
for candidate in candidates
1140+
)
1141+
if has_related_keywords:
1142+
logger.debug(
1143+
"检测到 tool_choice/thinking 相关错误,但未命中降级模式,"
1144+
"保持 tool_choice=required"
1145+
)
11261146

11271147
if (
11281148
"Function calling is not enabled" in str(e)

tests/test_openai_source.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,54 @@ async def test_handle_api_error_required_thinking_conflict_uses_payload_copy():
675675
await provider.terminate()
676676

677677

678+
@pytest.mark.asyncio
679+
async def test_handle_api_error_required_thinking_conflict_emits_fallback_log(
680+
monkeypatch,
681+
):
682+
provider = _make_provider({"provider": "moonshot"})
683+
try:
684+
payloads = {
685+
"tool_choice": "required",
686+
"messages": [{"role": "user", "content": [{"type": "text", "text": "hi"}]}],
687+
}
688+
context_query = payloads["messages"]
689+
err = _ErrorWithBody(
690+
"upstream error",
691+
{
692+
"error": {
693+
"message": "tool_choice 'required' is incompatible with thinking enabled",
694+
"type": "invalid_request_error",
695+
}
696+
},
697+
)
698+
699+
info_logs: list[str] = []
700+
701+
def _capture_info(message, *args, **kwargs):
702+
del args, kwargs
703+
info_logs.append(str(message))
704+
705+
monkeypatch.setattr(
706+
"astrbot.core.provider.sources.openai_source.logger.info",
707+
_capture_info,
708+
)
709+
710+
await provider._handle_api_error(
711+
err,
712+
payloads=payloads,
713+
context_query=context_query,
714+
func_tool=None,
715+
chosen_key="test-key",
716+
available_api_keys=["test-key"],
717+
retry_cnt=0,
718+
max_retries=10,
719+
)
720+
721+
assert any("tool_choice=required->auto" in log for log in info_logs)
722+
finally:
723+
await provider.terminate()
724+
725+
678726
@pytest.mark.asyncio
679727
async def test_prepare_chat_payload_materializes_context_http_image_urls(monkeypatch):
680728
provider = _make_provider()

0 commit comments

Comments
 (0)