diff --git a/src/renderer/src/aiCore/prepareParams/header.ts b/src/renderer/src/aiCore/prepareParams/header.ts index 8c53cbce53..d818c47943 100644 --- a/src/renderer/src/aiCore/prepareParams/header.ts +++ b/src/renderer/src/aiCore/prepareParams/header.ts @@ -1,13 +1,32 @@ -import { isClaude45ReasoningModel } from '@renderer/config/models' +import { isClaude4SeriesModel, isClaude45ReasoningModel } from '@renderer/config/models' +import { isAwsBedrockProvider } from '@renderer/config/providers' +import { isVertexProvider } from '@renderer/hooks/useVertexAI' +import { getProviderByModel } from '@renderer/services/AssistantService' import type { Assistant, Model } from '@renderer/types' import { isToolUseModeFunction } from '@renderer/utils/assistant' +// https://docs.claude.com/en/docs/build-with-claude/extended-thinking#interleaved-thinking const INTERLEAVED_THINKING_HEADER = 'interleaved-thinking-2025-05-14' +// https://docs.claude.com/en/docs/build-with-claude/context-windows#1m-token-context-window +const CONTEXT_100M_HEADER = 'context-1m-2025-08-07' +// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude/web-search +const WEBSEARCH_HEADER = 'web-search-2025-03-05' export function addAnthropicHeaders(assistant: Assistant, model: Model): string[] { const anthropicHeaders: string[] = [] - if (isClaude45ReasoningModel(model) && isToolUseModeFunction(assistant)) { + const provider = getProviderByModel(model) + if ( + isClaude45ReasoningModel(model) && + isToolUseModeFunction(assistant) && + !(isVertexProvider(provider) && isAwsBedrockProvider(provider)) + ) { anthropicHeaders.push(INTERLEAVED_THINKING_HEADER) } + if (isClaude4SeriesModel(model)) { + if (isVertexProvider(provider) && assistant.enableWebSearch) { + anthropicHeaders.push(WEBSEARCH_HEADER) + } + anthropicHeaders.push(CONTEXT_100M_HEADER) + } return anthropicHeaders } diff --git a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts index e865f9f15f..d55dd9d55e 100644 --- a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts +++ b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts @@ -21,8 +21,6 @@ import { isSupportedThinkingTokenModel, isWebSearchModel } from '@renderer/config/models' -import { isAwsBedrockProvider } from '@renderer/config/providers' -import { isVertexProvider } from '@renderer/hooks/useVertexAI' import { getAssistantSettings, getDefaultModel } from '@renderer/services/AssistantService' import store from '@renderer/store' import type { CherryWebSearchConfig } from '@renderer/store/websearch' @@ -179,8 +177,7 @@ export async function buildStreamTextParams( let headers: Record = options.requestOptions?.headers ?? {} - // https://docs.claude.com/en/docs/build-with-claude/extended-thinking#interleaved-thinking - if (!isVertexProvider(provider) && !isAwsBedrockProvider(provider) && isAnthropicModel(model)) { + if (isAnthropicModel(model)) { const newBetaHeaders = { 'anthropic-beta': addAnthropicHeaders(assistant, model).join(',') } headers = combineHeaders(headers, newBetaHeaders) } diff --git a/src/renderer/src/config/models/reasoning.ts b/src/renderer/src/config/models/reasoning.ts index 0d4c652848..a4e4228149 100644 --- a/src/renderer/src/config/models/reasoning.ts +++ b/src/renderer/src/config/models/reasoning.ts @@ -382,6 +382,12 @@ export function isClaude45ReasoningModel(model: Model): boolean { return regex.test(modelId) } +export function isClaude4SeriesModel(model: Model): boolean { + const modelId = getLowerBaseModelName(model.id, '/') + const regex = /claude-(sonnet|opus|haiku)-4(?:[.-]\d+)?(?:-[\w-]+)?$/i + return regex.test(modelId) +} + export function isClaudeReasoningModel(model?: Model): boolean { if (!model) { return false diff --git a/src/renderer/src/config/models/websearch.ts b/src/renderer/src/config/models/websearch.ts index f7bca774b8..65f938bcc8 100644 --- a/src/renderer/src/config/models/websearch.ts +++ b/src/renderer/src/config/models/websearch.ts @@ -11,10 +11,11 @@ import { isVertexAiProvider } from '../providers' import { isEmbeddingModel, isRerankModel } from './embedding' +import { isClaude4SeriesModel } from './reasoning' import { isAnthropicModel } from './utils' import { isPureGenerateImageModel, isTextToImageModel } from './vision' -export const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp( +const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp( `\\b(?:claude-3(-|\\.)(7|5)-sonnet(?:-[\\w-]+)|claude-3(-|\\.)5-haiku(?:-[\\w-]+)|claude-(haiku|sonnet|opus)-4(?:-[\\w-]+)?)\\b`, 'i' ) @@ -73,11 +74,11 @@ export function isWebSearchModel(model: Model): boolean { const modelId = getLowerBaseModelName(model.id, '/') - // bedrock和vertex不支持 - if ( - isAnthropicModel(model) && - !(provider.id === SystemProviderIds['aws-bedrock'] || provider.id === SystemProviderIds.vertexai) - ) { + // bedrock不支持 + if (isAnthropicModel(model) && !(provider.id === SystemProviderIds['aws-bedrock'])) { + if (isVertexAiProvider(provider)) { + return isClaude4SeriesModel(model) + } return CLAUDE_SUPPORTED_WEBSEARCH_REGEX.test(modelId) }