Skip to content

fix: keep Gemini functionResponse name/id paired - #9761

Open
wcqqq1214 wants to merge 4 commits into
AstrBotDevs:masterfrom
wcqqq1214:fix/9760-gemini-function-response-name-id
Open

wcqqq1214 wants to merge 4 commits into
AstrBotDevs:masterfrom
wcqqq1214:fix/9760-gemini-function-response-name-id

Conversation

@wcqqq1214

@wcqqq1214 wcqqq1214 commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #9760.

Gemini tool-result messages only carry tool_call_id, so _prepare_conversation was using that id as functionResponse.name. Strict Gemini backends then reject the follow-up request (functionResponse.name "call_176130" does not match functionCall.name "gemini_search"), and they also require functionResponse.id when the matching functionCall has a distinct id.

A second failure showed up on the same follow-up path: an empty assistant content was serialized as an empty text part (contents[n].parts[0].data: required oneof field 'data' must have one initialized field). Tool-only first turns hit this often.

Modifications / 改动点

Verification Steps / 验证步骤

  1. Ran ruff format --check and ruff check on astrbot/core/provider/sources/gemini_source.py.
  2. Ran pytest tests/test_gemini_source.py (3 passed).
  3. Exercised _prepare_conversation locally for:
    • the reported call_176130 / gemini_search pairing
    • a tool message appearing before its assistant tool_calls
    • official Gemini id == name, which still omits id
  4. Live two-round tool call with gemini-3.7-flash on a backend that strictly validates function-call history:
    • Round 1: model called get_weather with a distinct id (call_1517).
    • Prepared contents paired functionCall / functionResponse as name=get_weather, id=call_1517, with no empty text part.
    • Round 2 text_chat with the tool result succeeded and used the tool output (30°C, sunny).
    • A no-tool prompt also succeeded.

Screenshots or Test Results / 运行截图或测试结果

tests/test_gemini_source.py ... 3 passed

Prepared Gemini contents for the reported shape:

function_call:     {id: call_176130, name: gemini_search, ...}
function_response: {id: call_176130, name: gemini_search, response: {name: gemini_search, content: ok}}

When tool_call_id equals the function name (get_weather), both parts omit id.

Live gemini-3.7-flash tool round-trip:

ROUND1  role=tool  name=get_weather  id=call_1517  args={city: Beijing}
PREPARED
  ModelContent  function_call:     {id: call_1517, name: get_weather, ...}
  UserContent   function_response: {id: call_1517, name: get_weather, response: {name: get_weather, content: "temperature=30C, condition=Sunny"}}
ROUND2  "The current weather in Beijing is sunny with a temperature of 30°C."

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txt 和 pyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Fix Gemini tool-call history serialization so function names and IDs remain correctly paired and tool-only turns are accepted.

Bug Fixes:

  • Preserve matching Gemini function names and distinct call IDs across function-call and function-response messages, preventing strict backends from rejecting tool-result follow-ups.
  • Avoid serializing empty assistant text parts that make tool-only Gemini turns invalid.

Tests:

  • Verify Gemini conversation preparation and tool round-trips, including reordered tool messages and official Gemini ID behavior.

@wcqqq1214
wcqqq1214 marked this pull request as ready for review August 21, 2026 10:36
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Aug 21, 2026

@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 reviewed your changes and they look great!


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.

@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.

Sourcery assessment

Approved.

buyun14 pushed a commit to buyun14/AstrBot that referenced this pull request Sep 3, 2026
buyun14 added a commit to buyun14/AstrBot that referenced this pull request Sep 3, 2026
- PluginPagePage.vue: drop import of usePluginSidebarItems removed by AstrBotDevs#9823
  (broke production build); remove dead sidebar heal writes from AstrBotDevs#9917
- test_session_waiter_cross_user.py: stub astrbot.core logger required by
  AstrBotDevs#9442; update default-filter guards to shipped sender-scoped semantics
- test_tool_calls_result_cross_provider.py: await async _prepare_conversation;
  assert AstrBotDevs#9761 functionResponse name/id pairing

@kilisamemarisaaa kilisamemarisaaa 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.

Reviewed the latest head 21358a8 against issue #9760. The implementation pre-indexes assistant tool_calls so opaque tool_call_id values resolve back to the actual function name, preserves distinct IDs on both functionCall and functionResponse for strict Gemini-compatible gateways, omits IDs when they equal the function name, and skips empty assistant text parts. The diff is limited to conversation serialization, and the focused Gemini tests plus the full cross-platform CI matrix are green. I found no remaining correctness blocker.

buyun14 pushed a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
buyun14 added a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
- PluginPagePage.vue: drop import of usePluginSidebarItems removed by AstrBotDevs#9823
  (broke production build); remove dead sidebar heal writes from AstrBotDevs#9917
- test_session_waiter_cross_user.py: stub astrbot.core logger required by
  AstrBotDevs#9442; update default-filter guards to shipped sender-scoped semantics
- test_tool_calls_result_cross_provider.py: await async _prepare_conversation;
  assert AstrBotDevs#9761 functionResponse name/id pairing
buyun14 pushed a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
buyun14 added a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
- PluginPagePage.vue: drop import of usePluginSidebarItems removed by AstrBotDevs#9823
  (broke production build); remove dead sidebar heal writes from AstrBotDevs#9917
- test_session_waiter_cross_user.py: stub astrbot.core logger required by
  AstrBotDevs#9442; update default-filter guards to shipped sender-scoped semantics
- test_tool_calls_result_cross_provider.py: await async _prepare_conversation;
  assert AstrBotDevs#9761 functionResponse name/id pairing

This branch has not been deployed

No deployments
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:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Gemini functionResponse 未正确保留 name/id,导致 cpa-Antigravity 工具调用失败

2 participants