Repository navigation
fix(integrations): honor maxOutputTokens, set finishReason and sanitize tool ids on Claude - #1139
ScottMansfield wants to merge 1 commit into
Conversation
…ze tool ids on Claude ## Summary 9 files changed, 1501 insertions(+), 40 deletions(-) across `@google/adk-integrations`, `@google/adk`, and `docs/guides/integrations/models/anthropic_llm/index.md`. ### Problem `Claude` ignores `maxOutputTokens` on `LlmRequest.config` and sends a fixed `MAX_TOKEN` (`1024`) to `messages.create`. `messageToGenerateContentResponse` leaves `finishReason` unset on `LlmResponse` instead of mapping `message.stop_reason`. `ContentRequestProcessor` strips `adk-*` function call IDs on Claude turns, and `partToMessageBlock` forwards missing or invalid tool IDs unchanged. ### Solution Add `maxTokens` (default `8192`) to `ClaudeParams` and `Claude`, and prefer `llmRequest.config?.maxOutputTokens` over `this.maxTokens`. Map `message.stop_reason` through `toGoogleGenaiFinishReason` and set `finishReason` on `LlmResponse` in `messageToGenerateContentResponse`. Preserve function call IDs in `getContents` and `getCurrentTurnContents` for Claude models, and sanitize tool IDs with `ToolUseIdSanitizer`. ### Ported behaviors from `adk-python` (`v1.0.0` to `v2.13.0`) - Configurable `maxTokens` on `ClaudeParams` and `Claude` (default `8192`), overridden per request by `llmRequest.config?.maxOutputTokens`, while keeping `MAX_TOKEN = 1024` exported with `@deprecated`. - `toGoogleGenaiFinishReason` mapping all seven `Anthropic.StopReason` values (`end_turn`, `stop_sequence`, `tool_use`, `pause_turn`, `max_tokens`, `model_context_window_exceeded`, `refusal`) to `FinishReason` and setting `finishReason` on `LlmResponse`. - `preserveFunctionCallIds` in `getContents` and `getCurrentTurnContents` (enabled by `ContentRequestProcessor` for Claude models) and request-scoped `ToolUseIdSanitizer` in `partToMessageBlock` and `contentToMessageParam`. | Member | adk-python | adk-js | Why | | :--- | :--- | :--- | :--- | | Default max tokens | `AnthropicLlm.max_tokens: int = 8192` | `DEFAULT_MAX_TOKENS = 8192`, `Claude.maxTokens = 8192`, `MAX_TOKEN = 1024` (`@deprecated`) | Raises default output limit to 8192 while preserving the exported `MAX_TOKEN` symbol for existing callers | | Per-request max tokens | `llm_request.config.max_output_tokens or self.max_tokens` | `llmRequest.config?.maxOutputTokens ?? this.maxTokens` | Uses nullish coalescing so an explicit value overrides the instance default | | Stop reason mapping | `to_google_genai_finish_reason(anthropic_stop_reason)` | `toGoogleGenaiFinishReason(anthropicStopReason)` backed by `STOP_REASON_TO_FINISH_REASON satisfies Record<Anthropic.StopReason, FinishReason>` | Enforces compile-time exhaustiveness across all `@anthropic-ai/sdk` `StopReason` values | | Tool-use ID sanitizer | `_ToolUseIdSanitizer` class in `models/anthropic_llm.py` | `ToolUseIdSanitizer` class in `integrations/src/models/anthropic_utils.ts` | Pairs invalid, empty, or missing `functionCall.id` and `functionResponse.id` values with `toolu_fallback_<n>` IDs across a request | | Preserving `adk-*` IDs | `_preserve_function_call_ids` in `contents.py` | `preserveFunctionCallIds` parameter on `getContents` and `getCurrentTurnContents` in `content_processor_utils.ts` | Keeps client-generated `adk-*` IDs when `ContentRequestProcessor` detects a Claude model name or instance | *What the reviewer must decide* - Whether keeping `MAX_TOKEN = 1024` as a `@deprecated` export alongside `DEFAULT_MAX_TOKENS = 8192` is the right compatibility tradeoff for `@google/adk-integrations`. - Whether exporting `ToolUseIdSanitizer` from `integrations/src/models/anthropic_utils.ts` (and re-exporting through `@google/adk-integrations`) is desired for callers of `contentToMessageParam` and `partToMessageBlock`. ## Testing Done - `npm run build` (built `@google/adk`, `@google/adk-devtools`, and `@google/adk-integrations`). - `npx vitest run --project unit:integrations` (3 test files, 112 tests passed). - `npx vitest run --project unit:core core/test/agents/processors/content_processor_utils_test.ts core/test/agents/processors/content_request_processor_test.ts` (2 test files, 65 tests passed). - `npm run ts:check` and `npm run ts:check:samples` (passed with 0 errors). - `npm run lint`, `npm run format:check`, `npm run docs:check`, and `npx secretlint "**/*"` (all passed). Produced by Foundry (task 9178aa4d-dca7-42be-89b2-cf489f29b54d). Reviewed by an automated reviewer before upload; still needs human review.
|
Scored 2026-10-23 22:27 UTC at 08ce29f, round 1. Earlier runs are in this comment's edit history. Foundry merge critic: APPROVE This change updates the Claude integration to configure output token limits, map finish reasons, and preserve and sanitize tool-use identifiers. Rubric — the default is approve; each rule is a reason a human should read this change first. [no ] public_api — adds optional maxTokens to ClaudeParams and optional parameters to internal helpers without breaking exported signatures Posted by Foundry's merge critic as evidence rather than a request, so nothing here is waiting on you. Advisory only: Foundry has given no approval. |
Summary
9 files changed, 1501 insertions(+), 40 deletions(-) across
@google/adk-integrations,@google/adk, anddocs/guides/integrations/models/anthropic_llm/index.md.Problem
ClaudeignoresmaxOutputTokensonLlmRequest.configand sends a fixedMAX_TOKEN(1024) tomessages.create.messageToGenerateContentResponseleavesfinishReasonunset onLlmResponseinstead of mappingmessage.stop_reason.ContentRequestProcessorstripsadk-*function call IDs on Claude turns, andpartToMessageBlockforwards missing or invalid tool IDs unchanged.Solution
Add
maxTokens(default8192) toClaudeParamsandClaude, and preferllmRequest.config?.maxOutputTokensoverthis.maxTokens. Mapmessage.stop_reasonthroughtoGoogleGenaiFinishReasonand setfinishReasononLlmResponseinmessageToGenerateContentResponse. Preserve function call IDs ingetContentsandgetCurrentTurnContentsfor Claude models, and sanitize tool IDs withToolUseIdSanitizer.Ported behaviors from
adk-python(v1.0.0tov2.13.0)maxTokensonClaudeParamsandClaude(default8192), overridden per request byllmRequest.config?.maxOutputTokens, while keepingMAX_TOKEN = 1024exported with@deprecated.toGoogleGenaiFinishReasonmapping all sevenAnthropic.StopReasonvalues (end_turn,stop_sequence,tool_use,pause_turn,max_tokens,model_context_window_exceeded,refusal) toFinishReasonand settingfinishReasononLlmResponse.preserveFunctionCallIdsingetContentsandgetCurrentTurnContents(enabled byContentRequestProcessorfor Claude models) and request-scopedToolUseIdSanitizerinpartToMessageBlockandcontentToMessageParam.AnthropicLlm.max_tokens: int = 8192DEFAULT_MAX_TOKENS = 8192,Claude.maxTokens = 8192,MAX_TOKEN = 1024(@deprecated)MAX_TOKENsymbol for existing callersllm_request.config.max_output_tokens or self.max_tokensllmRequest.config?.maxOutputTokens ?? this.maxTokensto_google_genai_finish_reason(anthropic_stop_reason)toGoogleGenaiFinishReason(anthropicStopReason)backed bySTOP_REASON_TO_FINISH_REASON satisfies Record<Anthropic.StopReason, FinishReason>@anthropic-ai/sdkStopReasonvalues_ToolUseIdSanitizerclass inmodels/anthropic_llm.pyToolUseIdSanitizerclass inintegrations/src/models/anthropic_utils.tsfunctionCall.idandfunctionResponse.idvalues withtoolu_fallback_<n>IDs across a requestadk-*IDs_preserve_function_call_idsincontents.pypreserveFunctionCallIdsparameter ongetContentsandgetCurrentTurnContentsincontent_processor_utils.tsadk-*IDs whenContentRequestProcessordetects a Claude model name or instanceWhat the reviewer must decide
MAX_TOKEN = 1024as a@deprecatedexport alongsideDEFAULT_MAX_TOKENS = 8192is the right compatibility tradeoff for@google/adk-integrations.ToolUseIdSanitizerfromintegrations/src/models/anthropic_utils.ts(and re-exporting through@google/adk-integrations) is desired for callers ofcontentToMessageParamandpartToMessageBlock.Testing Done
npm run build(built@google/adk,@google/adk-devtools, and@google/adk-integrations).npx vitest run --project unit:integrations(3 test files, 112 tests passed).npx vitest run --project unit:core core/test/agents/processors/content_processor_utils_test.ts core/test/agents/processors/content_request_processor_test.ts(2 test files, 65 tests passed).npm run ts:checkandnpm run ts:check:samples(passed with 0 errors).npm run lint,npm run format:check,npm run docs:check, andnpx secretlint "**/*"(all passed).Produced by Foundry (task 9178aa4d-dca7-42be-89b2-cf489f29b54d). Reviewed by an automated
reviewer before upload; still needs human review.