Skip to content

fix(chat_completions): strip internal tool_name from messages for strict providers#29126

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-bcbf4d86
May 20, 2026
Merged

fix(chat_completions): strip internal tool_name from messages for strict providers#29126
teknium1 merged 2 commits into
mainfrom
hermes/hermes-bcbf4d86

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvages #28958 by @savanne-kham onto current main, with a broader docstring describing the bigger user impact than the original PR captured.

What this fixes

Community report (porkmagus, May 19): every API call after the first tool-call dies with HTTP 400 on Fireworks/Firepass, Moonshot/Kimi, and other strict OpenAI-compatible providers:

Extra inputs are not permitted, field: 'messages[N].tool_name', value: 'skill_view'

Repros from a fresh session — the first turn that triggers any tool kills the conversation; user has to restart hermes.

Root cause

agent/tool_dispatch_helpers.py make_tool_result_message() deliberately attaches tool_name to every tool-result dict so run_agent._flush_messages_to_session_db() can persist it for SQLite FTS indexing. That field is not part of the OpenAI Chat Completions schema and rides into the next-turn wire payload.

Permissive providers (OpenRouter, MiniMax) silently dropped the extra field, masking the bug. Strict providers (Fireworks, Moonshot/Kimi) reject the entire request.

Changes

  • agent/transports/chat_completions.py — extend the existing needs_sanitize detection and per-message pop step to cover tool_name, same pattern already used for codex_reasoning_items / codex_message_items. Broadened the docstring to explicitly name which providers reject it and why permissive providers masked the bug.
  • tests/agent/transports/test_chat_completions.py — new test_convert_messages_strips_tool_name covers strip + deepcopy-on-demand contract (FTS-side tool_name must survive on the original message).
  • scripts/release.py — AUTHOR_MAP entry for savanne.kham@protonmail.com.

Validation

Before After
Fireworks/Moonshot tool turn HTTP 400 tool_name not permitted succeeds
In-memory message tool_name preserved preserved (deepcopy-on-demand)
Session DB FTS index populated populated
transport tests 66/66 67/67

E2E verified via direct convert_messages() call on a realistic tool-result message: tool_name is absent from the serialized payload, but the original message dict retains it.

Closes #28958.

Co-authored-by: Savanne Kham savanne.kham@protonmail.com

savanne-kham and others added 2 commits May 19, 2026 23:08
…ders

The 'tool_name' key on role=tool messages is an internal Hermes field
(stored in the messages.tool_name SQLite column for FTS indexing) that
is not part of the OpenAI Chat Completions schema. Strict OpenAI-compatible
providers — notably Moonshot AI (Kimi) — reject it with HTTP 400:

  Error from provider: Extra inputs are not permitted,
  field: 'messages[N].tool_name', value: 'execute_code'

Add 'tool_name' to the sanitize block in ChatCompletionsTransport.convert_messages
alongside the existing Codex Responses API fields (codex_reasoning_items,
codex_message_items) so it is popped before the request is sent.

Reproducer:
  hermes chat --model kimi-k2.6
  > list the top 5 Hacker News stories
  -> assistant emits tool_call(execute_code)
  -> tool result message gets tool_name='execute_code'
  -> next turn's payload includes messages[N].tool_name -> 400

Permissive backends (MiniMax, OpenRouter on most routes) ignore the extra
field and were masking the bug.
Salvage follow-up to PR #28958 (savanne-kham):

- convert_messages() docstring now explicitly documents the tool_name strip
  alongside Codex fields, names which providers reject it (Fireworks,
  Moonshot/Kimi), and why permissive providers (OpenRouter, MiniMax)
  masked the bug.
- AUTHOR_MAP entry for savanne.kham@protonmail.com -> savanne-kham.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-bcbf4d86 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: 8973 on HEAD, 8973 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 4730 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 P2 Medium — degraded but workaround exists labels May 20, 2026
@teknium1 teknium1 merged commit 42c4288 into main May 20, 2026
20 of 21 checks passed
@teknium1 teknium1 deleted the hermes/hermes-bcbf4d86 branch May 20, 2026 09:44
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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants