fix: wrap plain-text tool results as JSON for Gemini compatibility#7216
fix: wrap plain-text tool results as JSON for Gemini compatibility#7216he-yufeng wants to merge 1 commit intoAstrBotDevs:masterfrom
Conversation
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 AstrBotDevs#7134
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The Gemini
function_responsepath currently only wraps non-JSON strings, but valid JSON that is not an object (e.g. a number, string, or array) will pass through unchanged and still violate theStructrequirement; consider checking thatjson.loads(content)specifically yields adictand wrapping any other JSON types as{"result": content}. - Model detection uses a substring check (
"gemini" in model), which may accidentally trigger for third-party models withgeminiin their name; if feasible, tightening this to known Gemini model prefixes or an explicit provider flag would make the behavior more predictable.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The Gemini `function_response` path currently only wraps non-JSON strings, but valid JSON that is not an object (e.g. a number, string, or array) will pass through unchanged and still violate the `Struct` requirement; consider checking that `json.loads(content)` specifically yields a `dict` and wrapping any other JSON types as `{"result": content}`.
- Model detection uses a substring check (`"gemini" in model`), which may accidentally trigger for third-party models with `gemini` in their name; if feasible, tightening this to known Gemini model prefixes or an explicit provider flag would make the behavior more predictable.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request updates the _finally_convert_payload method in openai_source.py to ensure that tool responses for Gemini models are correctly formatted as JSON objects, preventing 400 errors caused by plain text responses. The review feedback suggests refining the validation logic to ensure the content is specifically a JSON dictionary, as json.loads might successfully parse non-object values (like numbers or strings) that would still be rejected by the Gemini API.
| try: | ||
| json.loads(content) | ||
| except (json.JSONDecodeError, ValueError): | ||
| message["content"] = json.dumps( | ||
| {"result": content}, ensure_ascii=False | ||
| ) |
There was a problem hiding this comment.
Gemini 的 function_response 明确要求是一个 JSON 对象(对应 google.protobuf.Struct)。虽然 json.loads() 可以验证字符串是否为合法的 JSON,但它不能保证解析后是一个对象。例如,如果工具返回的是纯数字 123 或 JSON 字符串 "success",json.loads() 会成功解析,但如果代理层没有自动包装,可能仍然会导致 400 错误。建议检查解析后的结果是否为字典类型。
| try: | |
| json.loads(content) | |
| except (json.JSONDecodeError, ValueError): | |
| message["content"] = json.dumps( | |
| {"result": content}, ensure_ascii=False | |
| ) | |
| try: | |
| if not isinstance(json.loads(content), dict): | |
| raise ValueError() | |
| except (json.JSONDecodeError, ValueError): | |
| message["content"] = json.dumps( | |
| {"result": content}, ensure_ascii=False | |
| ) |
Summary
Gemini API 的
function_response要求google.protobuf.Struct(即 JSON 对象),但当工具(如内置知识库)返回纯文本结果时,框架直接把字符串传给 API,导致 400 Invalid argument。在
_finally_convert_payload中检测 Gemini 模型的 tool 消息,如果 content 不是合法 JSON,自动包装成{"result": content}后再发送。已有的 JSON 格式内容不受影响。Changes
openai_source.py:_finally_convert_payload()增加 Gemini tool content JSON 包装逻辑Test
Fixes #7134
Summary by Sourcery
Bug Fixes: