-
Notifications
You must be signed in to change notification settings - Fork 3.4k
feat(anthropic): advanced tool use #10812
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?
Changes from all commits
8761de3
ad4608d
9849126
dc8fb51
f1628f3
8f1f277
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import { SharedV3ProviderMetadata, SharedV3Warning } from '@ai-sdk/provider'; | ||
| import { validateTypes } from '@ai-sdk/provider-utils'; | ||
| import { | ||
| AnthropicAdvancedToolUse, | ||
| anthropicInputExamplesSchema, | ||
| anthropicProgrammaticToolCallingSchema, | ||
| AnthropicTool, | ||
| anthropicToolSearchSchema, | ||
| } from './anthropic-messages-api'; | ||
|
|
||
| /** | ||
| * Extracts and validates Anthropic advanced tool use configuration from provider metadata. | ||
| * | ||
| * This function processes provider metadata to extract tool use configuration options including | ||
| * deferred loading, allowed callers, and input examples. It supports both camelCase and snake_case | ||
| * property naming conventions for backwards compatibility. | ||
| * | ||
| * @param providerMetadata - Optional shared provider metadata containing Anthropic-specific configuration | ||
| * @returns A promise that resolves to an object containing validated advanced tool use settings: | ||
| * - `deferLoading`: Validated tool search/defer loading configuration | ||
| * - `allowedCallers`: Validated programmatic tool calling configuration | ||
| * - `inputExamples`: Validated input examples configuration | ||
| * @throws Will throw an error if any of the extracted configurations fail validation | ||
| */ | ||
|
|
||
| export async function getAnthropicAdvancedToolUseFeaturesSupport( | ||
| providerMetadata: SharedV3ProviderMetadata | undefined, | ||
| ): Promise<AnthropicAdvancedToolUse> { | ||
| const anthropic = providerMetadata?.anthropic; | ||
| const deferLoading = anthropic?.defer_loading ?? anthropic?.deferLoading; | ||
| const inputExamples = anthropic?.input_examples ?? anthropic?.inputExamples; | ||
| const allowed_callers = | ||
| anthropic?.allowed_callers ?? anthropic?.allowedCallers; | ||
|
|
||
| const [parseDeferLoading, parseAllowedCallers, parseInputExamples] = | ||
| await Promise.all([ | ||
| validateTypes({ | ||
| value: deferLoading, | ||
| schema: anthropicToolSearchSchema, | ||
| }), | ||
| validateTypes({ | ||
| value: allowed_callers, | ||
| schema: anthropicProgrammaticToolCallingSchema, | ||
| }), | ||
| validateTypes({ | ||
| value: inputExamples, | ||
| schema: anthropicInputExamplesSchema, | ||
| }), | ||
| ]); | ||
|
|
||
| let result: AnthropicAdvancedToolUse = {}; | ||
| if (parseDeferLoading !== undefined) { | ||
| result.defer_loading = parseDeferLoading; | ||
| } | ||
|
|
||
| if (parseAllowedCallers !== undefined) { | ||
| result.allowed_callers = parseAllowedCallers; | ||
| } | ||
|
|
||
| if (parseInputExamples !== undefined) { | ||
| result.input_examples = parseInputExamples; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| export const handleAnthropicAdvancedToolUseFeaturesWarnings = ( | ||
| anthropicTools: AnthropicTool[], | ||
| ) => { | ||
| const toolWarnings: SharedV3Warning[] = []; | ||
|
|
||
| // Check if any tool uses defer_loading | ||
| const anyToolUsesDeferLoading = anthropicTools.some( | ||
| t => 'defer_loading' in t && t.defer_loading === true, | ||
| ); | ||
|
|
||
| const searchTool = anthropicTools.find( | ||
| t => | ||
| t.name === 'tool_search_tool_bm25' || t.name === 'tool_search_tool_regex', | ||
| ); | ||
|
|
||
| if (anyToolUsesDeferLoading && !searchTool) { | ||
| toolWarnings.push({ | ||
| type: 'unsupported', | ||
| feature: `tool`, | ||
| details: `At least one tool has defer_loading set to true, but no tool search tool (tool_search_tool_bm25 or tool_search_tool_regex) is provided. A tool search tool is required when using deferred loading.`, | ||
| }); | ||
| } | ||
|
|
||
| return toolWarnings; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,10 @@ import { textEditor_20250728ArgsSchema } from './tool/text-editor_20250728'; | |
| import { webSearch_20250305ArgsSchema } from './tool/web-search_20250305'; | ||
| import { webFetch_20250910ArgsSchema } from './tool/web-fetch-20250910'; | ||
| import { validateTypes } from '@ai-sdk/provider-utils'; | ||
| import { | ||
| getAnthropicAdvancedToolUseFeaturesSupport, | ||
| handleAnthropicAdvancedToolUseFeaturesWarnings, | ||
| } from './advanced-tool-use'; | ||
|
|
||
| export async function prepareTools({ | ||
| tools, | ||
|
|
@@ -53,14 +57,17 @@ export async function prepareTools({ | |
| canCache: true, | ||
| }); | ||
|
|
||
| const advancedToolFeatures = | ||
| await getAnthropicAdvancedToolUseFeaturesSupport( | ||
| tool.providerOptions, | ||
| ); | ||
|
|
||
| anthropicTools.push({ | ||
| name: tool.name, | ||
| description: tool.description, | ||
| input_schema: tool.inputSchema, | ||
| cache_control: cacheControl, | ||
| ...(supportsStructuredOutput === true && tool.strict != null | ||
| ? { strict: tool.strict } | ||
| : {}), | ||
| ...advancedToolFeatures, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wouldn't this make all tool follow same
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no since
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would still be specified in provider options right, and that make it applied to all tools then. I mean how could you have mix of tools with some having defer_loading set to true and some with false
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops sorry, providerMetadata is tool property, I missed that, this works then |
||
| }); | ||
| break; | ||
| } | ||
|
|
@@ -212,6 +219,22 @@ export async function prepareTools({ | |
| break; | ||
| } | ||
|
|
||
| case 'anthropic.tool_search_tool_regex_20251119': { | ||
| anthropicTools.push({ | ||
| type: 'tool_search_tool_regex_20251119', | ||
| name: 'tool_search_tool_regex', | ||
| }); | ||
| break; | ||
| } | ||
|
|
||
| case 'anthropic.tool_search_tool_bm25_20251119': { | ||
| anthropicTools.push({ | ||
| type: 'tool_search_tool_bm25_20251119', | ||
| name: 'tool_search_tool_bm25', | ||
| }); | ||
| break; | ||
| } | ||
|
|
||
| default: { | ||
| toolWarnings.push({ | ||
| type: 'unsupported', | ||
|
|
@@ -233,6 +256,13 @@ export async function prepareTools({ | |
| } | ||
| } | ||
|
|
||
| const advancedToolFeaturesWarnings = | ||
| handleAnthropicAdvancedToolUseFeaturesWarnings(anthropicTools); | ||
|
|
||
| if (advancedToolFeaturesWarnings.length > 0) { | ||
| toolWarnings.push(...advancedToolFeaturesWarnings); | ||
| } | ||
|
|
||
| if (toolChoice == null) { | ||
| return { | ||
| tools: anthropicTools, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { | ||
| createProviderToolFactory, | ||
| lazySchema, | ||
| zodSchema, | ||
| } from '@ai-sdk/provider-utils'; | ||
| import { z } from 'zod/v4'; | ||
|
|
||
| const toolSearchToolBm25_20251119ArgsSchema = lazySchema(() => | ||
| zodSchema(z.object({})), | ||
| ); | ||
|
|
||
| export const toolSearchToolBm25_20251119 = createProviderToolFactory<{}, {}>({ | ||
| id: 'anthropic.tool_search_tool_bm25_20251119', | ||
| inputSchema: toolSearchToolBm25_20251119ArgsSchema, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { | ||
| createProviderToolFactory, | ||
| lazySchema, | ||
| zodSchema, | ||
| } from '@ai-sdk/provider-utils'; | ||
| import { z } from 'zod/v4'; | ||
|
|
||
| const toolSearchToolRegex_20251119ArgsSchema = lazySchema(() => | ||
| zodSchema(z.object({})), | ||
| ); | ||
|
|
||
| export const toolSearchToolRegex_20251119 = createProviderToolFactory<{}, {}>({ | ||
| id: 'anthropic.tool_search_tool_regex_20251119', | ||
| inputSchema: toolSearchToolRegex_20251119ArgsSchema, | ||
| }); |
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.
The
strictproperty from function tools is no longer being passed through to the Anthropic tool definition. This breaks the structured output feature whenstrictis set on a tool.View Details
📝 Patch Details
Analysis
Missing
strictproperty passthrough in Anthropic tool preparationWhat fails: The
prepareTools()function inpackages/anthropic/src/anthropic-prepare-tools.tsdoes not pass through thestrictproperty from function tools to the Anthropic tool definition, breaking structured output feature whenstrictis set.How to reproduce:
cd packages/anthropic pnpm test:node -- src/anthropic-prepare-tools.test.tsThe test "should include strict when supportsStructuredOutput is true and strict is true" would fail with:
What was happening: When refactoring to use
getAnthropicAdvancedToolUseFeaturesSupport(), the handling of thestrictproperty was removed. The function only extracteddefer_loading,allowed_callers, andinput_examplesfromadvancedToolFeatures, but never checkedtool.strict.What should happen: When
supportsStructuredOutputis true ANDtool.strictis defined, thestrictproperty should be included in the output tool definition, matching the pattern used by other providers (Deepseek, Mistral, OpenAI).Fix applied: Added conditional spread to include
strictproperty:This matches the established pattern across the codebase and ensures structured output mode works correctly with Anthropic models.
Verification: All 177 tests in the anthropic package now pass, including the 3 strict mode tests in
anthropic-prepare-tools.test.ts.