Skip to content

Latest commit

 

History

History
200 lines (152 loc) · 17.3 KB

API.md

File metadata and controls

200 lines (152 loc) · 17.3 KB

API 兼容说明

服务默认监听 http://127.0.0.1:8000,客户端不需要传入真实 OpenAI 或 Anthropic API key,但可以通过 Authorization 指定 OpenCode 上游模式。

鉴权与上游选择

  • 无 Authorization,或 Bearer public
    • 走 public Zen 免费模型。
    • /v1/models 只返回免费模型,且 ID 会去掉上游 -free 后缀(请求时会自动映射回 -free)。
  • Bearer <opencode-api-key>
    • 默认走 Zen。
    • 如果请求的是仅存在于 Go 目录中的模型,代理会自动切到 Go。
  • Bearer zen:<opencode-api-key>
    • 强制走 Zen。
  • Bearer go:<opencode-api-key>
    • 优先走 Go 订阅目录。
    • 对同时存在于 Zen 和 Go 的模型,也会按 Go 路径请求。

路由

路由 方法 说明
/v1/models GET 返回权限范围内的模型;-free 后缀会隐藏,已配置别名会替换对应上游模型 ID
/v1/chat/completions POST OpenAI Chat Completions 兼容入口
/v1/responses POST OpenAI Responses 兼容入口
/v1/messages POST Anthropic Messages 兼容入口
/v1/messages/count_tokens POST Anthropic token 计数入口:命中 anthropic 规则时直连上游 /zen/v1/messages/count_tokens,否则本地启发式估算
/v1/systemone POST TypeSafe System One 协议入口(state + typed questions → structured answers),直通上游 /zen/v1/systemone;仅 jev 系列模型可用——该模型不做文本生成,无法用 Chat / Responses / Messages 任何一条协议驱动。body 除 model 外原样透传,不走协议路由/协议转换,也不套用免费层指纹重做(该门禁只作用于 chat/completions/messages/responses 三个上游子路径)
/health GET 健康检查
/api/config GET/POST 管理面板配置接口
/api/stats GET/DELETE token 统计接口
/api/reload POST 刷新 OpenCode 会话和模型列表;并发请求共享一次刷新

GET /v1/models 的返回会随鉴权模式变化:

  • public 只显示免费 Zen 模型。
  • 默认或 zen: 模式显示 Zen 目录。
  • go: 模式显示 Go 目录,并附带 public 可用的免费模型。

上游协议路由

三个入站协议(/v1/chat/completions、/v1/responses、/v1/messages)默认经 Chat Completions 翻译后转发上游。配置 protocol_rules 后,按模型模式把请求分流到上游原生协议端点(anthropic → /zen/v1/messages、responses → /zen/v1/responses、chat_completions → /zen/v1/chat/completions),请求/响应由网关自动在入站与上游协议间转换,流式、工具调用、推理内容与 usage 统计均兼容。未命中规则时保持既有行为。规则语法、优先级与校验见 CONFIGURATION.md。

请求校验

temperature

入口 合法范围(闭区间)
/v1/messages 0..1
/v1/chat/completions 0..2
/v1/responses 0..2
  • 缺省(nil)、0、上界均合法;负数、越上界、NaN/Inf 在调用上游前被拒绝,不 clamp。
  • 拒绝时返回 HTTP 400,Content-Type: application/json,形状与入口协议一致:
    • Claude:{"type":"error","error":{"type":"invalid_request_error","message":...}}
    • Chat/Responses:{"error":{"type":"invalid_request_error","message":...,"param":"temperature"}}
  • 流式请求同样先返回普通 JSON 400,不会开始 SSE。

文件输入

  • document(Anthropic)/ input_file(Responses)是 best-effort file part,映射为 {type:"file",file:{...}}。并非所有上游模型支持 file 模态,上游拒绝时会透传明确错误。
  • 在受支持位置识别为文件输入但缺少任何可用 payload(无 file_data/file_id/file_url 或 document 无可用 source)时返回协议形状 HTTP 400 invalid_request_error,不会把 wrapper JSON 伪装成文本。

Chat Completions

