From 06a7edba587a41392e40602c2cfb061a8f549bd6 Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:22:58 +0800 Subject: [PATCH] fix: wrap plain-text tool results as JSON for Gemini compatibility Gemini API requires function_response to be a google.protobuf.Struct (JSON object). When tool results are plain text strings, the API returns 400 Invalid argument. Detect non-JSON tool content for Gemini models and wrap it in {"result": content} before sending. Fixes #7134 --- astrbot/core/provider/sources/openai_source.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 39bfc69dbf..526eab36a6 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -839,6 +839,9 @@ async def _prepare_chat_payload( def _finally_convert_payload(self, payloads: dict) -> None: """Finally convert the payload. Such as think part conversion, tool inject.""" + model = payloads.get("model", "").lower() + is_gemini = "gemini" in model + for message in payloads.get("messages", []): if message.get("role") == "assistant" and isinstance( message.get("content"), list @@ -855,6 +858,18 @@ def _finally_convert_payload(self, payloads: dict) -> None: if reasoning_content: message["reasoning_content"] = reasoning_content + # Gemini 的 function_response 要求 google.protobuf.Struct(即 JSON 对象), + # 纯文本会触发 400 Invalid argument,需要包一层 JSON。 + if is_gemini and message.get("role") == "tool": + content = message.get("content", "") + if isinstance(content, str): + try: + json.loads(content) + except (json.JSONDecodeError, ValueError): + message["content"] = json.dumps( + {"result": content}, ensure_ascii=False + ) + async def _handle_api_error( self, e: Exception,