From ef632d4e6a73d2feb8d9f67384c5f651880efbc2 Mon Sep 17 00:00:00 2001 From: Yige Date: Tue, 9 Jun 2026 09:54:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(api-providers):=20=E6=96=B0=E5=A2=9E=20web?= =?UTF-8?q?=5Fsearch=20/=20image=5Funderstanding=20=E5=A3=B0=E6=98=8E?= =?UTF-8?q?=E5=BC=8F=E5=90=8E=E7=AB=AF=E9=BB=98=E8=AE=A4=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api-providers/{web-search,image-understanding}/*.json + _index.json - 新增 schemas/api-provider.schema.json,validate.mjs 纳入校验(CI 自检) - 不改 compute/ 与 manifest,presetDataVersion 不变:老客户端忽略新目录,线上不受影响 Co-authored-by: yi-ge --- api-providers/image-understanding/_index.json | 4 ++ .../configured-vision-anthropic.json | 32 +++++++++ .../configured-vision-openai.json | 30 ++++++++ .../image-understanding/minimax.json | 18 +++++ api-providers/web-search/_index.json | 4 ++ api-providers/web-search/minimax.json | 20 ++++++ api-providers/web-search/serper.json | 19 +++++ api-providers/web-search/tavily.json | 23 +++++++ schemas/api-provider.schema.json | 69 +++++++++++++++++++ scripts/validate.mjs | 3 + 10 files changed, 222 insertions(+) create mode 100644 api-providers/image-understanding/_index.json create mode 100644 api-providers/image-understanding/configured-vision-anthropic.json create mode 100644 api-providers/image-understanding/configured-vision-openai.json create mode 100644 api-providers/image-understanding/minimax.json create mode 100644 api-providers/web-search/_index.json create mode 100644 api-providers/web-search/minimax.json create mode 100644 api-providers/web-search/serper.json create mode 100644 api-providers/web-search/tavily.json create mode 100644 schemas/api-provider.schema.json diff --git a/api-providers/image-understanding/_index.json b/api-providers/image-understanding/_index.json new file mode 100644 index 0000000..ce36a88 --- /dev/null +++ b/api-providers/image-understanding/_index.json @@ -0,0 +1,4 @@ +{ + "description": "image_understanding 工具后端加载顺序索引", + "order": ["configured-vision-openai", "configured-vision-anthropic", "minimax"] +} diff --git a/api-providers/image-understanding/configured-vision-anthropic.json b/api-providers/image-understanding/configured-vision-anthropic.json new file mode 100644 index 0000000..0d47d02 --- /dev/null +++ b/api-providers/image-understanding/configured-vision-anthropic.json @@ -0,0 +1,32 @@ +{ + "id": "configured-vision-anthropic", + "name": "视觉模型(Anthropic 兼容,复用默认映射)", + "capability": "image_understanding", + "enabled": true, + "builtin": true, + "priority": 20, + "endpoint": "${baseUrl}/messages", + "method": "POST", + "computeServiceType": "vision", + "requiresApiFormat": "anthropic-messages", + "auth": { "type": "header", "headerName": "x-api-key" }, + "request": { + "imageMode": "base64", + "headers": { "anthropic-version": "2023-06-01" }, + "bodyTemplate": { + "model": "${model}", + "max_tokens": 1024, + "messages": [ + { + "role": "user", + "content": [ + { "type": "image", "source": { "type": "base64", "media_type": "${imageMime}", "data": "${imageBase64}" } }, + { "type": "text", "text": "${prompt}" } + ] + } + ] + } + }, + "response": { "textPath": "content.0.text" }, + "timeoutMs": 30000 +} diff --git a/api-providers/image-understanding/configured-vision-openai.json b/api-providers/image-understanding/configured-vision-openai.json new file mode 100644 index 0000000..7e453a4 --- /dev/null +++ b/api-providers/image-understanding/configured-vision-openai.json @@ -0,0 +1,30 @@ +{ + "id": "configured-vision-openai", + "name": "视觉模型(OpenAI 兼容,复用默认映射)", + "capability": "image_understanding", + "enabled": true, + "builtin": true, + "priority": 10, + "endpoint": "${baseUrl}/chat/completions", + "method": "POST", + "computeServiceType": "vision", + "requiresApiFormat": "openai-completions", + "auth": { "type": "bearer" }, + "request": { + "imageMode": "url", + "bodyTemplate": { + "model": "${model}", + "messages": [ + { + "role": "user", + "content": [ + { "type": "text", "text": "${prompt}" }, + { "type": "image_url", "image_url": { "url": "${imageUrl}" } } + ] + } + ] + } + }, + "response": { "textPath": "choices.0.message.content" }, + "timeoutMs": 30000 +} diff --git a/api-providers/image-understanding/minimax.json b/api-providers/image-understanding/minimax.json new file mode 100644 index 0000000..51dc290 --- /dev/null +++ b/api-providers/image-understanding/minimax.json @@ -0,0 +1,18 @@ +{ + "id": "minimax", + "name": "MiniMax 图像理解(内测网关)", + "capability": "image_understanding", + "enabled": false, + "builtin": true, + "priority": 30, + "endpoint": "${baseUrl}/understand_image", + "method": "POST", + "baseUrlRef": "internal-testing", + "auth": { "type": "bearer" }, + "request": { + "imageMode": "url", + "bodyTemplate": { "prompt": "${prompt}", "image_url": "${imageUrl}" } + }, + "response": { "textPath": "text" }, + "timeoutMs": 30000 +} diff --git a/api-providers/web-search/_index.json b/api-providers/web-search/_index.json new file mode 100644 index 0000000..3085cf4 --- /dev/null +++ b/api-providers/web-search/_index.json @@ -0,0 +1,4 @@ +{ + "description": "web_search 工具后端加载顺序索引", + "order": ["tavily", "serper", "minimax"] +} diff --git a/api-providers/web-search/minimax.json b/api-providers/web-search/minimax.json new file mode 100644 index 0000000..0ab0230 --- /dev/null +++ b/api-providers/web-search/minimax.json @@ -0,0 +1,20 @@ +{ + "id": "minimax", + "name": "MiniMax 搜索(内测网关)", + "capability": "web_search", + "enabled": false, + "builtin": true, + "priority": 30, + "endpoint": "${baseUrl}/web_search", + "method": "POST", + "baseUrlRef": "internal-testing", + "auth": { "type": "bearer" }, + "request": { + "bodyTemplate": { "query": "${query}", "num_results": "${maxResults}" } + }, + "response": { + "resultsPath": "results", + "item": { "titlePath": "title", "urlPath": "url", "snippetPath": "content" } + }, + "timeoutMs": 10000 +} diff --git a/api-providers/web-search/serper.json b/api-providers/web-search/serper.json new file mode 100644 index 0000000..cf3671f --- /dev/null +++ b/api-providers/web-search/serper.json @@ -0,0 +1,19 @@ +{ + "id": "serper", + "name": "Serper", + "capability": "web_search", + "enabled": false, + "builtin": true, + "priority": 20, + "endpoint": "https://google.serper.dev/search", + "method": "POST", + "auth": { "type": "header", "headerName": "X-API-KEY", "apiKeyRef": "serper" }, + "request": { + "bodyTemplate": { "q": "${query}", "num": "${maxResults}" } + }, + "response": { + "resultsPath": "organic", + "item": { "titlePath": "title", "urlPath": "link", "snippetPath": "snippet" } + }, + "timeoutMs": 10000 +} diff --git a/api-providers/web-search/tavily.json b/api-providers/web-search/tavily.json new file mode 100644 index 0000000..9f37618 --- /dev/null +++ b/api-providers/web-search/tavily.json @@ -0,0 +1,23 @@ +{ + "id": "tavily", + "name": "Tavily", + "capability": "web_search", + "enabled": false, + "builtin": true, + "priority": 10, + "endpoint": "https://api.tavily.com/search", + "method": "POST", + "auth": { "type": "bearer", "apiKeyRef": "tavily" }, + "request": { + "bodyTemplate": { + "query": "${query}", + "max_results": "${maxResults}", + "search_depth": "basic" + } + }, + "response": { + "resultsPath": "results", + "item": { "titlePath": "title", "urlPath": "url", "snippetPath": "content" } + }, + "timeoutMs": 10000 +} diff --git a/schemas/api-provider.schema.json b/schemas/api-provider.schema.json new file mode 100644 index 0000000..922bb8f --- /dev/null +++ b/schemas/api-provider.schema.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://desirecore.net/schemas/config-center/api-provider.schema.json", + "title": "ApiProvider", + "description": "api-providers//*.json:声明式外部 API 供应商(web_search / image_understanding 工具后端)。镜像 desirecore 主仓 lib/schemas/agent-service/api-provider.ts。", + "type": "object", + "required": ["id", "name", "capability", "enabled", "endpoint", "auth", "request", "response"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "description": "同 capability 下唯一标识" }, + "name": { "type": "string", "description": "显示名称" }, + "capability": { + "type": "string", + "enum": ["web_search", "image_understanding"], + "description": "服务的工具能力" + }, + "enabled": { "type": "boolean", "description": "是否启用" }, + "builtin": { "type": "boolean", "description": "是否为内置默认", "default": false }, + "priority": { "type": "number", "description": "优先级,越小越优先", "default": 100 }, + "endpoint": { "type": "string", "description": "API 端点 URL,支持 ${变量}" }, + "method": { "type": "string", "enum": ["POST", "GET"], "description": "HTTP 方法", "default": "POST" }, + "computeServiceType": { "type": "string", "description": "复用算力默认映射的服务类型(如 vision)" }, + "requiresApiFormat": { "type": "string", "description": "仅当解析出的 provider apiFormat 等于此值时可用" }, + "baseUrlRef": { "type": "string", "description": "指向某算力 provider 的 provider 标识,取其 baseUrl 与密钥" }, + "auth": { + "type": "object", + "description": "鉴权配置", + "required": ["type"], + "additionalProperties": false, + "properties": { + "type": { "type": "string", "enum": ["bearer", "header", "query", "none"] }, + "apiKeyRef": { "type": "string", "description": "secrets.json 中的密钥引用名" }, + "headerName": { "type": "string", "description": "type=header 时的请求头名称" }, + "queryParam": { "type": "string", "description": "type=query 时的查询参数名" } + } + }, + "request": { + "type": "object", + "description": "请求构造,字符串值支持 ${变量}", + "additionalProperties": false, + "properties": { + "headers": { "type": "object", "additionalProperties": { "type": "string" } }, + "bodyTemplate": { "type": "object", "additionalProperties": true }, + "queryParams": { "type": "object", "additionalProperties": { "type": "string" } }, + "imageMode": { "type": "string", "enum": ["url", "base64"] } + } + }, + "response": { + "type": "object", + "description": "响应提取", + "additionalProperties": false, + "properties": { + "resultsPath": { "type": "string", "description": "web_search 结果数组的点路径" }, + "item": { + "type": "object", + "additionalProperties": false, + "properties": { + "titlePath": { "type": "string" }, + "urlPath": { "type": "string" }, + "snippetPath": { "type": "string" }, + "pageAgePath": { "type": "string" } + } + }, + "textPath": { "type": "string", "description": "image_understanding 结果文本的点路径" } + } + }, + "timeoutMs": { "type": "number", "description": "请求超时(毫秒)", "default": 10000 } + } +} diff --git a/scripts/validate.mjs b/scripts/validate.mjs index eb6bc5b..75549f8 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -52,6 +52,9 @@ function pickSchemaKey(absPath) { if (rel.startsWith('compute/providers/') && rel.endsWith('.json')) return 'provider' if (rel.startsWith('compute/coding-plans/') && rel.endsWith('.json')) return 'provider' if (rel.startsWith('compute/model-specs/') && rel.endsWith('.json')) return 'model-spec' + // api-providers//*.json:声明式外部 API 供应商(_index.json 复用 providers-index) + if (rel.startsWith('api-providers/') && rel.endsWith('/_index.json')) return 'providers-index' + if (rel.startsWith('api-providers/') && rel.endsWith('.json')) return 'api-provider' return null }