准确支持

  • model
  • messages
  • stream
  • temperature(闭区间 0..2)
  • max_tokens
  • max_completion_tokens(clamped 到 [128, cap];已带该字段时不再注入 max_tokens,遵循 OpenAI 二者互斥约束,避免上游 400,issue #35)
  • top_p
  • thinking
  • reasoning_effort
  • extra_body
  • tools
  • tool_choice

流式响应会原样保留合法的 usage-only 尾块(choices: [])以及完整 usage details。

Best-effort

  • 上游 Anthropic 响应会转换 stop reason、usage、reasoning、refusal 和工具调用。
  • 不同上游模型对 thinking / reasoning_effort 的支持可能不同。
  • 走原生 Anthropic 上游时:tool_choice.allowed_tools 按声明收窄 tools 列表(空交集 → tool_choice none,绝不静默放行全量);parallel_tool_calls:false 携带为 tool_choice.disable_parallel_tool_use:true(none 跳过;无 tool_choice 且已声明工具时补 auto 载体);tool_choice 的 any 透传、custom 映射 auto。
  • 走原生 Anthropic 上游时:历史 tool_calls[].id / tool_call_id 中 Anthropic 字符集(^[A-Za-z0-9_-]+$)外的 ID(如 Kimi/Gemini 风格 functions.x:0)会确定性清洗(语义保留 + hash 前缀,超 64 截断);同一 ID 在两侧清洗到相同值保持配对,合法 ID 直通不变。孤儿 tool_result(空 tool_use_id)降级为普通 user 文本,不再照发必 400;空 ID 的 tool_calls 条目跳过。
  • 走原生 Anthropic 上游时:对话中间出现的 system/developer 原地转 <system-reminder> 包裹的 user 轮(缓存锚点留在 messages 里);其与相邻 user 轮合并时 tool_result 块保持消息首位(Anthropic 要求紧跟 tool_use 的 user 消息以 tool_result 开头,400 文本 "Did not find N tool_result block(s) at the beginning of this message")。
  • 走原生 Anthropic 上游时:tool_result.content 内嵌 block 上的 part 级 cache_control 提升到 tool_result 块本身(Anthropic 拒绝嵌在 tool_result.content 里的块级断点;首个命中提升、其余折叠)。

不支持

  • 本项目未声明支持的 Chat Completions beta 字段不会被合成或伪造。

model 通常会先经过 model_alias 解析;显式使用 go: 或 zen: 且原模型存在于对应目录时,同名的 模型 -> 模型-free 兼容别名会让位于对应目录中的原模型。reasoning_effort 会按 reasoning_effort_map 转换。

Responses API

准确支持

  • 字符串 input;含 input_text / input_image 的 message item;函数及内置工具的 call/output item
  • instructions、messages(使用 Chat content 形状,非 Responses input item 形状)、previous_response_id
  • 显式零值的 temperature(闭区间 0..2)、top_p、frequency_penalty、presence_penalty
  • max_output_tokens、stop、user、parallel_tool_calls、stream_options、store
  • 函数工具、项目已有的内置工具、tool_choice、reasoning、metadata
  • Chat 经 protocol_rules 走原生 Responses 上游时:parallel_tool_calls / service_tier 透传上游;response_format(json_schema 展平 / json_object 透传 type)映射为 text.format;custom_tool_call(custom/freeform 工具)的 input 增量与 done 按同一 tool_calls index 累积;incomplete 按 reason 细分 finish_reason(max_output_tokens→length,content_filter→content_filter);上游 service_tier 回写 chat 顶层;response.done(Realtime/WS 别名)与 completed 同等终结流
  • Chat 经 protocol_rules 走原生 Anthropic 上游时:thinking 生效即剥离 temperature/top_p;adaptive-only 机型(Opus 4.7+ / Sonnet 5+ / Fable 系)无条件剥离(这些机型拒绝 sampling 参数,400 与 thinking 决策无关);其余机型 temperature/top_p 同时携带时收成单参(优先 temperature,对齐 Bifrost)
  • Anthropic-style tool_result(call_id,缺省时用 tool_use_id;content 支持 string、字符串数组、{type:"text"|"input_text"|"output_text",text} blocks;is_error:true 由网关内部标记携带——内容文本不再加 Error: 前缀,走 Anthropic 上游时转回 tool_result.is_error:true)
  • 正常终态 response.completed;长度截断终态 response.incomplete,reason 为 max_output_tokens

Best-effort

  • Responses 会通过 Chat Completions 上游实现;内置工具被编码为函数工具后再还原。
  • /v1/responses 入站只走翻译路径(Chat / Anthropic 上游),不透传到上游原生 /responses 端点。Chat 与 Claude Messages 入站在 Chat 翻译失败后仍会回退探测上游原生 /responses(请求与响应在网关内自动转换,路由见 native_responses_models 配置项)。
  • 仅在上游实际返回 reasoning 时生成 reasoning output item。
  • input 中的 top-level item 或 message content 可使用 input_file;支持 flat 字段 file_data、file_id、file_url、filename 以及 nested input_file object,并映射为 {type:"file",file:{...}}。模型不支持 file 模态时上游可能拒绝。

不支持

  • include 及未在上面列出的可选 Responses 字段;这些字段不会用占位值伪装成已支持。
  • input 中受支持位置的 input_file 若缺少任何可用 payload,会返回 HTTP 400,不会序列化为文本。

示例:

curl http://127.0.0.1:8000/v1/responses \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "input": "Write one short sentence.",
    "stream": false
  }'

