diff --git a/apps/server/src/services/llm/providers/anthropic.ts b/apps/server/src/services/llm/providers/anthropic.ts index d444b34a51..5175ed2370 100644 --- a/apps/server/src/services/llm/providers/anthropic.ts +++ b/apps/server/src/services/llm/providers/anthropic.ts @@ -89,19 +89,17 @@ export class AnthropicProvider extends BaseProvider { return this.anthropic(modelId); } + protected override addWebSearchTool(tools: ToolSet): void { + tools.web_search = this.anthropic.tools.webSearch_20250305({ + maxUses: 5 + }); + } + /** - * Override chat to add Anthropic-specific features: - * - Prompt caching via providerOptions - * - Extended thinking - * - Web search tool + * Override buildMessages to add Anthropic-specific cache control breakpoints. */ - override chat(messages: LlmMessage[], config: LlmProviderConfig): StreamResult { - const systemPrompt = this.buildSystemPrompt(messages, config); - const chatMessages = messages.filter(m => m.role !== "system"); - - // Anthropic-specific: cache control breakpoints on system prompt and conversation history + protected override buildMessages(chatMessages: LlmMessage[], systemPrompt: string | undefined): CoreMessage[] { const CACHE_CONTROL = { anthropic: { cacheControl: { type: "ephemeral" as const } } }; - const coreMessages: CoreMessage[] = []; if (systemPrompt) { @@ -122,38 +120,39 @@ export class AnthropicProvider extends BaseProvider { }); } + return coreMessages; + } + + /** + * Override chat to add Anthropic-specific extended thinking support. + */ + override chat(messages: LlmMessage[], config: LlmProviderConfig): StreamResult { + if (!config.enableExtendedThinking) { + return super.chat(messages, config); + } + + const systemPrompt = this.buildSystemPrompt(messages, config); + const chatMessages = messages.filter(m => m.role !== "system"); + const coreMessages = this.buildMessages(chatMessages, systemPrompt); + + const thinkingBudget = config.thinkingBudget || 10000; + const maxTokens = Math.max(config.maxTokens || 8096, thinkingBudget + 4000); + const streamOptions: Parameters[0] = { model: this.createModel(config.model || this.defaultModel), messages: coreMessages, - maxOutputTokens: config.maxTokens || 8096 - }; - - // Anthropic-specific: extended thinking - if (config.enableExtendedThinking) { - const thinkingBudget = config.thinkingBudget || 10000; - streamOptions.providerOptions = { + maxOutputTokens: maxTokens, + providerOptions: { anthropic: { thinking: { type: "enabled", budgetTokens: thinkingBudget } } - }; - streamOptions.maxOutputTokens = Math.max( - streamOptions.maxOutputTokens || 8096, - thinkingBudget + 4000 - ); - } - - // Build tools (shared + Anthropic-specific web search) - const tools: ToolSet = this.buildTools(config); - - if (config.enableWebSearch) { - tools.web_search = this.anthropic.tools.webSearch_20250305({ - maxUses: 5 - }); - } + } + }; + const tools = this.buildTools(config); if (Object.keys(tools).length > 0) { streamOptions.tools = tools; streamOptions.stopWhen = stepCountIs(5); diff --git a/apps/server/src/services/llm/providers/base_provider.ts b/apps/server/src/services/llm/providers/base_provider.ts index bcc22bd3c0..02fd4c6bb3 100644 --- a/apps/server/src/services/llm/providers/base_provider.ts +++ b/apps/server/src/services/llm/providers/base_provider.ts @@ -113,12 +113,21 @@ export abstract class BaseProvider implements LlmProvider { return coreMessages; } + /** + * Add provider-specific web search tool. Override in subclasses that support it. + */ + protected addWebSearchTool(_tools: ToolSet): void {} + /** * Build the tool set based on config. */ protected buildTools(config: LlmProviderConfig): ToolSet { const tools: ToolSet = {}; + if (config.enableWebSearch) { + this.addWebSearchTool(tools); + } + if (config.contextNoteId) { Object.assign(tools, currentNoteTools(config.contextNoteId)); } diff --git a/apps/server/src/services/llm/providers/openai.ts b/apps/server/src/services/llm/providers/openai.ts index a44ac9505b..759d31c7e7 100644 --- a/apps/server/src/services/llm/providers/openai.ts +++ b/apps/server/src/services/llm/providers/openai.ts @@ -1,4 +1,5 @@ import { createOpenAI, type OpenAIProvider as OpenAISDKProvider } from "@ai-sdk/openai"; +import type { ToolSet } from "ai"; import { BaseProvider, buildModelList } from "./base_provider.js"; @@ -76,4 +77,8 @@ export class OpenAiProvider extends BaseProvider { protected createModel(modelId: string) { return this.openai(modelId); } + + protected override addWebSearchTool(tools: ToolSet): void { + tools.web_search = this.openai.tools.webSearch(); + } }