mirror of
https://git.openapi.site/https://github.com/desirecore/config-center.git
synced 2026-10-21 06:44:14 +08:00
Claude Opus 4.7+ / Opus 5 / Fable 携带 temperature / top_p / top_k 会被官方 400 (Sonnet 5 非默认值 400),Claude Fable 5.1 对 tool_choice any / tool 回 400。客户端 改为纯按声明在出站删除采样参数、把强制工具选择降级为 auto(desirecore 主仓配套 PR)。 - providers/anthropic.json:claude-fable-5-1 增加 forcedToolChoiceUnsupported - model-specs/anthropic.json:fable-5-1 / opus-5 / opus-4-8 / opus-4-7 / sonnet-5 声明 samplingParametersDeprecated,fable-5-1 声明 forcedToolChoiceUnsupported,供精确命中 补全的动态 Provider 使用;opus-4-7 的 defaultTemperature 由 1 改为 null(不支持温度调节) - schemas:provider model extra 与 ModelSpec spec.extra 声明两个布尔字段(含 description) - 测试:已知拒绝代际必须声明(单向)、布尔类型校验 - presetDataVersion 116 → 117
348 lines
20 KiB
JSON
348 lines
20 KiB
JSON
{
|
||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||
"$id": "https://desirecore.net/schemas/config-center/provider.schema.json",
|
||
"title": "Provider",
|
||
"description": "frozen baseline schema 镜像 desirecore d185299(fix #471 之前)的 computeProviderSchema/providerModelSchema,作为已发布客户端兼容契约。任何写入 compute/providers/*.json 或 compute/coding-plans/*.json 的数据都必须通过此校验,否则会破坏老客户端(已发布版本的 schema 无法理解超出此契约的字段或类型)。\n\n关键约束:\n- defaultTemperature / defaultTopP 必须是 number(不接受 null)—— 历史教训:曾因 null 写入导致老客户端校验失败死锁\n- 顶层与 model 内 additionalProperties 均为 false —— 新增字段必须先升级老客户端 schema 再推送数据",
|
||
"type": "object",
|
||
"required": [
|
||
"id",
|
||
"provider",
|
||
"label",
|
||
"baseUrl",
|
||
"apiKeyRef",
|
||
"apiKeyVerified",
|
||
"enabled",
|
||
"status",
|
||
"services",
|
||
"models"
|
||
],
|
||
"properties": {
|
||
"id": {
|
||
"type": "string",
|
||
"description": "提供商唯一标识符(如 provider-openai-001)",
|
||
"minLength": 1
|
||
},
|
||
"provider": {
|
||
"type": "string",
|
||
"description": "提供商标识:openai、anthropic、deepseek、dashscope(阿里云)等",
|
||
"minLength": 1
|
||
},
|
||
"label": {
|
||
"type": "string",
|
||
"description": "提供商显示名称(如 OpenAI、Anthropic、阿里云 DashScope)",
|
||
"minLength": 1
|
||
},
|
||
"baseUrl": {
|
||
"type": "string",
|
||
"description": "API 基础 URL(如 https://api.openai.com/v1)",
|
||
"minLength": 1
|
||
},
|
||
"apiFormat": {
|
||
"type": "string",
|
||
"description": "API 协议格式:openai-completions、anthropic-messages、openai-responses、google-generative-ai 等"
|
||
},
|
||
"mediaBaseUrl": {
|
||
"type": "string",
|
||
"description": "媒体 API 基础 URL。当 provider 使用 Anthropic 格式(baseUrl 指向 /anthropic 端点)但其媒体 API(图片/语音/视频/音乐)仍走 OpenAI /v1 端点时使用"
|
||
},
|
||
"apiKeyRef": {
|
||
"type": "string",
|
||
"description": "密钥引用名,对应 secrets.json 中的 key;空字符串表示未配置密钥"
|
||
},
|
||
"apiKeyVerified": {
|
||
"type": "boolean",
|
||
"description": "API Key 是否已通过验证"
|
||
},
|
||
"enabled": {
|
||
"type": "boolean",
|
||
"description": "是否启用此提供商"
|
||
},
|
||
"status": {
|
||
"type": "string",
|
||
"enum": ["configured", "unconfigured", "error"],
|
||
"description": "提供商状态"
|
||
},
|
||
"services": {
|
||
"type": "array",
|
||
"description": "支持的服务类型列表(如 chat、reasoning、vision、embedding)",
|
||
"items": { "type": "string" }
|
||
},
|
||
"priceCurrency": {
|
||
"type": "string",
|
||
"enum": ["USD", "CNY"],
|
||
"description": "价格货币单位。models 中的 inputPrice/outputPrice 均以此货币计价"
|
||
},
|
||
"accessMode": {
|
||
"type": "string",
|
||
"enum": ["api", "coding-plan", "token-plan"],
|
||
"description": "接入模式:api(按量付费)、coding-plan(编程订阅套餐)或 token-plan(Token 团队版订阅)"
|
||
},
|
||
"brandGroup": {
|
||
"type": "string",
|
||
"description": "品牌分组标识,UI 按此字段排序"
|
||
},
|
||
"credentialSource": {
|
||
"type": "string",
|
||
"enum": ["codex-cli", "claude-oauth", "github-copilot"],
|
||
"description": "凭证托管来源。codex-cli:密钥由客户端本地 Codex CLI 凭证检测器托管(读取 ~/.codex/auth.json 自动刷新写回,fresh token 同步 secrets.json)。claude-oauth:Claude 订阅接入,由客户端 claude-auth 检测器托管——首选复用本机 Claude Code 登录(Agent SDK 自读,不写不刷新),或应用内 OAuth / setup-token 兜底;模型调用经 compat-proxy 内 claude-agent-sdk 后端履约,不直连。github-copilot:GitHub Copilot 订阅接入,由客户端 copilot-auth 检测器托管——GitHub Device Flow 登录,长期 GitHub OAuth token 存 config/copilot-oauth.json,定期交换约 30 分钟有效的 Copilot session token 写入 secrets.json;模型调用携带 Copilot 协议 headers 直连交换响应 endpoints.api 指定域名,模型列表由客户端在登录后经 /models 动态发现(可用集合随订阅档位与组织策略变化,故预设 models 为空)。需 requiredClientVersion ≥ 引入此字段的客户端版本——老客户端不识别本字段,必须先发版铺开 compute.json 韧性(容忍未知字段)再推送本数据"
|
||
},
|
||
"codingPlan": {
|
||
"type": "object",
|
||
"description": "Coding Plan / Token Plan 专属配置(仅当 accessMode = coding-plan 或 token-plan 时有效)",
|
||
"properties": {
|
||
"planTier": { "type": "string" },
|
||
"planLabel": { "type": "string" },
|
||
"quotas": {
|
||
"type": "object",
|
||
"properties": {
|
||
"per5h": { "type": "number", "minimum": 0 },
|
||
"perWeek": { "type": "number", "minimum": 0 },
|
||
"perMonth": { "type": "number", "minimum": 0 },
|
||
"per7d": { "type": "number", "minimum": 0 }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"usageTracking": {
|
||
"type": "object",
|
||
"properties": {
|
||
"method": {
|
||
"type": "string",
|
||
"enum": ["rest-api", "response-header", "manual", "credits", "none"]
|
||
},
|
||
"endpoint": { "type": "string" },
|
||
"headerKeys": {
|
||
"type": "object",
|
||
"properties": {
|
||
"remaining": { "type": "string" },
|
||
"limit": { "type": "string" },
|
||
"reset": { "type": "string" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"consoleUrl": { "type": "string" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"modelIdOverride": { "type": "string" },
|
||
"maxConcurrent": { "type": "number", "minimum": 1 },
|
||
"expiresAt": { "type": "string", "format": "date-time" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"tokenPlan": {
|
||
"type": "object",
|
||
"description": "Token Plan 团队版专属配置(仅当 accessMode = token-plan 时有效)",
|
||
"properties": {
|
||
"planTier": { "type": "string" },
|
||
"planLabel": { "type": "string" },
|
||
"quotas": {
|
||
"type": "object",
|
||
"properties": {
|
||
"per5h": { "type": "number", "minimum": 0 },
|
||
"perWeek": { "type": "number", "minimum": 0 },
|
||
"perMonth": { "type": "number", "minimum": 0 },
|
||
"per7d": { "type": "number", "minimum": 0 }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"usageTracking": {
|
||
"type": "object",
|
||
"properties": {
|
||
"method": {
|
||
"type": "string",
|
||
"enum": ["rest-api", "response-header", "manual", "credits", "none"]
|
||
},
|
||
"endpoint": { "type": "string" },
|
||
"headerKeys": {
|
||
"type": "object",
|
||
"properties": {
|
||
"remaining": { "type": "string" },
|
||
"limit": { "type": "string" },
|
||
"reset": { "type": "string" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"consoleUrl": { "type": "string" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"modelIdOverride": { "type": "string" },
|
||
"maxConcurrent": { "type": "number", "minimum": 1 },
|
||
"expiresAt": { "type": "string", "format": "date-time" }
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"models": {
|
||
"type": "array",
|
||
"description": "此接入面当前可调用的模型列表。可用性、凭据、价格与 API 映射属于 Provider;模型能力、上下文、Smart tier 与产品 reasoning 合同以 compute/model-specs 为准。",
|
||
"items": { "$ref": "#/definitions/model" }
|
||
},
|
||
"tombstones": {
|
||
"type": "array",
|
||
"description": "预置显式删除的模型 modelName 白名单",
|
||
"items": { "type": "string" }
|
||
},
|
||
"requiredClientVersion": {
|
||
"type": "string",
|
||
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
||
"description": "使用此 provider 所需的最低客户端版本(semver x.y.z)。对已铺开韧性(#848/#1021)的客户端是可安全推送的未知字段(非毒丸——毒丸只源自已知字段的 enum 值扩展);≥ 支持版本(含 desirecore #1038)的客户端据此把 provider 优雅门控为「需更新客户端」并从模型选择器排除。规约:新凭据源/新能力 provider 必须声明本字段;降低/解除要求请改为更小版本号而非删除字段(预设合并不回传字段删除)"
|
||
}
|
||
},
|
||
"additionalProperties": false,
|
||
"definitions": {
|
||
"model": {
|
||
"type": "object",
|
||
"required": ["modelName", "displayName", "serviceType", "capabilities"],
|
||
"properties": {
|
||
"modelName": {
|
||
"type": "string",
|
||
"description": "此 Provider 接入面实际接受的模型 ID,用于 API 调用并与 ModelSpec 的精确/受控别名匹配(如 gpt-5-mini、claude-sonnet-4)。",
|
||
"minLength": 1
|
||
},
|
||
"displayName": {
|
||
"type": "string",
|
||
"description": "旧客户端展示兼容副本。新 Smart 路径以 ModelSpec.displayName 为模型事实,此字段仅随接入面保留。",
|
||
"minLength": 1
|
||
},
|
||
"serviceType": {
|
||
"type": ["string", "array"],
|
||
"items": { "type": "string" },
|
||
"description": "旧客户端兼容的接入面服务声明;新 Smart 路径以 ModelSpec.spec 为能力事实。支持单个字符串或数组:chat、reasoning、fast、responses、translation、tts、asr、voice_clone、realtime_voice、simultaneous_interpret、vision、ocr、image_gen、video_gen、embedding、rerank、omni、computer_use 等"
|
||
},
|
||
"description": {
|
||
"type": "string",
|
||
"description": "模型简要描述"
|
||
},
|
||
"contextWindow": {
|
||
"type": "number",
|
||
"description": "旧客户端兼容副本;Smart 以 ModelSpec.spec.contextWindow 为模型事实,本地接入面覆盖只能收紧。",
|
||
"minimum": 0
|
||
},
|
||
"maxOutputTokens": {
|
||
"type": "number",
|
||
"description": "旧客户端兼容副本;新模型规格以 ModelSpec.spec.maxOutputTokens 为准。",
|
||
"minimum": 0
|
||
},
|
||
"capabilities": {
|
||
"type": "array",
|
||
"description": "旧客户端兼容副本;Smart 的能力标签以 ModelSpec.spec.capabilities 为准,接入面或用户覆盖只能收紧,不能扩张。",
|
||
"items": { "type": "string" }
|
||
},
|
||
"inputPrice": {
|
||
"type": "number",
|
||
"description": "输入价格(每百万 token),货币由 Provider.priceCurrency 决定",
|
||
"minimum": 0
|
||
},
|
||
"outputPrice": {
|
||
"type": "number",
|
||
"description": "输出价格(每百万 token),货币由 Provider.priceCurrency 决定",
|
||
"minimum": 0
|
||
},
|
||
"defaultTemperature": {
|
||
"type": "number",
|
||
"description": "默认温度参数(0-2)。【重要】必须是 number,禁止写为 null 或字符串。reasoning 等不支持温度的模型应完全省略此字段。null 会破坏 fix #471 之前发布的客户端(schema 严格 number),导致 readComputeConfig 死锁。",
|
||
"minimum": 0,
|
||
"maximum": 2
|
||
},
|
||
"defaultTopP": {
|
||
"type": "number",
|
||
"description": "默认 Top-P 参数(0-1)。【重要】必须是 number,禁止写为 null 或字符串。reasoning 等不支持 Top-P 的模型应完全省略此字段。null 会破坏 fix #471 之前发布的客户端,导致死锁。",
|
||
"minimum": 0,
|
||
"maximum": 1
|
||
},
|
||
"extra": {
|
||
"type": "object",
|
||
"description": "接入面特定配置:如 TTS 模型音色列表、ASR 支持格式、reasoning effort、native thinking 回放能力与工具轮 thinking 校验口径。这里是兼容扩展面:旧客户端会安全保留并忽略未知子字段。",
|
||
"properties": {
|
||
"adaptiveThinking": {
|
||
"type": "boolean",
|
||
"description": "Anthropic Messages 模型是否属于 adaptive thinking 代际(Claude Opus 4.7+ / Opus 5 / Sonnet 5 / Fable / Mythos)。true 时客户端把思考参数发成 thinking.type=adaptive + output_config.effort,而不是会被上游 400 拒绝的 thinking.type=enabled + budget_tokens;false 表示明确不是,禁止客户端按模型 ID 推断;缺省时客户端仅在官方 api.anthropic.com 端点上按模型代际表兜底。只作用于 anthropic-messages 接入面,Claude 订阅(claude-oauth)由 Claude CLI 自行改写、不受本字段影响。这是模型固有属性,权威声明写在 model-spec 的 spec.extra.adaptiveThinking;预置 Provider 条目不经规格库补全,必须在这里同样声明。"
|
||
},
|
||
"thinkingRoundTrip": {
|
||
"type": "object",
|
||
"description": "当前 Provider/model 接入面是否允许回放 Provider 原样返回的 Anthropic thinking。只授权当前未闭合工具轨迹;UI 展示 thinking、已完成旧轮次、跨模型/凭据数据永远没有回放资格。",
|
||
"required": ["enabled", "protocol", "allowEmptySignature", "scope"],
|
||
"properties": {
|
||
"enabled": {
|
||
"type": "boolean",
|
||
"description": "显式能力开关;客户端只接受 true,false 或缺失均 fail closed。"
|
||
},
|
||
"protocol": {
|
||
"type": "string",
|
||
"enum": ["anthropic-messages"],
|
||
"description": "能力对应的线上协议;不得由 Provider 名称或 URL 猜测。"
|
||
},
|
||
"allowEmptySignature": {
|
||
"type": "boolean",
|
||
"description": "是否允许回放该接入面原样返回的空 signature;只有真机证明网关确实要求时才能设为 true,不得制造空签名。"
|
||
},
|
||
"scope": {
|
||
"type": "string",
|
||
"enum": ["active-tool-turn"],
|
||
"description": "固定只作用于当前未闭合工具轨迹,禁止扩张到完成历史。"
|
||
}
|
||
},
|
||
"additionalProperties": false
|
||
},
|
||
"samplingParametersDeprecated": {
|
||
"type": "boolean",
|
||
"description": "模型是否拒绝采样参数(如 Claude Opus 4.7+ / Opus 5 / Fable / Mythos 携带 temperature、top_p 或 top_k 即 400,Sonnet 5 对非默认值 400)。true 时客户端出站删除请求体顶层的 temperature / top_p / top_k——无论取值来自模型 defaultTemperature、用户覆写、工作流默认值还是父会话继承;false 或缺省时原样发送,客户端不按模型 ID 推断。只描述模型固有属性:精确命中的 ModelSpec 会补全到各接入面的同名模型,family 兜底不继承;作用于 Agent 引擎发出的模型请求,Claude 订阅(claude-oauth)请求由 Claude CLI 自建、不受本字段影响。"
|
||
},
|
||
"forcedToolChoiceUnsupported": {
|
||
"type": "boolean",
|
||
"description": "模型是否拒绝强制工具选择(如 Claude Fable 5.1 / Mythos 5.1:tool_choice.type 为 any 或 tool 即 400)。true 时客户端把强制工具选择降级为 auto(Anthropic 的 any / tool、OpenAI 的 required / 指定函数),Anthropic 请求保留 disable_parallel_tool_use,none 与 auto 不受影响;false 或缺省时按调用方要求发送,客户端不按模型 ID 推断。只描述模型固有属性:精确命中的 ModelSpec 会补全到各接入面的同名模型,family 兜底不继承;作用于 Agent 引擎发出的模型请求,Claude 订阅(claude-oauth)请求由 Claude CLI 自建、不受本字段影响。"
|
||
},
|
||
"thinkingToolTurnValidation": {
|
||
"type": "string",
|
||
"enum": ["requires-thinking", "accepts-thinkless"],
|
||
"description": "接入面对 thinking 工具轨迹中不含 thinking 的工具轮的校验口径,仅在同一模型声明了 thinkingRoundTrip 时生效;缺省视为 requires-thinking。requires-thinking:接入面要求每个工具轮都带可回放 thinking(例如只在首轮返回 thinking、之后会拒绝整条会话的兼容网关),客户端在执行工具前把这类响应判为上游失败并重试,同一会话再次违规时关闭思考。accepts-thinkless:接入面按 Anthropic adaptive thinking 规则校验,模型自行跳过思考的工具轮本身就是合法历史,客户端原样接受,不重试也不降级。只有官方文档或真机回放证明接受此类历史的接入面才能声明 accepts-thinkless;不得由 Provider 名称或 URL 推断。旧客户端忽略该字段并保持 requires-thinking 行为。"
|
||
},
|
||
"reasoning": {
|
||
"type": "object",
|
||
"description": "当前 Provider 接入面接受哪些 reasoning effort 值——精确档位矩阵。只能写在 provider model 的 extra 里:同一模型经不同网关支持的集合可能不同,因此 model-spec 的 spec.extra 里写本键会被客户端 applySpec 主动丢弃。想表达「这个模型根本关不掉思考」的用 model-spec 的 spec.extra.thinkingOnly,那只回答 off 能否使用、不声明任何深度档位。",
|
||
"required": ["supportedEfforts"],
|
||
"properties": {
|
||
"supportedEfforts": {
|
||
"type": "array",
|
||
"minItems": 1,
|
||
"uniqueItems": true,
|
||
"items": {
|
||
"type": "string",
|
||
"enum": ["none", "minimal", "low", "medium", "high", "xhigh", "max"]
|
||
},
|
||
"description": "当前接入面实际接受的 OpenAI reasoning effort;它只能收紧精确 ModelSpec.routing.reasoning 合同,不能扩张。none 在产品层映射为 off;Ultra 是多 Agent 编排模式,禁止写入。"
|
||
},
|
||
"defaultEffort": {
|
||
"type": "string",
|
||
"enum": ["minimal", "low", "medium", "high", "xhigh", "max"],
|
||
"description": "当前接入面推荐的显式默认档位;仅当它仍属于 ModelSpec 合同时生效,且必须同时存在于 supportedEfforts。"
|
||
}
|
||
},
|
||
"allOf": [
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "minimal" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "minimal" } } } } },
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "low" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "low" } } } } },
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "medium" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "medium" } } } } },
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "high" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "high" } } } } },
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "xhigh" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "xhigh" } } } } },
|
||
{ "if": { "required": ["defaultEffort"], "properties": { "defaultEffort": { "const": "max" } } }, "then": { "properties": { "supportedEfforts": { "contains": { "const": "max" } } } } }
|
||
],
|
||
"additionalProperties": false
|
||
}
|
||
},
|
||
"additionalProperties": true
|
||
},
|
||
"apiModelId": {
|
||
"type": "string",
|
||
"description": "实际传给上游 API 的 model 参数。当 modelName 与 API 期望名称不同时使用"
|
||
},
|
||
"source": {
|
||
"type": "string",
|
||
"enum": ["preset", "synced", "user-added", "ollama-discovery"],
|
||
"description": "模型来源。config-center 数据应统一为 preset"
|
||
}
|
||
},
|
||
"additionalProperties": false
|
||
}
|
||
}
|
||
}
|