-
-
Notifications
You must be signed in to change notification settings - Fork 130
feat: update @anthropic-ai/sdk to version 0.74.0 and use real structu… #281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughMigrates Anthropic adapter to Anthropic’s GA structured output via output_config.format.json_schema, switches from Changes
Sequence Diagram(s)sequenceDiagram
participant Adapter as Adapter (text.ts)
participant SDK as Anthropic SDK (client.messages)
participant Model as Claude Model
participant Tool as Tool-based Fallback
Adapter->>SDK: messages.create(..., output_config.format.json_schema?, stream?)
SDK->>Model: forward request
alt Model supports structured_output (Claude 4+)
Model-->>SDK: structured JSON content blocks
SDK-->>Adapter: response.content (JSON)
Adapter->>Adapter: parse JSON -> data + rawText
else Older model / no structured_output
Adapter->>SDK: messages.create(..., tools + force tool_choice)
Model->>Tool: invoke tool
Tool-->>SDK: tool_use content
SDK-->>Adapter: tool_use or text fallback
Adapter->>Adapter: extract & JSON.parse -> data + rawText
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @.changeset/real-structured-output.md:
- Around line 9-11: The changeset claim "No breaking changes to the public API"
contradicts the earlier note that TypeScript tool choice types moved from the
beta namespace to `@anthropic-ai/sdk/resources/messages`; update the changeset to
either (a) mark this release as a major bump or (b) explicitly state that
runtime behavior is compatible but TypeScript type imports have changed (i.e.,
consumers must update imports from BetaToolChoice* / client.beta.messages to the
GA equivalents under `@anthropic-ai/sdk/resources/messages`); reference the
phrasing in the changeset text that mentions "tool choice types" and the
statement "No breaking changes to the public API" so the maintainer edits the
wording to reflect the type-level breaking change or increments the version
level accordingly.
In `@packages/typescript/ai-anthropic/src/text/text-provider-options.ts`:
- Around line 132-137: The JSDoc block for the system field is malformed—fix the
comment above the declaration system?: string | Array<TextBlockParam> by
restoring the missing leading '*' on the description line and aligning
indentation so every line in the JSDoc starts with " *" (e.g., " * System
prompt." and " * A system prompt is..."), keeping the text content intact and
leaving the system property signature unchanged.
🧹 Nitpick comments (3)
packages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts (2)
7-23:betaMessagesCreatemock is now dead code.After migrating to
client.messages.create, thebetaMessagesCreatemock (andclient.betabranch) is never used or asserted. Consider removing it to keep tests focused and avoid confusion about which API surface is exercised.♻️ Suggested cleanup
const mocks = vi.hoisted(() => { - const betaMessagesCreate = vi.fn() const messagesCreate = vi.fn() const client = { - beta: { - messages: { - create: betaMessagesCreate, - }, - }, messages: { create: messagesCreate, }, } - return { betaMessagesCreate, messagesCreate, client } + return { messagesCreate, client } })
57-185: No test coverage forstructuredOutputpath.The new native structured output flow (
output_config.formatwithjson_schema) introduced intext.tsis a key feature of this PR but has no test. Consider adding a test that mocks a non-streamingclient.messages.createcall withoutput_configand verifies the JSON parsing logic, including the error path.packages/typescript/ai-anthropic/src/tools/tool-converter.ts (1)
42-64: Theas Array<ToolUnion>cast silently bypasses type safety for beta-only tools.Several branches (lines 49–58:
code_execution,computer,memory,str_replace_editor,web_fetch) return types that are not part ofToolUnionper the JSDoc on lines 37–38. The cast on line 64 hides this mismatch. If the GAclient.messages.createendpoint rejects these tool types at runtime, there will be no compile-time warning.This is acknowledged in the doc comment, so no immediate action needed, but consider adding a runtime guard or at least a
console.warnwhen a beta-only tool is passed through the GA endpoint, to surface issues early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/typescript/ai-anthropic/src/adapters/text.ts`:
- Around line 178-220: The structured output request builds
createParams.output_config.format without a required "name" property and also
wraps JSON.parse errors inside the outer catch, causing double-prefixed error
messages; update the createParams construction in the method containing
createParams so output_config.format includes a "name" string alongside
type:'json_schema' and schema:outputSchema (matching Anthropic's expected
shape), and change error handling around client.messages.create and parsing so
JSON.parse(rawText) is either performed outside the outer try/catch or its
parsing errors are re-thrown directly (e.g., detect and re-throw the parse
Error) instead of being wrapped by the outer catch that throws `Structured
output generation failed`; reference createParams, output_config.format,
client.messages.create, rawText and the JSON.parse block when applying the fix.
🧹 Nitpick comments (2)
packages/typescript/ai-anthropic/src/text/text-provider-options.ts (1)
71-91: Inconsistent JSDoc indentation inAnthropicThinkingOptions.The
thinkingproperty (Line 73) uses 5 spaces of indentation before*, and thebudget_tokensdescription (Line 80) starts at column 0. While these appear to be pre-existing, they're in the same block as the reformatted JSDoc lines (74–75, 81–82), so this would be a good time to fix them.Suggested fix
export interface AnthropicThinkingOptions { /** - * Configuration for enabling Claude's extended thinking. - * - * When enabled, responses include thinking content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your max_tokens limit. - */ + * Configuration for enabling Claude's extended thinking. + * + * When enabled, responses include thinking content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your max_tokens limit. + */ thinking?: | { /** -* Determines how many tokens Claude can use for its internal reasoning process. Larger budgets can enable more thorough analysis for complex problems, improving response quality. -* -* Must be ≥1024 and less than max_tokens -*/ + * Determines how many tokens Claude can use for its internal reasoning process. Larger budgets can enable more thorough analysis for complex problems, improving response quality. + * + * Must be ≥1024 and less than max_tokens + */ budget_tokens: numberpackages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts (1)
31-32:as anycast trades type safety for test flexibility.This allows testing with arbitrary model strings. It's fine for test code, but consider using actual model IDs from
ANTHROPIC_MODELS(e.g.,'claude-sonnet-4') where possible to catch type regressions. The structured output tests already do this correctly.
23214a0 to
cff44cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/typescript/ai-anthropic/src/adapters/text.ts (1)
247-297:⚠️ Potential issue | 🟡 Minor
toolBasedStructuredOutputhas the same double-wrapped error that was fixed innativeStructuredOutput.The
throwat Line 284 (JSON parse failure) is inside the outertryblock, so it gets caught at Line 291 and re-wrapped as"Structured output generation failed: Failed to extract structured output from response...". Apply the same separation pattern used innativeStructuredOutput(Lines 193-222).Proposed fix
+ private async toolBasedStructuredOutput( + requestParams: InternalTextProviderOptions, + chatOptions: StructuredOutputOptions<AnthropicTextProviderOptions>['chatOptions'], + outputSchema: StructuredOutputOptions<AnthropicTextProviderOptions>['outputSchema'], + ): Promise<StructuredOutputResult<unknown>> { const structuredOutputTool = { name: 'structured_output', description: 'Use this tool to provide your response in the required structured format.', input_schema: { type: 'object' as const, properties: outputSchema.properties ?? {}, required: outputSchema.required ?? [], }, } + let response: Awaited<ReturnType<typeof this.client.messages.create>> try { - const response = await this.client.messages.create( + response = await this.client.messages.create( { ...requestParams, stream: false, tools: [structuredOutputTool], tool_choice: { type: 'tool', name: 'structured_output' }, }, { signal: chatOptions.request?.signal, headers: chatOptions.request?.headers, }, ) + } catch (error: unknown) { + const err = error as Error + throw new Error( + `Structured output generation failed: ${err.message || 'Unknown error occurred'}`, + ) + } - let parsed: unknown = null - let rawText = '' + let parsed: unknown = null + let rawText = '' - for (const block of response.content) { - if (block.type === 'tool_use' && block.name === 'structured_output') { - parsed = block.input - rawText = JSON.stringify(block.input) - break - } + for (const block of response.content) { + if (block.type === 'tool_use' && block.name === 'structured_output') { + parsed = block.input + rawText = JSON.stringify(block.input) + break } + } - if (parsed === null) { - rawText = response.content - .map((b) => { - if (b.type === 'text') { - return b.text - } - return '' - }) - .join('') - try { - parsed = JSON.parse(rawText) - } catch { - throw new Error( - `Failed to extract structured output from response. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`, - ) - } + if (parsed === null) { + rawText = response.content + .map((b) => { + if (b.type === 'text') { + return b.text + } + return '' + }) + .join('') + try { + parsed = JSON.parse(rawText) + } catch { + throw new Error( + `Failed to extract structured output from response. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`, + ) } - - return { data: parsed, rawText } - } catch (error: unknown) { - const err = error as Error - throw new Error( - `Structured output generation failed: ${err.message || 'Unknown error occurred'}`, - ) } + + return { data: parsed, rawText } }
🧹 Nitpick comments (3)
packages/typescript/ai-anthropic/src/model-meta.ts (1)
69-69: Dual source of truth for structured output capability.Each model declares
structured_output: true/falsein its metadata and theANTHROPIC_STRUCTURED_OUTPUT_MODELSset duplicates this information. Currently they're consistent, but future model additions could easily drift. Consider deriving the set from the model metadata (or vice versa) to keep a single source of truth.Example: derive the set from model metadata
// Collect all model consts into an array, then filter const ALL_MODELS = [ CLAUDE_OPUS_4_6, CLAUDE_OPUS_4_5, CLAUDE_SONNET_4_5, CLAUDE_HAIKU_4_5, CLAUDE_OPUS_4_1, CLAUDE_SONNET_4, CLAUDE_SONNET_3_7, CLAUDE_OPUS_4, CLAUDE_HAIKU_3_5, CLAUDE_HAIKU_3, ] as const export const ANTHROPIC_STRUCTURED_OUTPUT_MODELS: ReadonlySet<string> = new Set( ALL_MODELS.filter(m => m.supports.structured_output).map(m => m.id), )Also applies to: 100-100, 131-131, 162-162, 193-193, 224-224, 254-254, 285-285, 316-316, 347-347, 418-430
packages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts (1)
31-32:as anycast disables all type checking on model parameter.This hides invalid model strings at test time. A narrower cast like
as (typeof ANTHROPIC_MODELS)[number]would still allow flexibility while preserving some safety:-const createAdapter = <TModel extends string>(model: TModel) => - new AnthropicTextAdapter({ apiKey: 'test-key' }, model as any) +import type { ANTHROPIC_MODELS } from '../src/model-meta' +type AnyAnthropicModel = (typeof ANTHROPIC_MODELS)[number] +const createAdapter = (model: string) => + new AnthropicTextAdapter({ apiKey: 'test-key' }, model as AnyAnthropicModel)packages/typescript/ai-anthropic/src/tools/tool-converter.ts (1)
9-9: Type cast for beta-only tools is documented and necessary.The
as Array<ToolUnion>cast on Line 64 is necessary because the tool converters for ComputerUse, CodeExecution, Memory, and WebFetch return beta-only types from@anthropic-ai/sdk/resources/betathat are not included in the GAToolUniondiscriminated union. The JSDoc at Lines 36-38 correctly documents this structural compatibility at runtime. The cast is appropriate here given the SDK constraints.Consider adding a runtime assertion or type guard in a future iteration to catch shape mismatches for beta tools, rather than relying solely on the cast. This would provide better protection if Anthropic's SDK evolves in ways that change the runtime shape of these beta tool types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/typescript/ai-anthropic/src/adapters/text.ts (1)
236-245:⚠️ Potential issue | 🟡 Minor
toolBasedStructuredOutputhardcodestype: 'object'regardless ofoutputSchema.type.The tool's
input_schemaalways setstype: 'object'(line 241), discarding whateveroutputSchema.typeactually is. If a caller passes a non-object schema (e.g., an array root), this silently changes the schema semantics. Consider propagatingoutputSchema.typeinstead.Suggested fix
const structuredOutputTool = { name: 'structured_output', description: 'Use this tool to provide your response in the required structured format.', input_schema: { - type: 'object' as const, - properties: outputSchema.properties ?? {}, - required: outputSchema.required ?? [], + ...outputSchema, }, }
🧹 Nitpick comments (3)
packages/typescript/ai-anthropic/src/tools/index.ts (1)
16-25: Potential overlap betweenToolUnionand individual GA tool types in this union.
ToolUnionfrom the SDK likely already includes GA equivalents ofBashTool,ComputerUseTool,TextEditorTool, etc. The overlap is harmless (TypeScript collapses duplicate union members), but as beta tools graduate to GA, consider pruning the individual types that become redundant withToolUnionto keep the union tidy.packages/typescript/ai-anthropic/tests/anthropic-adapter.test.ts (1)
31-32: Theas anycast bypasses model type safety.Using
model as anyallows testing with arbitrary model strings, which is practical. However, consider using a type assertion to(typeof ANTHROPIC_MODELS)[number]or adding a comment explaining why the cast is needed, so future contributors don't accidentally misuse this pattern in non-test code.packages/typescript/ai-anthropic/src/model-meta.ts (1)
418-430:ANTHROPIC_STRUCTURED_OUTPUT_MODELSduplicates per-modelstructured_outputflags.The set is consistent with the individual model metadata, but the capability is now expressed in two places. Consider deriving the set from the model metadata to avoid them drifting apart, e.g.:
export const ANTHROPIC_STRUCTURED_OUTPUT_MODELS: ReadonlySet<string> = new Set( [CLAUDE_OPUS_4_6, CLAUDE_OPUS_4_5, CLAUDE_SONNET_4_5, CLAUDE_HAIKU_4_5, CLAUDE_OPUS_4_1, CLAUDE_SONNET_4, CLAUDE_OPUS_4] .filter(m => m.supports.structured_output) .map(m => m.id) )This is optional — the current approach is clear and unlikely to drift with only ~10 models.
🎯 Changes
The changes are mostly made because we have structured outputs offered by anthropic, so we don't have to deal with malformed json thus zod having validation errors.
The intention if this PR is to get rid off the tool-call-as-response hack and use real structured output since it's also not a beta feature anymore.
For it to be out of beta, we also need to bump the SDK version and the things affected (mostly types)
✅ Checklist
I have followed the steps in the Contributing guide.
(The file seems missing, but I did everything I could think of)
I have tested this code locally with
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
Dependencies
Improvements
Note