Skip to content

fix(agent): handle duplicate tool call IDs in OpenAI-compatible transcripts#283

Merged
viettranx merged 2 commits into
nextlevelbuilder:mainfrom
badgerbees:fix/duplicate-tool-call-ids
Mar 19, 2026
Merged

fix(agent): handle duplicate tool call IDs in OpenAI-compatible transcripts#283
viettranx merged 2 commits into
nextlevelbuilder:mainfrom
badgerbees:fix/duplicate-tool-call-ids

Conversation

@badgerbees

Copy link
Copy Markdown
Contributor

The Issue

Several "OpenAI-compatible" APIs (including OpenRouter, vLLM, and certain local models) strictly enforce unique tool_call_ids across the entire conversation history. If a transcript contains duplicate IDs, the provider returns an HTTP 400 Bad Request, effectively breaking the session. This happens in two ways:

  1. Live collisions: The model returns two tool calls with the same ID in a single response (common in DeepSeek and certain open-source models).
  2. Historical reuse: The model happens to reuse an ID (e.g., call_abc) in a later turn that was already used in an earlier turn.

The Fix

This PR implements an "occurrence-aware" ID sanitization strategy to ensure transcript-level uniqueness without losing data.

  1. Occurrence-Aware Sanitization: Updated sanitizeHistory in internal/agent/loop_history.go to track how many times a raw ID has been seen in the transcript. Duplicate IDs are elegantly rewritten (e.g., id , id_1, id_2) while maintaining the correct pairing between assistant calls and tool results via encounter-order queues.
  2. Live ID Normalization: Added normalizeToolCallIDs in internal/agent/loop.go. This catches and fixes collisions immediately as they are returned by the LLM, ensuring that new messages join the history with unique IDs from the start.
  3. Deep-Copy Safety: Added deep-copy logic for ToolCalls slices during sanitization to avoid shared-memory side effects on the original session history object.
  4. Anthropic/Thinking Safe: Added a critical guard that skips ID rewriting if RawAssistantContent is present. This is essential for Anthropic Claude models (or any model using raw content passback) to avoid breaking the link between the provider's internal thinking blocks and tool results.

badgerbees and others added 2 commits March 19, 2026 23:10
Replace complex occurrence-aware queue system with simpler approach:
- uniquifyToolCallIDs: append runID+iteration+index to all IDs at
  response time, guaranteeing cross-turn uniqueness via UUID
- sanitizeHistory: simple seen-set dedup for legacy sessions with
  pre-existing duplicate IDs, no queue/counter logic needed
- Restore full problem description in sanitizeHistory docstring
- Add unit tests for both dedup paths and edge cases
pull Bot pushed a commit to Stars1233/goclaw that referenced this pull request Mar 19, 2026
…cripts

Simplified approach to fix duplicate tool_call_ids that cause HTTP 400 on OpenAI-compatible APIs (OpenRouter, vLLM, DeepSeek).

- uniquifyToolCallIDs: appends runID+iteration+index to all IDs at response time, guaranteeing cross-turn uniqueness via UUID
- sanitizeHistory: simple seen-set dedup for legacy sessions with pre-existing duplicate IDs
- Anthropic guard: skips ID rewriting when RawAssistantContent is present
- Unit tests for both paths + edge cases

Closes nextlevelbuilder#283
@viettranx viettranx merged commit 55bc752 into nextlevelbuilder:main Mar 19, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants