mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-22 15:15:18 +00:00
* fix(anthropic): don't strip a trailing tool_use with no subsequent message Fixes #8159 cleanOrphanedToolCalls treated any assistant tool_use with no matching tool_result as an orphan and stripped it -- including a tool_use in the very last message, where there is no subsequent message to have found a result in yet. "No result yet" is not the same as "no result ever": the tool may simply not have finished executing, or the conversion may be happening for a reason other than sending the completed turn to Anthropic (token counting, a resumed/replayed session snapshot, a retry issued before tool execution completes, ...). Silently deleting a currently-active tool_use corrupts the assistant's most recent turn, and the damage compounds when that turn also carries a signed extended-thinking block: the block's signature is computed over the full sibling content of the turn, so removing the tool_use next to it invalidates the signature and replaying the mutated turn produces Anthropic 400 "thinking blocks in the latest assistant message cannot be modified" -- a genuinely confusing error for something the client did to its own outgoing request. Fix: when an assistant tool_use is in the last message of the array (no message follows it at all), treat its tool_use blocks as valid/unresolved rather than scanning for a match that can't exist yet. A tool_use is only condemned as orphaned when a subsequent message was actually scanned and found to lack a matching tool_result. Also fixed one existing test that unintentionally pinned the buggy behavior as expected output ("cleans orphaned tool_use blocks without matching tool_result" used a fixture with no subsequent message at all, which is the trailing case, not a genuine orphan) -- added a real next message with unrelated content so it now exercises an actual orphan. Added a new regression test for the trailing case. Verification: - New/updated unit tests in converter.test.ts (73 tests, was 72). - Full anthropicContentGenerator/ suite: 193 tests pass. - tsc --noEmit -p packages/core/tsconfig.json and eslint clean for touched files. * fix(anthropic): sanitize tool_use.id / tool_result.tool_use_id Fixes #8160 The Anthropic converter passed Part.functionCall.id / FunctionResponse.id straight through as tool_use.id / tool_result.tool_use_id with only an empty-string-style fallback, never validating or sanitizing against Anthropic's accepted character set. Anthropic validates both fields against ^[a-zA-Z0-9_-]+$ server-side and rejects anything else -- including the empty string, since `+` requires at least one character. functionCall.id is the Gemini lingua-franca's own id field with no such constraint, so it can legitimately carry another provider's id scheme, a composite/namespaced id, or be entirely absent. Live-verified against the real Anthropic Messages API (via our corporate LiteLLM proxy in front of Vertex, model claude-sonnet-4-6): - A tool_use.id containing characters outside [a-zA-Z0-9_-] (e.g. `call:abc.def/ghi?jkl`) 400s: "String should match pattern '^[a-zA-Z0-9_-]+$'". - An empty-string tool_result.tool_use_id (the exact fallback the old code emitted) 400s the same way. - The sanitized replacement (`call_abc_def_ghi_jkl`) round-trips successfully (HTTP 200) with the pair still linked. Fix: resolveToolUseId() sanitizes non-conforming characters to `_`, generates a real fallback id (never an empty string) when the source is missing, and memoizes the source id -> sanitized id mapping per request (toolIdMap) so a tool_use/tool_result pair sharing a source id still resolves to the same wire id after sanitization. Uniqueness is enforced across the whole request (usedToolIds) rather than the previous per-Content-object counter, which fixes a latent collision: two different Content objects each missing an id previously both started their local counter at 0, producing duplicate fallback ids like `tool_0` in two different assistant turns. State is scoped to a single convertGeminiRequestToAnthropic call (reset via resetToolIdState) since the converter instance is long-lived across requests (constructed once per generator, not once per call). Also merges in the trailing-tool_use fix from #8163 (fixes #8159): one of the new tests here needs the model's missing-id tool_use to survive as the final message in order to observe the generated fallback id, which depends on that fix. This PR's net diff will shrink to just the sanitization change once #8163 merges. Verification: - New tests in converter.test.ts covering: bad-char sanitization with linkage preserved, non-empty fallback generation (not empty string), no id collisions across two missing-id calls in one request, and same-source-id consistency across tool_use/tool_result in different messages. - Full anthropicContentGenerator/ suite: 197 tests pass. - Live proxy verification as described above. - tsc --noEmit -p packages/core/tsconfig.json and eslint clean for touched files. --------- Co-authored-by: Palanisamy, Dinesh <Dinesh.Palanisamy@netapp.com> |
||
|---|---|---|
| .. | ||
| acp-bridge | ||
| audio-capture | ||
| channels | ||
| chrome-extension | ||
| cli | ||
| core | ||
| cua-driver | ||
| desktop | ||
| mobile-mcp | ||
| sdk-java | ||
| sdk-python | ||
| sdk-typescript | ||
| vscode-ide-companion | ||
| web-shell | ||
| web-templates | ||
| webui | ||
| zed-extension | ||