feat: Add Anthropic web search and web fetch tools support

Implements comprehensive support for Anthropic's web search (web_search_20250305) and web fetch (web_fetch_20250910) tools.

- Add llmVndAntWebSearch and llmVndAntWebFetch parameters with ['auto', 'off'] options
- Enable tools for Claude 4.5, 4.1, 4, 3.7, 3.5 Sonnet/Haiku/Opus models (including thinking variants)
- Inject web_search_20250305 and web_fetch_20250910 tools based on parameter values
- Configure web search with max_uses=5 for progressive searches
- Configure web fetch with max_uses=5 and citations enabled
- Add dynamic beta header injection for web fetch (web-fetch-2025-09-10)
- Add UI controls in model settings for easy parameter configuration

Parser already supports web_search_tool_result and web_fetch_tool_result blocks (no changes needed).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Enrico Ros <enricoros@users.noreply.github.com>
This commit is contained in:
claude[bot]
2025-10-18 07:34:51 +00:00
parent a75a31ff04
commit b59eb6cbfb
6 changed files with 161 additions and 12 deletions
+16
View File
@@ -88,6 +88,22 @@ export const DModelParameterRegistry = {
} as const,
} as const,
llmVndAntWebSearch: {
label: 'Web Search',
type: 'enum' as const,
description: 'Enable web search for real-time information',
values: ['auto', 'off'] as const,
// No initialValue - undefined means off (same as 'off')
} as const,
llmVndAntWebFetch: {
label: 'Web Fetch',
type: 'enum' as const,
description: 'Enable fetching content from web pages and PDFs',
values: ['auto', 'off'] as const,
// No initialValue - undefined means off (same as 'off')
} as const,
llmVndGeminiAspectRatio: {
label: 'Aspect Ratio',
type: 'enum' as const,
@@ -406,6 +406,8 @@ export namespace AixWire_API {
topP: z.number().min(0).max(1).optional(),
forceNoStream: z.boolean().optional(),
vndAntThinkingBudget: z.number().nullable().optional(),
vndAntWebSearch: z.enum(['auto', 'off']).optional(),
vndAntWebFetch: z.enum(['auto', 'off']).optional(),
vndGeminiAspectRatio: z.enum(['1:1', '2:3', '3:2', '3:4', '4:3', '9:16', '16:9', '21:9']).optional(),
vndGeminiGoogleSearch: z.enum(['unfiltered', '1d', '1w', '1m', '6m', '1y']).optional(),
vndGeminiShowThoughts: z.boolean().optional(),
@@ -141,12 +141,38 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate:
// --- Tools ---
// Allow/deny auto-adding hosted tools when custom tools are present
// const hasCustomTools = chatGenerate.tools?.some(t => t.type === 'function_call');
// const hasRestrictivePolicy = chatGenerate.toolsPolicy?.type === 'any' || chatGenerate.toolsPolicy?.type === 'function_call';
// const skipHostedToolsDueToCustomTools = hasCustomTools && hasRestrictivePolicy;
const hasCustomTools = chatGenerate.tools?.some(t => t.type === 'function_call');
const hasRestrictivePolicy = chatGenerate.toolsPolicy?.type === 'any' || chatGenerate.toolsPolicy?.type === 'function_call';
const skipHostedToolsDueToCustomTools = hasCustomTools && hasRestrictivePolicy;
// Hosted tools
// ...
if (!skipHostedToolsDueToCustomTools) {
const hostedTools: NonNullable<TRequest['tools']> = [];
// Web Search Tool
if (model.vndAntWebSearch === 'auto') {
hostedTools.push({
type: 'web_search_20250305',
name: 'web_search',
max_uses: 5, // Allow up to 5 progressive searches
});
}
// Web Fetch Tool
if (model.vndAntWebFetch === 'auto') {
hostedTools.push({
type: 'web_fetch_20250910',
name: 'web_fetch',
max_uses: 5, // Allow up to 5 fetches
citations: { enabled: true }, // Enable citations
});
}
// Merge hosted tools with custom tools
if (hostedTools.length > 0) {
payload.tools = payload.tools ? [...payload.tools, ...hostedTools] : hostedTools;
}
}
// Preemptive error detection with server-side payload validation before sending it upstream
@@ -36,15 +36,28 @@ export function createChatGenerateDispatch(access: AixAPI_Access, model: AixAPI_
} {
switch (access.dialect) {
case 'anthropic':
case 'anthropic': {
const anthropicRequest = anthropicAccess(access, model.id, '/v1/messages');
// Add web fetch beta header if enabled
if (model.vndAntWebFetch === 'auto') {
const currentBeta = anthropicRequest.headers['anthropic-beta'] as string || '';
const betaFeatures = currentBeta ? currentBeta.split(',') : [];
if (!betaFeatures.includes('web-fetch-2025-09-10')) {
betaFeatures.push('web-fetch-2025-09-10');
anthropicRequest.headers['anthropic-beta'] = betaFeatures.join(',');
}
}
return {
request: {
...anthropicAccess(access, model.id, '/v1/messages'),
...anthropicRequest,
body: aixToAnthropicMessageCreate(model, chatGenerate, streaming),
},
demuxerFormat: streaming ? 'fast-sse' : null,
chatGenerateParse: streaming ? createAnthropicMessageParser() : createAnthropicMessageParserNS(),
};
}
case 'gemini':
/**
@@ -80,6 +80,18 @@ const _xaiSearchModeOptions = [
{ value: 'off', label: 'Off', description: 'Never perform a search' },
] as const;
const _antWebSearchOptions = [
{ value: 'auto', label: 'On', description: 'Enable web search for real-time information' },
{ value: 'off', label: 'Off', description: 'Disabled (default)' },
{ value: _UNSPECIFIED, label: 'Off', description: 'Disabled (default)' },
] as const;
const _antWebFetchOptions = [
{ value: 'auto', label: 'On', description: 'Enable fetching web content and PDFs' },
{ value: 'off', label: 'Off', description: 'Disabled (default)' },
{ value: _UNSPECIFIED, label: 'Off', description: 'Disabled (default)' },
] as const;
const _imageGenerationOptions = [
{ value: _UNSPECIFIED, label: 'Off', description: 'Default (disabled)' },
{ value: 'mq', label: 'Standard', description: 'Quick gen' },
@@ -132,6 +144,8 @@ export function LLMParametersEditor(props: {
llmTemperature = FALLBACK_LLM_PARAM_TEMPERATURE, // fallback for undefined, result is number | null
llmForceNoStream,
llmVndAntThinkingBudget,
llmVndAntWebSearch,
llmVndAntWebFetch,
llmVndGeminiAspectRatio,
llmVndGeminiGoogleSearch,
llmVndGeminiShowThoughts,
@@ -252,6 +266,32 @@ export function LLMParametersEditor(props: {
/>
)}
{showParam('llmVndAntWebSearch') && (
<FormSelectControl
title='Web Search'
tooltip='Enable web search for real-time information retrieval'
value={llmVndAntWebSearch ?? _UNSPECIFIED}
onChange={(value) => {
if (value === _UNSPECIFIED || !value || value === 'off') onRemoveParameter('llmVndAntWebSearch');
else onChangeParameter({ llmVndAntWebSearch: value });
}}
options={_antWebSearchOptions}
/>
)}
{showParam('llmVndAntWebFetch') && (
<FormSelectControl
title='Web Fetch'
tooltip='Enable fetching full content from web pages and PDF documents'
value={llmVndAntWebFetch ?? _UNSPECIFIED}
onChange={(value) => {
if (value === _UNSPECIFIED || !value || value === 'off') onRemoveParameter('llmVndAntWebFetch');
else onChangeParameter({ llmVndAntWebFetch: value });
}}
options={_antWebFetchOptions}
/>
)}
{showParam('llmVndGeminiAspectRatio') && (
<FormSelectControl
title='Aspect Ratio'
@@ -10,7 +10,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Sonnet 4.5 (Thinking)',
description: 'Claude Sonnet 4.5 with extended thinking mode enabled for complex reasoning',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 64000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
benchmark: { cbaElo: 1451 + 1 }, // FALLBACK-UNTIL-AVAILABLE: claude-opus-4-1-20250805-thinking-16k + 1
@@ -20,7 +24,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Haiku 4.5 (Thinking)',
description: 'Claude Haiku 4.5 with extended thinking mode - first Haiku model with reasoning capabilities',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 64000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
},
@@ -30,7 +38,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Opus 4.1 (Thinking)',
description: 'Claude Opus 4.1 with extended thinking mode enabled for complex reasoning',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 32000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
benchmark: { cbaElo: 1451 }, // claude-opus-4-1-20250805-thinking-16k
@@ -42,7 +54,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Opus 4 (Thinking)',
description: 'Claude Opus 4 with extended thinking mode enabled for complex reasoning',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 32000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
benchmark: { cbaElo: 1420 }, // claude-opus-4-20250514-thinking-16k
@@ -52,7 +68,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Sonnet 4 (Thinking)',
description: 'Claude Sonnet 4 with extended thinking mode enabled for complex reasoning',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 64000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
benchmark: { cbaElo: 1400 }, // claude-sonnet-4-20250514-thinking-32k
@@ -63,7 +83,11 @@ export const hardcodedAnthropicVariants: { [modelId: string]: Partial<ModelDescr
idVariant: 'thinking',
label: 'Claude Sonnet 3.7 (Thinking)',
description: 'Claude 3.7 with extended thinking mode enabled for complex reasoning',
parameterSpecs: [{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false }],
parameterSpecs: [
{ paramId: 'llmVndAntThinkingBudget', required: true, hidden: false },
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
maxCompletionTokens: 64000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching, LLM_IF_OAI_Reasoning],
benchmark: { cbaElo: 1385 }, // claude-3-7-sonnet-20250219-thinking-32k
@@ -83,6 +107,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 64000,
trainingDataCutoff: 'Jul 2025',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
// Note: Tiered pricing - ≤200K: $3/$15, >200K: $6/$22.50. Using lower tier as base.
chatPrice: { input: 3, output: 15, cache: { cType: 'ant-bp', read: 0.30, write: 3.75, duration: 300 } },
benchmark: { cbaElo: 1438 + 1 }, // FALLBACK-UNTIL-AVAILABLE: claude-opus-4-1-20250805 + 1
@@ -95,6 +123,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 64000,
trainingDataCutoff: 'Jul 2025',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 1, output: 5, cache: { cType: 'ant-bp', read: 0.10, write: 1.25, duration: 300 } },
},
@@ -107,6 +139,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 32000,
trainingDataCutoff: 'Mar 2025',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 15, output: 75, cache: { cType: 'ant-bp', read: 1.50, write: 18.75, duration: 300 } },
benchmark: { cbaElo: 1438 }, // claude-opus-4-1-20250805
},
@@ -121,6 +157,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 32000,
trainingDataCutoff: 'Mar 2025',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 15, output: 75, cache: { cType: 'ant-bp', read: 1.50, write: 18.75, duration: 300 } },
benchmark: { cbaElo: 1411 }, // claude-opus-4-20250514
},
@@ -132,6 +172,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 64000,
trainingDataCutoff: 'Mar 2025',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 3, output: 15, cache: { cType: 'ant-bp', read: 0.30, write: 3.75, duration: 300 } },
benchmark: { cbaElo: 1386 }, // claude-sonnet-4-20250514
},
@@ -145,6 +189,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 64000,
trainingDataCutoff: 'Nov 2024',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 3, output: 15, cache: { cType: 'ant-bp', read: 0.30, write: 3.75, duration: 300 } },
benchmark: { cbaElo: 1369 }, // claude-3-7-sonnet-20250219
},
@@ -184,6 +232,10 @@ export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: bo
maxCompletionTokens: 8192,
trainingDataCutoff: 'Jul 2024',
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching],
parameterSpecs: [
{ paramId: 'llmVndAntWebSearch' },
{ paramId: 'llmVndAntWebFetch' },
],
chatPrice: { input: 0.80, output: 4.00, cache: { cType: 'ant-bp', read: 0.08, write: 1.00, duration: 300 } },
benchmark: { cbaElo: 1319, cbaMmlu: 75.2 }, // claude-3-5-haiku-20241022
},