Anthropic Messages

鉴权

  • Authorization: Bearer <key> 与 x-api-key: <key> 同权;Bearer 优先。
  • 转发上游原生 /zen/v1/messages 时,网关同时发送 Authorization: Bearer <key> 与 Anthropic 原生 x-api-key: <key>;上游 /messages 以 x-api-key 为准。
  • 有效 opencode key:sk- 前缀且长度 > 15;go: / zen: 前缀路由同样适用于两种头。
  • Anthropic 真 key(sk-ant-)不会转发上游,回落 public。
  • 占位短 key(如 Claude Code 默认 sk-local)因长度不足走 public。

准确支持

  • system(顶层)与消息内 role=system 合并为上游唯一首条 system(\n\n 拼接,顶层在前);system 为 block 数组时启发式递归计入 token
  • stop_sequences、temperature(闭区间 0..1)/ top_p / top_k(包括显式零值);开启 thinking 时这三个参数按上游 Anthropic 语义剥离(避免 400)
  • max_tokens:Chat/Responses 直通与翻译同样收敛 [128, cap](cap 来自 max_tokens_cap / max_tokens_cap_per_model);count_tokens 命中 anthropic 规则时只降不补
  • metadata.user_id:若为 JSON 串则只转发 session_id(避免 device_id 外泄);否则原样转发
  • 文本、base64/URL image、tool_use、tool_result(包括 is_error);合法的 tool result 在普通用户内容之前的顺序会被保留
  • is_error:true 的 tool_result 转 Chat 上游时内容文本不加 Error: 前缀(不变形),错误标记由网关内部携带;转 Anthropic 上游时保留原生 is_error:true
  • tool_use.id / tool_result.tool_use_id 中 Anthropic 字符集(^[A-Za-z0-9_-]+$)外的 ID 确定性清洗(同侧配对保持);空 tool_use_id 的 tool_result 降级为普通 user 文本(转 Chat tool 消息必缺 tool_call_id),空 ID 的 tool_use 跳过
  • tool_result 中的 image 转为紧随其后的 role=user + image_url,tool 文本保留字符串并标注 [image attached];tool_result 中的 document 同样转为紧随其后的 user file part 并标注 [document attached];orphan tool_use(无 matching tool_result)在翻译路径由 Worker A/B 的配对归一化处理
  • tool_choice 的 auto、any、tool、none;disable_parallel_tool_use:true 映射为上游 parallel_tool_calls=false
  • output_config.effort → 上游 reasoning_effort;thinking 翻译按机型收口(对齐 Bifrost):adaptive-only 机型(Opus 4.7+ / Sonnet 5+ / Fable 系)上 thinking.type=enabled(+budget) 重写为 adaptive + effort(budget 折算 effort,budget_tokens 不随 adaptive 发送),其余机型保持 enabled 归一;走原生 Anthropic 上游时按机型发 adaptive(+display)/between_tools/enabled+budget,不再恒发 enabled+budget
  • JSON Schema 约束字段(包括 additionalParameters、format)
  • stop reason、usage 以及流式 content block 配对
  • /v1/messages/count_tokens:protocol_rules 命中 anthropic 上游时直连 /zen/v1/messages/count_tokens 透传取精确计数;未命中、仅命中 chat/responses 或上游失败时回落本地启发式(chat 上游对 thinking 的采样参数互斥剥参同样生效)

