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 }