Skip to content

fix(gemini): preserve assistant tool calls during history conversion#8742

Closed
Rat0323 wants to merge 5 commits into
AstrBotDevs:masterfrom
Rat0323:fix/gemini-thinking-conflict
Closed

fix(gemini): preserve assistant tool calls during history conversion#8742
Rat0323 wants to merge 5 commits into
AstrBotDevs:masterfrom
Rat0323:fix/gemini-thinking-conflict

Conversation

@Rat0323

@Rat0323 Rat0323 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes Gemini history conversion when an assistant message contains both normal content and tool_calls.

Previously, _prepare_conversation() handled assistant messages with mutually exclusive if / elif / elif branches. If an assistant message had text content, or list content such as a think/text part, the tool_calls branch was skipped. The next Gemini request could then contain a tool response without the corresponding assistant function-call part, which can make Gemini repeat the same tool call or request invalid tool names in later turns.

Changes

  • Preserve assistant text/list content and function calls in the same ModelContent.parts list.
  • Treat tool_calls: null as empty, so OpenAI-style assistant messages with a null field do not crash local conversion.
  • Add regression tests for:
    • assistant text content + tool_calls
    • assistant list/think content + tool_calls
    • tool_calls: null

Notes

This PR was narrowed after re-checking the evidence. The earlier wording about a universal Gemini thinking_config + tools incompatibility was too broad, so that part has been removed from the patch and from the main claim here. This PR now focuses on the confirmed serialization bug.

Verification

uv run python -m pytest tests/test_gemini_source.py -q
uv run ruff check astrbot/core/provider/sources/gemini_source.py tests/test_gemini_source.py

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Jun 12, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Gemini provider source to automatically disable the thinking configuration when tools are active, preventing API errors since Gemini does not support both simultaneously. The reviewer suggested adding a warning log when this automatic disabling occurs to improve observability and prevent confusion, which is a valuable improvement.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +297 to +298
if tool_list:
thinking_config = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When thinking_config is automatically disabled due to the presence of tools, it is highly recommended to log a warning message. This provides crucial observability for users and developers, preventing confusion as to why the model's thinking/reasoning mode is not active when plugins or tools are triggered.

Suggested change
if tool_list:
thinking_config = None
if tool_list and thinking_config:
logger.warning("[Gemini] Thinking config is automatically disabled because tools are active for this request.")
thinking_config = None

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Given that tool_list may be None or different iterable types over time, consider making the condition more explicit (e.g., if tool_list is not None and len(tool_list) > 0) to avoid surprising behavior if tool_list changes shape in future refactors.
  • Since this override changes user-visible behavior when tools are present, it might be safer to route this through a small helper (e.g., disable_thinking_if_tools) so the intent is centralized and future changes to tool handling don’t accidentally bypass this constraint.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Given that `tool_list` may be `None` or different iterable types over time, consider making the condition more explicit (e.g., `if tool_list is not None and len(tool_list) > 0`) to avoid surprising behavior if `tool_list` changes shape in future refactors.
- Since this override changes user-visible behavior when tools are present, it might be safer to route this through a small helper (e.g., `disable_thinking_if_tools`) so the intent is centralized and future changes to tool handling don’t accidentally bypass this constraint.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Rat0323 Rat0323 force-pushed the fix/gemini-thinking-conflict branch from 54c1a72 to 851b526 Compare June 12, 2026 13:58
@Rat0323 Rat0323 changed the title fix(gemini): automatically disable thinking config when tools are present fix(gemini): resolve thinking-tool conflict and assistant tool-call loop Jun 12, 2026
@Soulter

Soulter commented Jun 13, 2026

Copy link
Copy Markdown
Member

是否有相关文档链接,以及报错日志?》

@Rat0323

Rat0323 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the latest revision.

This earlier comment reflected the initial hypothesis that explicit thinking_config and tools were universally incompatible. After re-checking the evidence and running a minimal reproduction, I narrowed this PR and removed that claim from the patch.

