Skip to content

fix: strip extra_content from tool_calls for strict APIs (Fireworks, Mistral)#38543

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-17044901
Jun 3, 2026
Merged

fix: strip extra_content from tool_calls for strict APIs (Fireworks, Mistral)#38543
teknium1 merged 1 commit into
mainfrom
hermes/hermes-17044901

Conversation

@teknium1

@teknium1 teknium1 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Fireworks and Mistral no longer 400 on sessions that have prior Gemini tool calls in history.

Root cause: Gemini 3 thinking models attach extra_content (thought_signature) to tool_calls for replay. Both sanitize paths only stripped call_id/response_item_id, so extra_content survived to the wire and strict providers rejected the request with Extra inputs are not permitted, field: 'messages[N].tool_calls[M].extra_content'.

Salvages @nateGeorge's #18663 onto current main, with the unconditional strip made model-aware so it doesn't regress Gemini-on-aggregator.

Changes

  • agent/transports/chat_completions.py: convert_messages() strips extra_content from tool_calls — gated on _model_consumes_thought_signature(model). Model threaded in from build_kwargs.
  • run_agent.py: _sanitize_tool_calls_for_strict_api() takes a model arg; strips extra_content unless the target is Gemini-family (defaults to stripping when the model is unknown).
  • agent/conversation_loop.py, agent/chat_completion_helpers.py: pass model=agent.model to the sanitizer.
  • Tests: transport + parity coverage for strip-for-strict / keep-for-Gemini / strip-on-unknown-model / mixed-session stale-sig.

Why model-aware (not the original unconditional strip)

extra_content is required when the destination is Gemini (thinking models 400 without the replayed signature). Stripping it for all non-Codex providers would trade a Fireworks 400 for a Gemini-on-OpenRouter 400. So: keep for Gemini-family targets, strip for everyone else. Native Gemini is unaffected — it uses GeminiNativeClient and bypasses these paths entirely.

Validation

Target model extra_content on wire History preserved
Fireworks / llama-v3p1-70b stripped yes
google/gemini-3-pro-preview kept yes
mistral-large (stale Gemini sig) stripped yes

165 targeted tests pass (tests/agent/transports/test_chat_completions.py, tests/run_agent/test_provider_parity.py). E2E verified through _build_api_kwargs with isolated HERMES_HOME for all three cases above.

Salvages #18663 — original author @nateGeorge credited via commit authorship.
Fixes #17986.

Infographic

strip-extra-content-strict-apis

…Mistral)

Fireworks/Mistral reject HTTP 400 'Extra inputs are not permitted, field:
messages[N].tool_calls[M].extra_content' on any session whose history
contains prior Gemini tool calls. Gemini 3 thinking models attach
extra_content (thought_signature) to tool_calls; it survived to the wire
because the sanitize paths only stripped call_id/response_item_id.

Strip extra_content from the outgoing wire copy in both sanitize paths
(ChatCompletionsTransport.convert_messages + _sanitize_tool_calls_for_strict_api),
but gate it on the target model: keep extra_content for Gemini-family
targets (the thought_signature MUST be replayed or Gemini 400s), strip it
for everyone else — including non-Gemini models that inherit a stale Gemini
signature earlier in a mixed-provider session. Native Gemini is unaffected
(GeminiNativeClient bypasses these paths).

Original stored history is never mutated (only the per-call copy).

Fixes #17986.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-17044901 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9744 on HEAD, 9744 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5047 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent loop, run_agent.py, prompt builder comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists labels Jun 3, 2026
@teknium1 teknium1 merged commit e8c3ac2 into main Jun 3, 2026
23 checks passed
@teknium1 teknium1 deleted the hermes/hermes-17044901 branch June 3, 2026 23:42
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 comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Title: HTTP 400 on Fireworks custom endpoint — first turn only, fallback succeeds

3 participants