feat(api-providers): 新增 web_search / image_understanding 声明式后端默认配置 (#31)

- 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 <jackyoncode@gmail.com>
This commit is contained in:
2026-06-09 09:54:27 +08:00
committed by GitHub
parent e84edec964
commit ef632d4e6a
10 changed files with 222 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
{
"description": "image_understanding 工具后端加载顺序索引",
"order": ["configured-vision-openai", "configured-vision-anthropic", "minimax"]
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,4 @@
{
"description": "web_search 工具后端加载顺序索引",
"order": ["tavily", "serper", "minimax"]
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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/<capability>/*.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 }
}
}

View File

@@ -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/<capability>/*.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
}