服务默认监听 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。
| 入口 | 合法范围(闭区间) |
|---|---|
/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"}}
- Claude:
- 流式请求同样先返回普通 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 400invalid_request_error,不会把 wrapper JSON 伪装成文本。
modelmessagesstreamtemperature(闭区间0..2)max_tokensmax_completion_tokens(clamped 到[128, cap];已带该字段时不再注入max_tokens,遵循 OpenAI 二者互斥约束,避免上游 400,issue #35)top_pthinkingreasoning_effortextra_bodytoolstool_choice
流式响应会原样保留合法的 usage-only 尾块(choices: [])以及完整 usage details。
- 上游 Anthropic 响应会转换 stop reason、usage、reasoning、refusal 和工具调用。
- 不同上游模型对
thinking/reasoning_effort的支持可能不同。 - 走原生 Anthropic 上游时:
tool_choice.allowed_tools按声明收窄tools列表(空交集 →tool_choicenone,绝不静默放行全量);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 Ntool_resultblock(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 转换。
- 字符串
input;含input_text/input_image的 message item;函数及内置工具的 call/output item instructions、messages(使用 Chat content 形状,非 Responsesinputitem 形状)、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_callsindex 累积;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
- 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以及 nestedinput_fileobject,并映射为{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
}'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 数组时启发式递归计入 tokenstop_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:truetool_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];orphantool_use(无 matching tool_result)在翻译路径由 Worker A/B 的配对归一化处理tool_choice的auto、any、tool、none;disable_parallel_tool_use:true映射为上游parallel_tool_calls=falseoutput_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 的采样参数互斥剥参同样生效)
- 上游 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)的超长名缩短复用同一机制并额外获得字符集折叠。
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[](对齐 BifrostReasoningDetails{index,type,text,signature,data}:thinking →reasoning.text,redacted →reasoning.encrypted),流式路径同样经reasoning_detailsdelta 流出(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_controlbreakpoints 现在被保留:Claude 侧的system[].cache_control、messages[].content[].cache_control、tools[].cache_control会在转换到 Chat 上游时按文本/工具名重放到对应消息/工具上(config.cache_control_breakpoints默认开启,rejectsCacheControl列表内的 GLM/Zhipu 仍跳过),同时自动附加顶层cache_controlbreakpoint;cached_tokensusage 由上游返回时映射到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 转换成对应事件。