LLMs: auto-inject web search

This commit is contained in:
Enrico Ros
2026-02-04 20:14:51 -08:00
parent 80fcc7d3e3
commit 4ea8a06503
6 changed files with 60 additions and 37 deletions
@@ -1,6 +1,6 @@
import * as z from 'zod/v4';
import { LLM_IF_ANT_PromptCaching, LLM_IF_ANT_ToolsSearch, LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Vision, LLM_IF_Tools_WebSearch } from '~/common/stores/llms/llms.types';
import { LLM_IF_ANT_PromptCaching, LLM_IF_ANT_ToolsSearch, LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Vision } from '~/common/stores/llms/llms.types';
import { Release } from '~/common/app.release';
import type { ModelDescriptionSchema } from '../llm.server.types';
@@ -314,16 +314,3 @@ export function llmsAntCreatePlaceholderModel(model: AnthropicWire_API_Models_Li
};
}
/**
* Injects the LLM_IF_Tools_WebSearch interface for models that have web search/fetch parameters.
* This allows the UI to show the web search indicator automatically based on model capabilities.
*/
export function llmsAntInjectWebSearchInterface(model: ModelDescriptionSchema): ModelDescriptionSchema {
const hasWebParams = model.parameterSpecs?.some(spec =>
spec.paramId === 'llmVndAntWebSearch' || spec.paramId === 'llmVndAntWebFetch',
);
return (hasWebParams && !model.interfaces?.includes(LLM_IF_Tools_WebSearch)) ? {
...model,
interfaces: [...model.interfaces, LLM_IF_Tools_WebSearch],
} : model;
}
@@ -8,10 +8,11 @@ import { createDebugWireLogger } from '~/server/wire';
import { fetchJsonOrTRPCThrow } from '~/server/trpc/trpc.router.fetchers';
import type { ModelDescriptionSchema } from './llm.server.types';
import { llmsAutoInjectWebSearchInterface } from './models.mappings';
// protocol: Anthropic
import { anthropicInjectVariants, anthropicValidateModelDefs_DEV, AnthropicWire_API_Models_List, hardcodedAnthropicModels, llmsAntCreatePlaceholderModel, llmsAntInjectWebSearchInterface } from './anthropic/anthropic.models';
import { anthropicInjectVariants, anthropicValidateModelDefs_DEV, AnthropicWire_API_Models_List, hardcodedAnthropicModels, llmsAntCreatePlaceholderModel } from './anthropic/anthropic.models';
import { ANTHROPIC_API_PATHS, anthropicAccess } from './anthropic/anthropic.access';
// protocol: Gemini
@@ -69,7 +70,8 @@ function createDispatch<T>(dispatch: ListModelsDispatch<T>): ListModelsDispatch<
export async function listModelsRunDispatch(access: AixAPI_Access, signal?: AbortSignal): Promise<ModelDescriptionSchema[]> {
const dispatch = _listModelsCreateDispatch(access, signal);
const wireModels = await dispatch.fetchModels();
return dispatch.convertToDescriptions(wireModels);
return dispatch.convertToDescriptions(wireModels)
.map(llmsAutoInjectWebSearchInterface); // unified way of auto-injecting cosmetic/derived IFs
}
@@ -146,8 +148,7 @@ function _listModelsCreateDispatch(access: AixAPI_Access, signal?: AbortSignal):
return llmsAntCreatePlaceholderModel(model);
})
// inject thinking variants using the centralized variant system
.reduce(anthropicInjectVariants, [])
.map(llmsAntInjectWebSearchInterface);
.reduce(anthropicInjectVariants, []);
},
});
}
@@ -1,6 +1,40 @@
import type { DModelParameterId } from '~/common/stores/llms/llms.parameters';
import { LLM_IF_Tools_WebSearch } from '~/common/stores/llms/llms.types';
import type { ModelDescriptionSchema } from './llm.server.types';
// -- Auto-inject web search interface --
/**
* Parameter IDs that imply the model supports web search capabilities.
* When any of these are present in parameterSpecs, LLM_IF_Tools_WebSearch is auto-added to interfaces.
*/
const _webSearchParamIds: string[] = [
'llmVndAntWebFetch',
'llmVndAntWebSearch',
'llmVndGeminiGoogleSearch',
'llmVndMoonshotWebSearch',
'llmVndOaiWebSearchContext',
'llmVndOrtWebSearch',
'llmVndPerplexitySearchMode',
'llmVndXaiWebSearch',
'llmVndXaiXSearch',
] as const satisfies DModelParameterId[];
/**
* Auto-injects LLM_IF_Tools_WebSearch for models that have web search/fetch parameters.
* Applied centrally in listModelsRunDispatch so individual vendors don't need to manage this.
*/
export function llmsAutoInjectWebSearchInterface(model: ModelDescriptionSchema): ModelDescriptionSchema {
const hasWebParams = model.parameterSpecs?.some(spec => _webSearchParamIds.includes(spec.paramId));
return (hasWebParams && !model.interfaces?.includes(LLM_IF_Tools_WebSearch)) ? {
...model,
interfaces: [...model.interfaces, LLM_IF_Tools_WebSearch],
} : model;
}
// -- Dev model definitions check --
/**
@@ -1,6 +1,6 @@
import type { OpenAIWire_API_Models_List } from '~/modules/aix/server/dispatch/wiretypes/openai.wiretypes';
import { DModelInterfaceV1, LLM_IF_HOTFIX_NoTemperature, LLM_IF_HOTFIX_StripImages, LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Responses, LLM_IF_OAI_Vision, LLM_IF_Outputs_Audio, LLM_IF_Tools_WebSearch } from '~/common/stores/llms/llms.types';
import { DModelInterfaceV1, LLM_IF_HOTFIX_NoTemperature, LLM_IF_HOTFIX_StripImages, LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Responses, LLM_IF_OAI_Vision, LLM_IF_Outputs_Audio } from '~/common/stores/llms/llms.types';
import { Release } from '~/common/app.release';
import type { ModelDescriptionSchema } from '../../llm.server.types';
@@ -19,7 +19,7 @@ export const hardcodedOpenAIVariants: ModelVariantMap = {
label: 'GPT-5.2 (No-thinking)',
hidden: true, // hidden by default as redundant, user can unhide in settings
description: 'Supports temperature control for creative applications. GPT-5.2 with reasoning disabled (reasoning_effort=none).',
interfaces: [LLM_IF_OAI_Responses, LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching, LLM_IF_Tools_WebSearch], // NO LLM_IF_OAI_Reasoning, NO LLM_IF_HOTFIX_NoTemperature
interfaces: [LLM_IF_OAI_Responses, LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching], // NO LLM_IF_OAI_Reasoning, NO LLM_IF_HOTFIX_NoTemperature
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort52', initialValue: 'none', hidden: true }, // factory 'none', not changeable
{ paramId: 'llmVndOaiWebSearchContext' },
@@ -59,6 +59,7 @@ const IFS_GPT_AUDIO: DModelInterfaceV1[] = [LLM_IF_OAI_Chat, LLM_IF_Outputs_Audi
const IFS_CHAT_MIN: DModelInterfaceV1[] = [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json] as const;
const IFS_CHAT_CACHE: DModelInterfaceV1[] = [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching] as const;
const IFS_CHAT_CACHE_REASON: DModelInterfaceV1[] = [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_PromptCaching, LLM_IF_OAI_Reasoning] as const;
// NOTE: 'LLM_IF_Tools_WebSearch' is auto-injected by llmsAutoInjectWebSearchInterface() for models with web search parameterSpecs - no need to add it manually
// per-type parameter specs
const PS_DEEP_RESEARCH = [{ paramId: 'llmVndOaiWebSearchContext' as const, initialValue: 'medium', hidden: true } as const];
@@ -80,7 +81,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'Most capable model for professional work and long-running agents. Improvements in general intelligence, long-context, agentic tool-calling, and vision.',
contextWindow: 400000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort52', initialValue: 'medium' /* our decision: set to medium to have thinking - clones can set to 'none' to have temperature */ },
{ paramId: 'llmVndOaiWebSearchContext' },
@@ -122,7 +123,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'GPT-5.2 model powering ChatGPT. Fast, capable for everyday work with clear improvements in info-seeking, how-tos, technical writing.',
contextWindow: 128000,
maxCompletionTokens: 16384,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiWebSearchContext' },
// { paramId: 'llmVndOaiVerbosity' }, // 2026-01-20: still unsupported
@@ -166,7 +167,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'The best model for coding and agentic tasks with configurable reasoning effort.',
contextWindow: 400000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort4' }, { paramId: 'llmVndOaiWebSearchContext' },
{ paramId: 'llmVndOaiVerbosity' },
@@ -191,7 +192,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
contextWindow: 128000,
maxCompletionTokens: 16384,
// interfaces: [LLM_IF_OAI_Responses, LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_PromptCaching], // no function calling or reasoning
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiWebSearchContext' },
// { paramId: 'llmVndOaiVerbosity' }, // 2026-01-20: still unsupported
@@ -258,7 +259,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'The best model for coding and agentic tasks across domains.',
contextWindow: 400000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort4' }, { paramId: 'llmVndOaiWebSearchContext' },
{ paramId: 'llmVndOaiVerbosity' }, // gpt-5-class nets have verbosity control
@@ -312,7 +313,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'A version of GPT-5 optimized for agentic coding in Codex.',
contextWindow: 400000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort' }, // works
{ paramId: 'llmVndOaiWebSearchContext' }, // works, although is not triggered often
@@ -330,7 +331,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'Updated web search model in Chat Completions API. 60% cheaper with domain filtering support.',
contextWindow: 400000,
maxCompletionTokens: 100000,
interfaces: [...IFS_CHAT_MIN, LLM_IF_Tools_WebSearch],
interfaces: IFS_CHAT_MIN,
parameterSpecs: [{ paramId: 'llmVndOaiWebSearchContext', initialValue: 'medium' }], // Search enabled by default
chatPrice: { input: 1.25, cache: { cType: 'oai-ac', read: 0.125 }, output: 10 },
// benchmark: TBD
@@ -348,7 +349,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'A faster, more cost-efficient version of GPT-5 for well-defined tasks.',
contextWindow: 400000,
maxCompletionTokens: 128000,
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature],
interfaces: [LLM_IF_OAI_Responses, ...IFS_CHAT_CACHE_REASON, LLM_IF_HOTFIX_NoTemperature],
parameterSpecs: [{ paramId: 'llmVndOaiReasoningEffort4' }, { paramId: 'llmVndOaiWebSearchContext' }, { paramId: 'llmVndOaiVerbosity' }, { paramId: 'llmVndOaiImageGeneration' }, { paramId: 'llmForceNoStream' }],
chatPrice: { input: 0.25, cache: { cType: 'oai-ac', read: 0.025 }, output: 2 },
benchmark: { cbaElo: 1390 }, // gpt-5-mini-high
@@ -733,7 +734,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'Latest snapshot of the GPT-4o model optimized for web search capabilities.',
contextWindow: 128000,
maxCompletionTokens: 16384,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Json, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature], // NOTE: 2025-03-15: confirmed on 'playground' that this model does not support images
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Json, LLM_IF_HOTFIX_NoTemperature], // NOTE: 2025-03-15: confirmed on 'playground' that this model does not support images
parameterSpecs: [{ paramId: 'llmVndOaiWebSearchContext' }, { paramId: 'llmVndOaiWebSearchGeolocation' }],
chatPrice: { input: 2.5, output: 10 },
// benchmarks don't apply to search models
@@ -817,7 +818,7 @@ export const _knownOpenAIChatModels: ManualMappings = [
description: 'Latest snapshot of the GPT-4o Mini model optimized for web search capabilities.',
contextWindow: 128000,
maxCompletionTokens: 16384,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Json, LLM_IF_Tools_WebSearch, LLM_IF_HOTFIX_NoTemperature], // NOTE: this support function calling, but only its own, not a Custom Function
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Json, LLM_IF_HOTFIX_NoTemperature], // NOTE: this support function calling, but only its own, not a Custom Function
parameterSpecs: [{ paramId: 'llmVndOaiWebSearchContext' }, { paramId: 'llmVndOaiWebSearchGeolocation' }],
chatPrice: { input: 0.15, output: 0.6 },
// benchmarks don't apply to search models
@@ -1,7 +1,7 @@
import type { ModelDescriptionSchema } from '../../llm.server.types';
import { createVariantInjector, ModelVariantMap } from '../../llm.server.variants';
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning, LLM_IF_Tools_WebSearch } from '~/common/stores/llms/llms.types';
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning } from '~/common/stores/llms/llms.types';
// configuration
@@ -38,7 +38,7 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
label: 'Sonar Deep Research',
description: 'Expert-level research model for exhaustive searches and comprehensive reports. 128k context.',
contextWindow: 128000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning, LLM_IF_Tools_WebSearch],
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning],
parameterSpecs: [
{ paramId: 'llmVndOaiReasoningEffort' }, // REUSE!
{ paramId: 'llmVndOaiWebSearchContext', initialValue: 'low' }, // REUSE!
@@ -58,7 +58,7 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
label: 'Sonar Reasoning Pro',
description: 'Premier reasoning model (DeepSeek R1) with Chain of Thought. 128k context.',
contextWindow: 128000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning, LLM_IF_Tools_WebSearch],
interfaces: [LLM_IF_OAI_Chat, LLM_IF_OAI_Reasoning],
parameterSpecs: [
{ paramId: 'llmVndOaiWebSearchContext', initialValue: 'low' }, // REUSE!
{ paramId: 'llmVndPerplexitySearchMode' },
@@ -78,7 +78,7 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
description: 'Advanced search model for complex queries and deep content understanding. 200k context.',
contextWindow: 200000,
maxCompletionTokens: 8000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_Tools_WebSearch],
interfaces: [LLM_IF_OAI_Chat],
parameterSpecs: [
{ paramId: 'llmVndOaiWebSearchContext', initialValue: 'low' }, // REUSE!
{ paramId: 'llmVndPerplexitySearchMode' },
@@ -95,7 +95,7 @@ const _knownPerplexityChatModels: ModelDescriptionSchema[] = [
label: 'Sonar',
description: 'Lightweight, cost-effective search model for quick, grounded answers. 128k context.',
contextWindow: 128000,
interfaces: [LLM_IF_OAI_Chat, LLM_IF_Tools_WebSearch],
interfaces: [LLM_IF_OAI_Chat],
parameterSpecs: [
{ paramId: 'llmVndOaiWebSearchContext', initialValue: 'low' }, // REUSE!
{ paramId: 'llmVndPerplexitySearchMode' },
@@ -2,7 +2,7 @@ import * as z from 'zod/v4';
import { fetchJsonOrTRPCThrow } from '~/server/trpc/trpc.router.fetchers';
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Vision, LLM_IF_Tools_WebSearch } from '~/common/stores/llms/llms.types';
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Vision } from '~/common/stores/llms/llms.types';
import { Release } from '~/common/app.release';
import type { ModelDescriptionSchema } from '../../llm.server.types';
@@ -36,7 +36,7 @@ const PRICE_40 = {
// we don't add LLM_IF_OAI_Responses explicitly here, as the code fully treats XAI/XAI Models with responses
const XAI_IF: ModelDescriptionSchema['interfaces'] = [
LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json, LLM_IF_Tools_WebSearch,
LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Json,
] as const;
const XAI_IF_Vision: ModelDescriptionSchema['interfaces'] = [