Skip to content

fix(agent): include name field on every role:tool message for Gemini compatibility#16482

Closed
0xsir0000 wants to merge 1 commit into
NousResearch:mainfrom
0xsir0000:fix/gemini-tool-message-name
Closed

fix(agent): include name field on every role:tool message for Gemini compatibility#16482
0xsir0000 wants to merge 1 commit into
NousResearch:mainfrom
0xsir0000:fix/gemini-tool-message-name

Conversation

@0xsir0000

Copy link
Copy Markdown
Contributor

What does this PR do?

Gemini's OpenAI-compatibility endpoint strictly requires the `name` field on `role: tool` messages — it returns HTTP 400 ("Request contains an invalid argument") when the function name is missing. OpenAI/Anthropic/ollama tolerate the absence, so the gap stays invisible until the conversation accumulates a tool-result turn and the user routes it through Gemini (direct API or via ollama-cloud proxy).

This PR adds a `_get_tool_call_name_static()` helper alongside the existing `_get_tool_call_id_static()`, and populates `name` at every site in `run_agent.py` that constructs a `role: tool` message:

# Site What it produces
1 Pre-call sanitizer stub (4711) "[Result unavailable]" filler for missing tool result
2 tool-call args repair marker (8055) corruption marker injection
3 Parallel exec — interrupt skip (8377) per-tool cancellation skip
4 Parallel exec — tool result (8642) normal tool result
5 Parallel exec — cross-call interrupt (8679) post-cancel skip for remaining calls
6 Sequential exec — tool result (9006) normal tool result
7 Sequential exec — cross-call interrupt (9032) post-cancel skip for remaining calls
8 Invalid tool-name recovery (11837) "Tool 'X' does not exist"
9 Invalid JSON-args recovery (11928) "Invalid JSON arguments"
10 Exception fallback (12413) synthetic error result for orphaned tool_calls

Each call site was already in scope of the function name (`function_name`, `skipped_name`, `name`, or a dict tool_call), so the change is local — no new lookups, no behavior change for providers that already worked.

Related Issue

Fixes #16478

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • `run_agent.py` — new `_get_tool_call_name_static()` helper + `name` field added to all 10 `role: tool` construction sites.
  • `tests/run_agent/test_agent_guardrails.py` — 6 new helper tests (`TestGetToolCallNameStatic`).
  • `tests/run_agent/test_tool_call_args_sanitizer.py` — updated `test_marker_message_inserted_when_missing` to assert the new `name` field.

How to Test

  1. Configure a Gemini model as primary or fallback (e.g. `gemini-2.5-flash` via direct Gemini API, or `gemini-3-flash-preview` via ollama-cloud).
  2. Send a message that triggers a tool call (e.g. ask it to read a URL via `web_extract`).
  3. Continue the conversation through several tool rounds.
  4. Before this PR: `Error code: 400 - Request contains an invalid argument`. After: tool turns succeed.

Automated:
```bash
pytest tests/run_agent/test_agent_guardrails.py tests/run_agent/test_tool_call_args_sanitizer.py

42 passed

```
`tests/run_agent/` overall: 555 passed (one pre-existing flaky thread-timing failure unrelated to this change).

Notes

The helper docstring spells out that `name` is best-effort: when the upstream tool_call genuinely has no `function.name` (corrupt history, dict without function key), the helper returns `""` and OpenAI/Anthropic/ollama still accept it. Gemini will only reject when both the field is empty and the conversation is being replayed there — which is the same failure mode as today.

A future cleanup could push `name` injection into a single `_make_tool_message()` constructor, but the spread-out call sites were left alone here to keep the diff a one-line addition per site.

…compatibility (NousResearch#16478)

Gemini's OpenAI-compatibility endpoint strictly requires the `name` field
on `role: tool` messages — it returns HTTP 400 ("Request contains an
invalid argument") when the function name is missing. OpenAI/Anthropic/
ollama tolerate the absence, so the gap stays invisible until the
conversation accumulates a tool turn and the user routes it through Gemini
(direct API or via ollama-cloud proxy).

Fix: add a `_get_tool_call_name_static()` helper alongside the existing
`_get_tool_call_id_static()`, and populate `name` at every site that
constructs a `role: tool` message — the pre-call sanitizer stub, the
tool-call args repair marker, both interrupt-skip paths, both
result-append paths (parallel + sequential), the invalid-tool-name
recovery, the invalid-JSON-args recovery, and the exception fallback.

Each call site was already in scope of the function name (`function_name`,
`skipped_name`, `name`, or a dict tool_call), so the change is local —
no new lookups, no behavior change for providers that already worked.

Fixes NousResearch#16478
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder provider/gemini Google Gemini (AI Studio, Cloud Code) labels Apr 27, 2026
@teknium1

teknium1 commented May 4, 2026

Copy link
Copy Markdown
Contributor

Salvaged via #19728 onto current main - your commit authorship was preserved. Thanks!

@teknium1 teknium1 closed this May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P2 Medium — degraded but workaround exists provider/gemini Google Gemini (AI Studio, Cloud Code) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix: Missing name field in tool messages causes Gemini API HTTP 400

3 participants