From b07fc759c23a99dde45451a33e1cfb0ff8fcddef Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Sat, 21 Mar 2026 17:35:42 -0700 Subject: [PATCH] AIX: Anthropic: wires: update with new API features and tools - tools allowed callers for client and server - all tool definitions common options - new code_execution, web_fetch, web_search tools - top-level cache_contol - thinking with disabled summaries for speed - message updates with container variants -fix tool_search_tool results --- .../adapters/anthropic.messageCreate.ts | 16 +- .../chatGenerate/parsers/anthropic.parser.ts | 24 ++- .../dispatch/wiretypes/anthropic.wiretypes.ts | 179 +++++++++++++----- 3 files changed, 155 insertions(+), 64 deletions(-) diff --git a/src/modules/aix/server/dispatch/chatGenerate/adapters/anthropic.messageCreate.ts b/src/modules/aix/server/dispatch/chatGenerate/adapters/anthropic.messageCreate.ts index 436ebc2c2..0d574c964 100644 --- a/src/modules/aix/server/dispatch/chatGenerate/adapters/anthropic.messageCreate.ts +++ b/src/modules/aix/server/dispatch/chatGenerate/adapters/anthropic.messageCreate.ts @@ -155,12 +155,14 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate: if (model.vndAntThinkingBudget === 'adaptive') { payload.thinking = { type: 'adaptive', + // display: 'summarized', // default, use 'omitted' to not include it and stream faster while preserving the signature }; delete payload.temperature; } else if (model.vndAntThinkingBudget !== null) { payload.thinking = { type: 'enabled', budget_tokens: model.vndAntThinkingBudget < payload.max_tokens ? model.vndAntThinkingBudget : payload.max_tokens - 1, + // display: 'summarized', // default, use 'omitted' to not include it and stream faster while preserving the signature }; delete payload.temperature; } else { @@ -217,9 +219,9 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate: if (model.vndAntWebSearch === 'auto') { hostedTools.push({ type: 'web_search_20250305', + // type: 'web_search_20260209', // TODO name: 'web_search', - ...(model.vndAntWebSearchMaxUses !== undefined ? { max_uses: model.vndAntWebSearchMaxUses } : {}), // Allow up to 10 searches by default - // max_uses: model.vndAntWebSearchMaxUses ?? DEFAULT_WEB_SEARCH_MAX_USES, // Allow up to 10 searches by default + ...(model.vndAntWebSearchMaxUses !== undefined ? { max_uses: model.vndAntWebSearchMaxUses } : {}), // Pass user geolocation for location-aware search results ...(model.userGeolocation ? { user_location: { type: 'approximate' as const, ...model.userGeolocation }, @@ -231,9 +233,9 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate: if (model.vndAntWebFetch === 'auto') { hostedTools.push({ type: 'web_fetch_20250910', + //type: 'web_fetch_20260209', name: 'web_fetch', - ...(model.vndAntWebFetchMaxUses !== undefined ? { max_uses: model.vndAntWebFetchMaxUses } : {}), // Allow up to 5 fetches by default - // max_uses: model.vndAntWebFetchMaxUses ?? DEFAULT_WEB_FETCH_MAX_USES, // Allow up to 5 fetches by default + ...(model.vndAntWebFetchMaxUses !== undefined ? { max_uses: model.vndAntWebFetchMaxUses } : {}), citations: { enabled: true }, // Enable citations }); } @@ -279,8 +281,8 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate: if (!payload.tools?.length) payload.tools = []; - if (!payload.tools.some(t => t.type === 'code_execution_20250825')) - payload.tools.push({ type: 'code_execution_20250825', name: 'code_execution' }); + if (!payload.tools.some(t => t.type === 'code_execution_20260120' /* Beta */ || t.type === 'code_execution_20250825')) + payload.tools.push({ type: 'code_execution_20260120', name: 'code_execution' }); } } @@ -457,7 +459,7 @@ function _toAnthropicTools(itds: AixTools_ToolDefinition[], strictToolsEnabled: // [Anthropic, 2025-11-24] Tool Search Tool - auto-defer all custom tools ...(toolSearchToolEnabled ? { defer_loading: true } : {}), // [Anthropic, 2025-11-24] Programmatic Tool Calling - pass through allowed_callers and input_examples - ...(allowed_callers ? { allowed_callers: allowed_callers.map(c => c === 'code_execution' ? 'code_execution_20250825' : c) } : {}), + ...(allowed_callers ? { allowed_callers: allowed_callers.map(c => c === 'code_execution' ? 'code_execution_20260120' : c) } : {}), ...(input_examples ? { input_examples } : {}), }; diff --git a/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts b/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts index a3411ea9b..80ec86b81 100644 --- a/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts +++ b/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts @@ -330,6 +330,10 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction { file_count: fileIds.length, file_ids: fileIds, }); + } else if (content_block.content.type === 'encrypted_code_execution_result') { + // Encrypted variant (PFC + web_search) - stdout is encrypted, show as transient placeholder + pt.sendVoidPlaceholder('code-exec', 'Code executed (encrypted output)'); + console.log('[Anthropic] Encrypted code execution result:', { return_code: content_block.content.return_code }); } else if (content_block.content.type === 'code_execution_tool_result_error') { // Error during code execution pt.appendText(`\n\nāš ļø Skill execution error: ${content_block.content.error_code}\n`); @@ -403,10 +407,10 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction { // using the Files API with content_block.file_id break; - case 'tool_result': // [Anthropic, 2025-11-24] Tool Search Tool - The actual tool definitions are auto-expanded by Anthropic's API - if (Array.isArray(content_block.content)) { + case 'tool_search_tool_result': // [Anthropic, 2025-11-24] Tool Search Tool + if (content_block.content?.type === 'tool_search_tool_search_result') { // success - const toolNames = content_block.content.map((ref: { type: string; tool_name: string }) => ref.tool_name); + const toolNames = content_block.content.tool_references.map(ref => ref.tool_name); pt.sendVoidPlaceholder('code-exec', `šŸ” Discovered ${toolNames.length} tool(s): ${toolNames.join(', ')}`); // Log for future debugging console.log('[Anthropic] Tool search discovered:', { tools: toolNames }); @@ -806,6 +810,10 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction { file_count: fileIds.length, file_ids: fileIds, }); + } else if (contentBlock.content.type === 'encrypted_code_execution_result') { + // Encrypted variant (PFC + web_search) - stdout is encrypted, show as transient placeholder + pt.sendVoidPlaceholder('code-exec', 'Code executed (encrypted output)'); + console.log('[Anthropic] Encrypted code execution result (non-streaming):', { return_code: contentBlock.content.return_code }); } else if (contentBlock.content.type === 'code_execution_tool_result_error') { // Error during code execution pt.appendText(`\n\nāš ļø Skill execution error: ${contentBlock.content.error_code}\n`); @@ -872,16 +880,16 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction { }); break; - case 'tool_result': // [Anthropic, 2025-11-24] Tool Search Tool - The actual tool definitions are auto-expanded by Anthropic's API - if (Array.isArray(contentBlock.content)) { + case 'tool_search_tool_result': // [Anthropic, 2025-11-24] Tool Search Tool + if (contentBlock.content?.type === 'tool_search_tool_search_result') { // success - const toolNames = contentBlock.content.map((ref: { type: string; tool_name: string }) => ref.tool_name); + const toolNames = contentBlock.content.tool_references.map(ref => ref.tool_name); pt.sendVoidPlaceholder('code-exec', `šŸ” Discovered ${toolNames.length} tool(s): ${toolNames.join(', ')}`); // Log for future debugging console.log('[Anthropic] Tool search discovered (non-streaming):', { tools: toolNames }); - } else if ((contentBlock.content as any)?.type === 'tool_search_tool_result_error') { + } else if (contentBlock.content?.type === 'tool_search_tool_result_error') { // error during tool search - pt.sendVoidPlaceholder('code-exec', `šŸ” Tool search error: ${(contentBlock.content as any).error_code}`); + pt.sendVoidPlaceholder('code-exec', `šŸ” Tool search error: ${contentBlock.content.error_code}`); } break; diff --git a/src/modules/aix/server/dispatch/wiretypes/anthropic.wiretypes.ts b/src/modules/aix/server/dispatch/wiretypes/anthropic.wiretypes.ts index 4fa28c1ff..ff6ca68cb 100644 --- a/src/modules/aix/server/dispatch/wiretypes/anthropic.wiretypes.ts +++ b/src/modules/aix/server/dispatch/wiretypes/anthropic.wiretypes.ts @@ -13,6 +13,14 @@ const hotFixAntShipNoEmptyTextBlocks = true; // Replace empty text blocks with a * * ## Updates * + * ### 2026-03-21 - API Sync: GA tool versions, thinking display, caller updates, cache_control + * - Tools: Added web_search_20260209 (GA), web_fetch_20260209/20260309 (GA), code_execution_20260120 (GA REPL) + * - Request: Added top-level `cache_control` for automatic caching (Feb 2026) + * - Request.thinking: Added `display` field ("summarized"|"omitted") for adaptive/enabled thinking (Mar 2026) + * - ToolUseBlock/ServerToolUseBlock.caller: Added `code_execution_20260120` caller type + * - ServerToolUseBlock: Added `caller` field (same shape as ToolUseBlock.caller) + * - event_MessageDelta.delta: Added `container` field for container state updates + * * ### 2026-02-06 - API Sync: output_config.format, inference_geo, eager_input_streaming * - Request: deprecated top-level `output_format`, moved to `output_config.format` (GA: 2026-01-29) * - Request: added `inference_geo` for region-of-availability inference routing @@ -130,22 +138,24 @@ export namespace AnthropicWire_Blocks { data: z.string(), }); + /** + * [Anthropic, 2025-11-24] Programmatic Tool Calling + * Indicates how a tool was invoked - direct by model, or programmatically from code execution. + */ + const _ToolUseCaller_schema = z.discriminatedUnion('type', [ + z.object({ type: z.literal('direct') }), + z.object({ + type: z.enum(['code_execution_20250825', 'code_execution_20260120']), + tool_id: z.string(), // ref the server_tool_use (code_execution) that made this call + }), + ]); + export const ToolUseBlock_schema = _CommonBlock_schema.extend({ type: z.literal('tool_use'), id: z.string(), name: z.string(), // length: 1-64 input: z.any(), // Formally an 'object', but relaxed for robust parsing, and code-enforced - /** - * [Anthropic, 2025-11-24] Programmatic Tool Calling - indicates how this tool was invoked. - * Requires the advanced-tool-use-2025-11-20 beta feature. - */ - caller: z.discriminatedUnion('type', [ - z.object({ type: z.literal('direct') }), // model called tool directly - z.object({ - type: z.literal('code_execution_20250825'), // tool called programmatically from within code execution - tool_id: z.string(), // ref the server_tool_use (code_execution) that made this call - }), - ]).optional(), + caller: _ToolUseCaller_schema.optional(), }); @@ -257,6 +267,7 @@ export namespace AnthropicWire_Blocks { z.string(), // forward-compatibility parsing ]), input: z.any(), + caller: _ToolUseCaller_schema.optional(), }); export const WebSearchToolResultBlock_schema = _CommonBlock_schema.extend({ @@ -278,6 +289,7 @@ export namespace AnthropicWire_Blocks { ]), }), ]), + caller: _ToolUseCaller_schema.optional(), }); export const WebFetchToolResultBlock_schema = _CommonBlock_schema.extend({ @@ -298,6 +310,7 @@ export namespace AnthropicWire_Blocks { ]), }), ]), + caller: _ToolUseCaller_schema.optional(), }); const _CodeExecutionOutputBlock_schema = z.object({ @@ -316,6 +329,14 @@ export namespace AnthropicWire_Blocks { return_code: z.number(), content: z.array(_CodeExecutionOutputBlock_schema), }), + // Encrypted variant - used when code execution is combined with programmatic tool calling + web_search + z.object({ + type: z.literal('encrypted_code_execution_result'), + encrypted_stdout: z.string(), + stderr: z.string(), + return_code: z.number(), + content: z.array(_CodeExecutionOutputBlock_schema), + }), z.object({ type: z.literal('code_execution_tool_result_error'), error_code: z.string(), @@ -398,23 +419,24 @@ export namespace AnthropicWire_Blocks { file_id: z.string(), }); - /** - * [Anthropic, 2025-11-24] Tool Search Tool - Result of tool search operation - * Contains either an array of tool references or an error. - */ + /** [Anthropic, 2025-11-24] Tool Search Tool - Result of tool search operation. */ export const ToolSearchToolResultBlock_schema = _CommonBlock_schema.extend({ - type: z.literal('tool_result'), + type: z.literal('tool_search_tool_result'), tool_use_id: z.string(), content: z.union([ - // success - array of tool references - z.array(z.object({ - type: z.literal('tool_reference'), - tool_name: z.string(), - })), + // success + z.object({ + type: z.literal('tool_search_tool_search_result'), + tool_references: z.array(z.object({ + type: z.literal('tool_reference'), + tool_name: z.string(), + })), + }), // error z.object({ type: z.literal('tool_search_tool_result_error'), - error_code: z.union([z.enum(['too_many_requests', 'invalid_pattern', 'pattern_too_long', 'unavailable']), z.string() /* forward-compatibility */]), + error_code: z.union([z.enum(['invalid_tool_input', 'unavailable', 'too_many_requests', 'execution_time_exceeded']), z.string()]), + error_message: z.string().nullish(), }), ]), }); @@ -494,6 +516,7 @@ export namespace AnthropicWire_Messages { AnthropicWire_Blocks.MCPToolUseBlock_schema, AnthropicWire_Blocks.MCPToolResultBlock_schema, AnthropicWire_Blocks.ContainerUploadBlock_schema, + AnthropicWire_Blocks.ToolSearchToolResultBlock_schema, ]); export const MessageInput_schema = z.object({ @@ -573,6 +596,18 @@ export namespace AnthropicWire_Tools { /** 2024-10-22: cache-control can be set on the Tools block as well. We could make use of this instead of the System Instruction blocks for prompts with longer tools. */ cache_control: AnthropicWire_Blocks._CacheControl_schema.nullish(), + + /** When true, tool is not loaded into context initially - discovered via tool_search when needed. */ + defer_loading: z.boolean().optional(), + + /** Specifies which contexts can invoke this tool. */ + allowed_callers: z.array(z.enum([ + 'direct', // (default) direct by model + 'code_execution_20250825', 'code_execution_20260120', // allow programmatically from code execution + ])).optional(), + + /** Structured Outputs - guarantees tool names and inputs match schema exactly. */ + strict: z.boolean().optional(), }); const _CustomToolDefinition_schema = _ToolDefinitionBase_schema.extend({ @@ -601,31 +636,14 @@ export namespace AnthropicWire_Tools { required: z.array(z.string()).optional(), // 2025-02-24: seems to be removed; we may still have this, but it may also be within the 'properties' object }), - /** - * [Anthropic, 2025-11-13] Structured Outputs - guarantees tool inputs to match `input_schema` exactly. - */ - strict: z.boolean().optional(), - - /** - * [Anthropic, 2025-06-11] Eager Input Streaming - enables fine-grained streaming of tool input parameters. - * When true, tool inputs are streamed earlier during generation for lower latency. - */ + /** Eager Input Streaming - tool inputs streamed earlier during generation for lower latency. */ eager_input_streaming: z.boolean().optional(), - /** - * [Anthropic, 2025-11-24] Tool Search Tool - when true, this tool is not loaded into context initially and can be discovered via the tool search tool when needed. - */ - defer_loading: z.boolean().optional(), - - /** - * [Anthropic, 2025-11-24] Programmatic Tool Calling - 2 new fields: - * - specifies which contexts can invoke this tool - * - concrete usage examples to improve accuracy - can increase accuracy (e.g. 72% -> 90% in examples) - */ - allowed_callers: z.array(z.enum(['direct', 'code_execution_20250825'])).optional(), // can be both ['direct', 'code_execution_20250825'] + /** Provide sample tool calls */ input_examples: z.array(z.record(z.string(), z.any())).optional(), }); + // Latest Tool Versions (sorted alphabetically by tool name) // Deprecated versions (removed): // - bash_20241022 -> bash_20250124 @@ -633,14 +651,16 @@ export namespace AnthropicWire_Tools { // - computer_20241022 -> computer_20250124 // - text_editor_20241022, text_editor_20250124, text_editor_20250429 -> text_editor_20250728 + /** BETA (standalone): requires "computer-use-*" beta header. GA as sub-tool of code_execution. */ const _BashTool_20250124_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('bash_20250124'), name: z.literal('bash'), }); + // -- Code Execution Tools -- + /** - * Current (No support for the legacy code_execution_20250522): Supports Bash commands, file operations, and multiple languages. Requires beta header: "code-execution-2025-08-25" - * + * Supports Bash commands, file operations, and multiple languages. * When this tool is provided, Claude automatically gains access to two sub-tools: * - 'bash_code_execution': Run shell commands * - 'text_editor_code_execution': View, create, and edit files, including writing code @@ -651,9 +671,17 @@ export namespace AnthropicWire_Tools { }); /** - * Requires beta header: "computer-use-2025-01-24" - * NOTE: newer version available - computer_20251124 (beta header: "computer-use-2025-11-24") adds `enable_zoom: boolean` + * [Anthropic, Jan 2026 BETA] Code execution REPL-based code execution with persistent state across calls (daemon mode + gVisor checkpoint). + * BETA: As of 2026-03-21, this is not the default - all models still support code_execution_20250825 */ + const _CodeExecutionTool_20260120_schema = _ToolDefinitionBase_schema.extend({ + type: z.literal('code_execution_20260120'), + name: z.literal('code_execution'), + }); + + // -- Computer Use Tools (BETA: requires computer-use-* beta header) -- + + /** BETA: requires "computer-use-2025-01-24" beta header. */ const _ComputerUseTool_20250124_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('computer_20250124'), name: z.literal('computer'), @@ -662,11 +690,18 @@ export namespace AnthropicWire_Tools { display_number: z.number().nullish(), }); + /** BETA: requires "computer-use-2025-11-24" beta header. Adds enable_zoom. */ + const _ComputerUseTool_20251124_schema = _ComputerUseTool_20250124_schema.extend({ + type: z.literal('computer_20251124'), + enable_zoom: z.boolean().optional(), + }); + const _MemoryTool_20250818_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('memory_20250818'), name: z.literal('memory'), }); + /** BETA (standalone): requires "computer-use-*" beta header. GA as sub-tool of code_execution. */ const _TextEditor_20250728_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('text_editor_20250728'), name: z.literal('str_replace_based_edit_tool'), @@ -685,6 +720,8 @@ export namespace AnthropicWire_Tools { name: z.literal('tool_search_tool_bm25'), }); + // -- Web Fetch Tools -- + const _WebFetchTool_20250910_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('web_fetch_20250910'), name: z.literal('web_fetch'), @@ -695,6 +732,19 @@ export namespace AnthropicWire_Tools { max_uses: z.number().nullish(), }); + /** [Anthropic, Feb 2026 GA] */ + const _WebFetchTool_20260209_schema = _WebFetchTool_20250910_schema.extend({ + type: z.literal('web_fetch_20260209'), + }); + + /** [Anthropic, Mar 2026] Adds `use_cache` - only in SDK types, not yet in public docs. */ + const _WebFetchTool_20260309_schema = _WebFetchTool_20260209_schema.extend({ + type: z.literal('web_fetch_20260309'), + use_cache: z.boolean().optional(), // default true; set false to bypass cache and fetch fresh content + }); + + // -- Web Search Tools -- + const _WebSearchTool_20250305_schema = _ToolDefinitionBase_schema.extend({ type: z.literal('web_search_20250305'), name: z.literal('web_search'), @@ -704,19 +754,30 @@ export namespace AnthropicWire_Tools { user_location: z.any().nullish(), // UserLocation schema }); + /** [Anthropic, Feb 2026 GA] Web search v2 - latest. */ + const _WebSearchTool_20260209_schema = _WebSearchTool_20250305_schema.extend({ + type: z.literal('web_search_20260209'), + }); + + export const ToolDefinition_schema = z.discriminatedUnion('type', [ // Client-side tools _CustomToolDefinition_schema, // Hosted tool definitions & Hosted Tools _BashTool_20250124_schema, _CodeExecutionTool_20250825_schema, - _ComputerUseTool_20250124_schema, + _CodeExecutionTool_20260120_schema, + _ComputerUseTool_20250124_schema, // BETA + _ComputerUseTool_20251124_schema, // BETA _MemoryTool_20250818_schema, _TextEditor_20250728_schema, _ToolSearchToolBM25_20251119_schema, // [Anthropic, 2025-11-24] Tool Search Tool - BM25 variant _ToolSearchToolRegex_20251119_schema, // [Anthropic, 2025-11-24] Tool Search Tool - Regex variant _WebFetchTool_20250910_schema, + _WebFetchTool_20260209_schema, + _WebFetchTool_20260309_schema, _WebSearchTool_20250305_schema, + _WebSearchTool_20260209_schema, ]); } @@ -836,6 +897,12 @@ export namespace AnthropicWire_API_Message_Create { */ tools: z.array(AnthropicWire_Tools.ToolDefinition_schema).optional(), + /** + * [Anthropic, Feb 2026] Top-level cache control for automatic caching. + * When set, automatically caches the last cacheable block in the request. + */ + cache_control: AnthropicWire_Blocks._CacheControl_schema.optional(), + /** * (optional) Metadata to include with the request. * user_id: This should be a uuid, hash value, or other opaque identifier. @@ -856,12 +923,21 @@ export namespace AnthropicWire_API_Message_Create { /** * When enabled, responses include thinking content blocks showing Claude's thinking process before the final answer. + * + * - display: 'omitted': empty thinking field, preserves signature for multi-turn, faster streaming */ thinking: z.union([ // [Anthropic, 4.6+] Adaptive thinking - Claude decides when and how much to think - z.object({ type: z.literal('adaptive') }), + z.object({ + type: z.literal('adaptive'), + display: z.enum(['summarized' /* default */, 'omitted']).optional(), + }), // Requires a minimum budget of 1,024 tokens and counts towards your max_tokens limit. - z.object({ type: z.literal('enabled'), budget_tokens: z.number() }), + z.object({ + type: z.literal('enabled'), + budget_tokens: z.number(), + display: z.enum(['summarized' /* default */, 'omitted']).optional(), + }), // having this for completeness, but seems like it's not needed / can be omitted z.object({ type: z.literal('disabled') }), ]).optional(), @@ -997,6 +1073,11 @@ export namespace AnthropicWire_API_Message_Create { delta: z.object({ stop_reason: StopReason_schema.nullable(), stop_sequence: z.string().nullable(), + /** + * Container state updates - present when Skills/code_execution tools are used. + * Provides container id/expiry that may differ from message_start if the container was created mid-stream. + */ + container: AnthropicWire_Skills.Container_schema.nullish(), }), // MessageDeltaUsage - extended to include cache and server tool metrics usage: z.object({