The current confirmed fix is focused on Gemini history conversion: assistant messages that contain both content and tool_calls must serialize both the text/list content and the function-call parts. See the updated PR description and the latest comment for the current scope.

@Rat0323

Rat0323 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Updated validation note.

The useful part of the earlier log review is the pre-patch tool-loop evidence: in a PR-summary request, Gemini returned assistant content together with a tool call, then later turns repeated get_pull_request_files and eventually requested invalid names such as github.get_pull_request_files / github_get_pull_request_files.

After narrowing the PR, I no longer use these logs as evidence for a universal thinking_config + tools incompatibility. The confirmed root cause covered by this PR is the old if / elif / elif history conversion dropping tool_calls whenever assistant content was also present.

The latest revision adds focused regression tests for assistant text/list content with tool_calls, plus tool_calls: null compatibility.

@Rat0323

Rat0323 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the latest revision.

This note referred to the earlier broader patch. The current PR no longer claims a universal Gemini thinking/tools conflict and is now verified with focused unit tests for Gemini history conversion.

@dosubot dosubot Bot mentioned this pull request Jun 14, 2026
2 tasks
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jun 14, 2026
@Rat0323 Rat0323 changed the title fix(gemini): resolve thinking-tool conflict and assistant tool-call loop fix(gemini): preserve assistant tool calls during history conversion Jun 14, 2026
@Rat0323

Rat0323 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Updated this PR after re-checking the evidence and running a minimal reproduction against the PR base.

The confirmed issue is Gemini history conversion: when an assistant message contains both content and tool_calls, the previous mutually exclusive branches serialized only the content and dropped the function-call parts. Later turns could then include tool responses without the matching assistant function calls, which explains the repeated/invalid tool-call behavior observed in the logs.

I also re-tested explicit thinking_config + tools in my current Gemini-compatible environment and could not reproduce a universal server-side incompatibility, so I removed that part from this patch and no longer present it as the main root cause.

This revision now focuses on the verified serialization bug and adds regression tests for assistant text/list content with tool_calls, plus tool_calls: null compatibility.

Verification:

uv run python -m pytest tests/test_gemini_source.py -q
uv run ruff check astrbot/core/provider/sources/gemini_source.py tests/test_gemini_source.py

@Rat0323 Rat0323 force-pushed the fix/gemini-thinking-conflict branch from bb8b34e to 413d4b9 Compare June 14, 2026 22:30
@Rat0323

Rat0323 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

CI note for the latest revision:

The current failing Unit Tests / Run pytest suite check is not from the Gemini changes in this PR. In the failed run, tests/test_gemini_source.py passed, and the two failures are both in tests/test_fastapi_v1_dashboard.py:

  • test_v1_openapi_alias_websocket_routes_are_mounted
  • test_dashboard_config_aliases_are_registered_on_fastapi

This PR currently only changes:

  • astrbot/core/provider/sources/gemini_source.py
  • tests/test_gemini_source.py

After comparing the latest green master run and this failed PR run, the important difference appears to be dependency resolution timing. The green master CI run installed fastapi==0.136.3, while this PR run installed fastapi==0.137.0 because the repository does not commit a lockfile for CI. With FastAPI 0.137.0, included routers can appear as _IncludedRouter wrappers in app.router.routes; these tests currently inspect top-level route objects directly and fail because those wrappers do not expose .path and websocket paths are no longer visible through that old enumeration method.

So I am keeping this PR focused on the Gemini history-conversion regression instead of adding unrelated dashboard/test fixes here. I also tried to rerun the failed check, but GitHub requires repository admin permission for that action.

@Rat0323

Rat0323 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Closing this in favor of #8787. The original hypothesis in this PR was narrowed after validation, so I opened a clean replacement PR with a single focused commit for the confirmed Gemini history-conversion bug.

@Rat0323 Rat0323 closed this Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants