diff --git a/__tests__/validate.test.mjs b/__tests__/validate.test.mjs index 52201af..bbf5c96 100644 --- a/__tests__/validate.test.mjs +++ b/__tests__/validate.test.mjs @@ -102,6 +102,66 @@ describe('真实数据全量校验', () => { assert.equal(result.ok, true, JSON.stringify(result.errors, null, 2)) }) + it('WebSearch 服务端能力仅标记官方直连 Anthropic/OpenAI 模型', () => { + const anthropic = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'anthropic.json'), 'utf8')) + const openai = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'openai.json'), 'utf8')) + const openaiCodex = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'openai-codex.json'), 'utf8')) + + const enabled = (provider) => provider.models + .filter((model) => model.extra?.serverSideWebSearch?.enabled === true) + .map((model) => model.modelName) + + assert.deepEqual(enabled(anthropic), [ + 'claude-fable-5', + 'claude-opus-4-8', + 'claude-sonnet-5', + 'claude-opus-4-7', + 'claude-sonnet-4-6', + ]) + assert.deepEqual(enabled(openai), [ + 'gpt-5.5', + 'gpt-5.5-pro', + 'gpt-5.4', + 'gpt-5.4-pro', + 'gpt-5.4-mini', + 'gpt-5.4-nano', + 'gpt-5', + 'gpt-4.1', + 'gpt-4.1-mini', + 'o4-mini', + ]) + assert.deepEqual(enabled(openaiCodex), []) + + for (const model of anthropic.models.filter((item) => enabled(anthropic).includes(item.modelName))) { + assert.equal(model.extra.serverSideWebSearch.dialect, 'anthropic-messages') + assert.equal(model.extra.serverSideWebSearch.toolType, 'web_search_20260318') + assert.equal('maxUses' in model.extra.serverSideWebSearch, false) + } + for (const model of openai.models.filter((item) => enabled(openai).includes(item.modelName))) { + assert.equal(model.extra.serverSideWebSearch.dialect, 'openai-responses') + assert.equal(model.extra.serverSideWebSearch.toolType, 'web_search') + assert.equal('maxUses' in model.extra.serverSideWebSearch, false) + } + }) + + it('Brave Search API 使用显式密钥声明且默认关闭', () => { + const brave = JSON.parse(readFileSync(join(ROOT, 'api-providers', 'web-search', 'brave.json'), 'utf8')) + const index = JSON.parse(readFileSync(join(ROOT, 'api-providers', 'web-search', '_index.json'), 'utf8')) + + assert.equal(brave.enabled, false) + assert.equal(brave.endpoint, 'https://api.search.brave.com/res/v1/web/search') + assert.deepEqual(brave.auth, { + type: 'header', + headerName: 'X-Subscription-Token', + apiKeyRef: 'brave', + }) + assert.deepEqual(index.order, ['tavily', 'brave', 'serper']) + assert.equal( + validateFile(join(ROOT, 'api-providers', 'web-search', 'brave.json'), validators).ok, + true, + ) + }) + it('DeepSeek V4 Pro/Flash 应与官方的 1M/384K reasoning profile 一致', () => { const provider = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'deepseek.json'), 'utf8')) const specFile = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'deepseek.json'), 'utf8')) diff --git a/api-providers/web-search/_index.json b/api-providers/web-search/_index.json index 60e0fd3..e9d7d89 100644 --- a/api-providers/web-search/_index.json +++ b/api-providers/web-search/_index.json @@ -1,4 +1,4 @@ { "description": "web_search 工具后端加载顺序索引", - "order": ["tavily", "serper"] + "order": ["tavily", "brave", "serper"] } diff --git a/api-providers/web-search/brave.json b/api-providers/web-search/brave.json new file mode 100644 index 0000000..a3389e1 --- /dev/null +++ b/api-providers/web-search/brave.json @@ -0,0 +1,35 @@ +{ + "id": "brave", + "name": "Brave Search API", + "capability": "web_search", + "enabled": false, + "builtin": true, + "priority": 20, + "endpoint": "https://api.search.brave.com/res/v1/web/search", + "method": "GET", + "auth": { + "type": "header", + "headerName": "X-Subscription-Token", + "apiKeyRef": "brave" + }, + "request": { + "headers": { + "Accept": "application/json", + "Accept-Encoding": "gzip" + }, + "queryParams": { + "q": "${query}", + "count": "${maxResults}" + } + }, + "response": { + "resultsPath": "web.results", + "item": { + "titlePath": "title", + "urlPath": "url", + "snippetPath": "description", + "pageAgePath": "page_age" + } + }, + "timeoutMs": 10000 +} diff --git a/api-providers/web-search/serper.json b/api-providers/web-search/serper.json index cf3671f..c2c781c 100644 --- a/api-providers/web-search/serper.json +++ b/api-providers/web-search/serper.json @@ -4,7 +4,7 @@ "capability": "web_search", "enabled": false, "builtin": true, - "priority": 20, + "priority": 30, "endpoint": "https://google.serper.dev/search", "method": "POST", "auth": { "type": "header", "headerName": "X-API-KEY", "apiKeyRef": "serper" }, diff --git a/compute/providers/anthropic.json b/compute/providers/anthropic.json index 9455297..391a3e7 100644 --- a/compute/providers/anthropic.json +++ b/compute/providers/anthropic.json @@ -35,6 +35,12 @@ "inputPrice": 10, "outputPrice": 50, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "anthropic-messages", + "fallbackPriority": 1, + "toolType": "web_search_20260318" + }, "cachePricing": { "write5m": 12.5, "write1h": 20, @@ -66,6 +72,12 @@ "inputPrice": 5, "outputPrice": 25, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "anthropic-messages", + "fallbackPriority": 2, + "toolType": "web_search_20260318" + }, "cachePricing": { "write5m": 6.25, "write1h": 10, @@ -100,6 +112,12 @@ "inputPrice": 2, "outputPrice": 10, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "anthropic-messages", + "fallbackPriority": 3, + "toolType": "web_search_20260318" + }, "cachePricing": { "write5m": 2.5, "write1h": 4, @@ -133,6 +151,12 @@ "inputPrice": 5, "outputPrice": 25, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "anthropic-messages", + "fallbackPriority": 4, + "toolType": "web_search_20260318" + }, "cachePricing": { "write5m": 6.25, "write1h": 10, @@ -163,6 +187,12 @@ "outputPrice": 15, "defaultTemperature": 1, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "anthropic-messages", + "fallbackPriority": 5, + "toolType": "web_search_20260318" + }, "cachePricing": { "write5m": 3.75, "write1h": 6, diff --git a/compute/providers/openai.json b/compute/providers/openai.json index b6b5517..75b0634 100644 --- a/compute/providers/openai.json +++ b/compute/providers/openai.json @@ -46,6 +46,12 @@ "inputPrice": 5, "outputPrice": 30, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 1, + "toolType": "web_search" + }, "cachedInputPrice": 0.5, "reasoningEffort": [ "none", @@ -79,6 +85,12 @@ "inputPrice": 30, "outputPrice": 180, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 2, + "toolType": "web_search" + }, "responsesOnly": true, "reasoningEffort": [ "medium", @@ -110,6 +122,12 @@ "inputPrice": 2.5, "outputPrice": 15, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 3, + "toolType": "web_search" + }, "cachedInputPrice": 0.25, "reasoningEffort": [ "none", @@ -140,6 +158,12 @@ "inputPrice": 30, "outputPrice": 180, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 4, + "toolType": "web_search" + }, "responsesOnly": true, "reasoningEffort": [ "medium", @@ -172,6 +196,12 @@ "inputPrice": 0.75, "outputPrice": 4.5, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 5, + "toolType": "web_search" + }, "cachedInputPrice": 0.075, "reasoningEffort": [ "none", @@ -201,6 +231,12 @@ "inputPrice": 0.2, "outputPrice": 1.25, "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 6, + "toolType": "web_search" + }, "cachedInputPrice": 0.02, "reasoningEffort": [ "none", @@ -395,7 +431,14 @@ ], "inputPrice": 1.25, "outputPrice": 10, - "extra": {} + "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 7, + "toolType": "web_search" + } + } }, { "modelName": "gpt-5-pro", @@ -477,7 +520,14 @@ "outputPrice": 8, "defaultTemperature": 1, "defaultTopP": 1, - "extra": {} + "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 8, + "toolType": "web_search" + } + } }, { "modelName": "gpt-4.1-mini", @@ -499,7 +549,14 @@ "outputPrice": 1.6, "defaultTemperature": 1, "defaultTopP": 1, - "extra": {} + "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 9, + "toolType": "web_search" + } + } }, { "modelName": "gpt-4.1-nano", @@ -754,7 +811,14 @@ ], "inputPrice": 1.1, "outputPrice": 4.4, - "extra": {} + "extra": { + "serverSideWebSearch": { + "enabled": true, + "dialect": "openai-responses", + "fallbackPriority": 10, + "toolType": "web_search" + } + } } ], "tombstones": [ diff --git a/manifest.json b/manifest.json index 6081df0..b85db5d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "presetDataVersion": 79, - "updatedAt": "2026-07-20", + "presetDataVersion": 80, + "updatedAt": "2026-07-23", "description": "DesireCore 官方配置中心" }