工具名兼容(避免上游 name 400)

  • 上游 Anthropic Messages 强制 tools[].name / tool_choice.name / 历史 tool_use.name / mcp_servers[].name 满足 ^[a-zA-Z0-9_-]{1,64}$。客户端合法发出的超长名(如 MCP 风格 mcp__server__very_long_tool)或含非法字符名(点号、空格、Unicode)会在发往 anthropic 上游前确定性改写:合法名直通不变;含非法字符的短名按 rune 折叠为 _(可读前缀保留);超长名取折叠前缀 + 原名 sha256 后 8 字节十六进制(恒 64 字符)。
  • 映射为纯函数(sha256 派生):同一原始名跨请求、跨网关重启、跨轮历史回放、跨 key 轮换重试恒得同一短名;tool_choice 与历史 tool_use 与 tools[] 三处必然一致。
  • 折叠/缩短结果与已声明合法名碰撞时确定性消歧(哈希形),双向映射保证还原。
  • 响应侧(流式 content_block、非流式 content、跨协议 Chat/Responses 转换)按相反映射还原,客户端看到的名字与所发逐字节一致;mcp_servers[].name 被缩短时,上游拼出的 mcp__<缩短 server>__<tool> 复合名同样还原。
  • 全部名字已合法时不改写任何字节(快路径);Claude/Chat 入站走 Responses 上游的桥接路径(chat_to_responses_upstream.go / claude_responses.go)的超长名缩短复用同一机制并额外获得字符集折叠。

Best-effort / 显式丢弃(可观测)

  • document(source.type=base64,默认 application/pdf;source.type=url)映射为 Chat content part {type:"file",file:{...}},可保留 block/title 作为 filename。模型不支持 file 模态时上游可能拒绝;document 缺少可用 payload 时返回 HTTP 400。
  • thinking 会在没有 signature 时继续输出,以提高客户端兼容性。代理不会伪造 signature。
  • thinking 响应中的 signature / redacted data 落 typed 推理槽位 reasoning_details[](对齐 Bifrost ReasoningDetails{index,type,text,signature,data}:thinking → reasoning.text,redacted → reasoning.encrypted),流式路径同样经 reasoning_details delta 流出(keep_reasoning=false 时与 reasoning_content 同一抑制契约)。Chat 请求历史中携带 reasoning_details 的 assistant 消息走原生 Anthropic 上游时,在 assistant 消息头部重放 thinking+signature / redacted_thinking 块(thinking-head 要求);历史请求中的 thinking 块数仍计入 request_plan(history_signature_count),不记录签名内容。
  • redacted_thinking.data 是不可解释的加密数据:经 /v1/messages 历史不再 JSON-stringify 进文本(密文泄漏),走 typed 槽位 / 原样重放;claude→Responses 路径的请求历史中 thinking/redacted 回放为 reasoning item(文本落 summary、signature/密文落 encrypted_content,对齐 Bifrost 的 summary 与 encrypted_content 双存),文本与密文都缺时才丢弃。native Anthropic 响应中明确存在的 signature / redacted data 同时由私有 roundtrip 字段(_opencode2api_anthropic_content)保留,仅用于 Claude Messages 往返,Chat/Responses 公共 payload 不会泄漏这些私有字段。
  • 无 Chat Completions 等价物的字段不进上游 body,但会绑定并记入 request_plan / body summary:context_management、cache_control、anthropic-beta、带 type 且无 input_schema 的 server tools(如 web_search_*)。
  • cache_control breakpoints 现在被保留:Claude 侧的 system[].cache_control、messages[].content[].cache_control、tools[].cache_control 会在转换到 Chat 上游时按文本/工具名重放到对应消息/工具上(config.cache_control_breakpoints 默认开启,rejectsCacheControl 列表内的 GLM/Zhipu 仍跳过),同时自动附加顶层 cache_control breakpoint;cached_tokens usage 由上游返回时映射到 prompt_tokens_details.cached_tokens 并累计到本地 stats。请求中的 cache_control 也仍然计入 cache_control_blocks 供诊断。

不支持(不实现语义)

  • prompt caching、context management、server tools 的真实能力
  • 转发 anthropic-beta 到上游

示例:

curl http://127.0.0.1:8000/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk-your-opencode-key" \
  -d '{
    "model": "gpt-4o-mini",
    "max_tokens": 256,
    "messages": [{"role": "user", "content": "hello"}]
  }'

流式响应

stream: true 时服务会使用 SSE 返回,并在内部清理空 delta、空 finish reason 和不需要的 reasoning 字段。Responses 和 Anthropic 流式接口会把上游 Chat Completions chunk 转换成对应事件。