将本地 OpenCode 运行时转换为 OpenAI 兼容 API 网关。在任何 OpenAI 客户端中使用免费模型 (GPT, Nemotron, MiniMax)。
| 特性 | 说明 |
|---|---|
| 🟢 OpenAI 兼容 | /v1/models, /v1/chat/completions, /v1/responses |
| 📡 流式输出 | Chat Completions 与 Responses API 的完整 SSE 流式支持 |
| 🧠 推理控制 | 支持 reasoning_effort 和 reasoning: { "effort": "high" } |
| 🔧 外部工具调用(完整兼容) | 客户端按标准 OpenAI tools 格式传入,代理自动桥接,返回标准 tool_calls,无需任何兼容代码 |
| 🛡️ 工具安全 | 默认禁用 OpenCode 内置工具,外部工具命名空间隔离防冲突 |
| 🐳 Docker 部署 | 一键部署,自动启动 OpenCode 后端 |
| 🌐 内置 web_fetch 透传 | 当请求未传入 tools 且显式开启特性时,仅允许 OpenCode 内置 web_fetch 参与该次请求 |
# 1. 克隆并配置 git clone https://github.com/TiaraBasori/opencode2api.git cd opencode2api cp .env.example .env # 2. 编辑 .env 设置你的配置 # 必填: API_KEY, OPENCODE_SERVER_PASSWORD # 3. 启动 docker compose up -d # 4. 测试 curl http://127.0.0.1:10000/health
# 1. 安装 OpenCode CLI npm install -g opencode-ai # Linux/macOS: curl -fsSL https://opencode.ai/install | bash # 2. 克隆并运行 git clone https://github.com/TiaraBasori/opencode2api.git cd opencode2api npm install cp config.json.example config.json npm start
OpenCode2API 对外部工具调用做了完整的兼容处理,客户端只需按标准 OpenAI API 格式传入 tools,即可获得标准的 tool_calls 返回,无需任何适配代码。
客户端 (LangChain / OpenAI SDK / curl)
│ 传入标准 OpenAI tools 格式
▼
OpenCode2API 代理层
│ 1. 注册外部工具,加命名空间前缀 (external__)
│ 2. 注入提示词引导模型输出 <function_calls> 格式
│ 3. 模型输出后,解析并转换为标准 tool_calls
│ 4. 若模型未正确输出工具调用,智能重试一次
▼
客户端收到标准 OpenAI tool_calls 响应
| 保障项 | 说明 |
|---|---|
| 标准格式 | 客户端传入标准 OpenAI tools,收到标准 tool_calls,完全兼容 OpenAI SDK |
| 解析容错 | 支持 <function_calls> 标签格式、裸 JSON 格式、markdown 代码块包裹、多行格式化、嵌套 arguments |
| 智能重试 | auto 模式下检测到模型"想调用但格式不对"时自动重试;required 模式下始终强制重试 |
| 命名空间隔离 | 外部工具自动加 external__ 前缀,与 OpenCode 内置工具隔离,同名不冲突 |
| 推理内容过滤 | 模型的 <think/> 推理内容不会泄露到客户端响应中 |
from langchain_openai import ChatOpenAI from langchain.agents import create_agent def get_weather(city: str) -> str: """获取指定城市的天气""" # ... 实际实现 ... return f"{city}当前天气:晴,气温26°C" llm = ChatOpenAI( base_url="http://127.0.0.1:10000/v1", model="opencode/deepseek-v4-flash-free", api_key="your-api-key", temperature=0.2, ) agent = create_agent( model=llm, tools=[get_weather], system_prompt="你是一个天气助手,请用中文回答。", ) result = agent.invoke( {"messages": [{"role": "user", "content": "深圳今天天气怎么样?"}]} ) print(result)
from openai import OpenAI client = OpenAI( base_url="http://127.0.0.1:10000/v1", api_key="your-api-key", ) response = client.chat.completions.create( model="opencode/deepseek-v4-flash-free", messages=[{"role": "user", "content": "深圳今天天气怎么样?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } }] ) if response.choices[0].message.tool_calls: for tc in response.choices[0].message.tool_calls: print(f"工具: {tc.function.name}, 参数: {tc.function.arguments}")
curl -X POST http://127.0.0.1:10000/v1/chat/completions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "opencode/big-pickle", "messages": [{"role": "user", "content": "深圳今天天气怎么样?"}], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } } ] }'
| 值 | 行为 |
|---|---|
"auto" (默认) |
模型自行决定是否调用工具;若模型输出了工具调用痕迹但格式不对,代理会智能重试一次 |
"required" |
强制模型必须调用工具;若首次未成功,代理会强制重试 |
"none" |
禁用工具调用 |
{"type": "function", "function": {"name": "xxx"}} |
强制调用指定工具 |
curl -X POST http://127.0.0.1:10000/v1/chat/completions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "opencode/big-pickle", "messages": [{"role": "user", "content": "你好!"}], "stream": false }'
curl -N -X POST http://127.0.0.1:10000/v1/responses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt5-nano", "input": "用一句话打招呼", "reasoning": {"effort": "high"}, "stream": true }'
curl -N -X POST http://127.0.0.1:10000/v1/responses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "opencode/big-pickle", "input": "查询东京天气", "stream": true, "tools": [ { "type": "function", "function": { "name": "weather_lookup", "description": "Look up weather by city", "parameters": { "type": "object", "properties": { "city": {"type": "string"}, "unit": {"type": "string"} }, "required": ["city"] } } } ] }'
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 🐳 Docker | 完整栈,自动启动 OpenCode 后端 | 生产环境,最简配置 |
| 💻 独立 Node | 手动管理后端 | 开发、自定义集成 |
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PORT / OPENCODE_PROXY_PORT |
10000 |
代理服务端口 |
OPENCODE_SERVER_PORT |
10001 |
OpenCode 后端服务端口 |
API_KEY |
- | Bearer Token 认证密钥 |
BIND_HOST |
0.0.0.0 |
绑定地址 |
DISABLE_TOOLS |
true |
禁用 OpenCode 内置工具 |
OPENCODE_EXTERNAL_TOOLS_MODE |
proxy-bridge |
外部工具桥接模式;当前仅支持 proxy-bridge |
OPENCODE_EXTERNAL_TOOLS_CONFLICT_POLICY |
namespace |
外部工具与 OpenCode 内置工具的冲突隔离策略;当前仅支持 namespace |
OPENCODE_INTERNAL_WEB_FETCH_ENABLED |
false |
兼容旧开关;当未显式配置 allowlist 时,启用后默认放行 web_fetch |
OPENCODE_INTERNAL_ALLOWED_TOOLS |
(none) |
当请求未传入 tools 时,允许使用的 OpenCode 内置工具列表,逗号分隔 |
OPENCODE_INTERNAL_TOOL_METRICS_ENABLED |
true |
输出 internal allowlist 模式的调试/指标日志 |
OPENCODE_TOOL_DISCOVERY_FIXTURE |
(none) |
集成测试/本地调试用的后端工具 ID 固定列表,逗号分隔 |
OPENCODE_HEALTH_DETAILS_ENABLED |
true |
控制 /health/details 是否暴露 |
OPENCODE_HEALTH_DETAILS_REQUIRE_AUTH |
true |
控制 /health/details 是否要求 Bearer 认证 |
OPENCODE_METRICS_ENABLED |
false |
控制 /metrics 是否暴露 |
OPENCODE_METRICS_REQUIRE_AUTH |
true |
控制 /metrics 是否要求 Bearer 认证 |
USE_ISOLATED_HOME |
false |
使用隔离的 OpenCode 配置目录 |
OPENCODE_PROXY_PROMPT_MODE |
standard |
提示词处理模式 |
OPENCODE_PROXY_OMIT_SYSTEM_PROMPT |
false |
忽略传入的 system prompt |
OPENCODE_PROXY_AUTO_CLEANUP_CONVERSATIONS |
false |
自动清理会话存储 |
OPENCODE_PROXY_CLEANUP_INTERVAL_MS |
43200000 |
清理间隔 (毫秒) |
OPENCODE_PROXY_CLEANUP_MAX_AGE_MS |
86400000 |
最大存储时间 (毫秒) |
OPENCODE_PROXY_REQUEST_TIMEOUT_MS |
180000 |
请求超时时间 (毫秒) |
OPENCODE_SERVER_URL |
http://127.0.0.1:10001 |
OpenCode 后端地址 |
OPENCODE_SERVER_PASSWORD |
- | OpenCode 后端密码 |
OPENCODE_PATH |
opencode |
OpenCode 可执行文件路径 |
OPENCODE_ZEN_API_KEY |
- | Zen API Key 透传 |
DEBUG / OPENCODE_PROXY_DEBUG |
false |
调试日志 |
📄 完整配置参考: 配置详解
API_KEY=your-secret-key OPENCODE_SERVER_PASSWORD=your-password DISABLE_TOOLS=true OPENCODE_EXTERNAL_TOOLS_MODE=proxy-bridge OPENCODE_EXTERNAL_TOOLS_CONFLICT_POLICY=namespace OPENCODE_INTERNAL_ALLOWED_TOOLS=web_fetch OPENCODE_INTERNAL_TOOL_METRICS_ENABLED=true OPENCODE_TOOL_DISCOVERY_FIXTURE= OPENCODE_HEALTH_DETAILS_ENABLED=true OPENCODE_HEALTH_DETAILS_REQUIRE_AUTH=true OPENCODE_METRICS_ENABLED=false OPENCODE_METRICS_REQUIRE_AUTH=true OPENCODE_PROXY_PROMPT_MODE=plugin-inject OPENCODE_PROXY_OMIT_SYSTEM_PROMPT=true OPENCODE_PROXY_AUTO_CLEANUP_CONVERSATIONS=true
- 外部客户端传入的
tools按 OpenAI 标准格式传入,代理自动桥接处理,客户端无需任何适配代码。 - 代理会把外部工具虚拟化后交给模型使用,并把模型输出转换为标准 OpenAI
tool_calls格式返回。 - 代理内置多层解析容错:支持
<function_calls>标签、裸 JSON、markdown 代码块、多行格式化、嵌套 arguments。 - 当模型未正确输出工具调用时,代理会智能判断是否需要重试(auto 模式下检测工具调用痕迹,required 模式下始终重试)。
- 同名冲突通过命名空间隔离处理,例如客户端的
web_fetch不会误触发 OpenCode 内置工具。 - 内部命名空间名(如
external__web_fetch)是代理内部实现细节,不属于公开 API。 - 模型的推理内容(
<think/>块)不会泄露到客户端响应中。
- 当请求 未传入
tools时,代理会进入 internal allowlist 模式,并只允许OPENCODE_INTERNAL_ALLOWED_TOOLS中声明的 OpenCode 内置工具。 OPENCODE_INTERNAL_WEB_FETCH_ENABLED=true仅作为兼容旧配置的快捷方式:当未显式配置OPENCODE_INTERNAL_ALLOWED_TOOLS时,会默认把 allowlist 视为web_fetch。- 代理会读取后端工具列表,并通过精确匹配或
.<tool>//<tool>后缀匹配解析最终可用工具。 - 如果配置的 allowlist 在后端工具列表中一个也匹配不到,代理会自动回退到"全部内置工具禁用"的安全模式。
OPENCODE_INTERNAL_TOOL_METRICS_ENABLED=true时,代理会输出 internal allowlist 模式的调试/指标日志,包括模式选择、后端工具发现、allowlist 命中结果和降级原因,但不会记录工具返回内容。OPENCODE_TOOL_DISCOVERY_FIXTURE可用于集成测试或本地调试,绕过真实client.tool.ids()返回固定工具 ID 列表。- 一旦客户端显式传入
tools,请求立即回到现有外部工具桥接逻辑,OpenCode 内置工具继续保持禁用。
/health始终保持为轻量健康检查接口。/health/details返回结构化 JSON 诊断数据,可通过OPENCODE_HEALTH_DETAILS_ENABLED控制是否暴露,并通过OPENCODE_HEALTH_DETAILS_REQUIRE_AUTH控制是否要求 Bearer 认证。/metrics返回 Prometheus 文本格式指标,可通过OPENCODE_METRICS_ENABLED控制是否暴露,并通过OPENCODE_METRICS_REQUIRE_AUTH控制是否要求 Bearer 认证。/metrics目前暴露 internal tool mode、tool discovery failures、fallback count 和 tool-id cache 数量等指标。
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/health |
健康检查 |
GET |
/health/details |
结构化诊断接口(可配置开关/鉴权) |
GET |
/metrics |
Prometheus 指标接口(可配置开关/鉴权) |
GET |
/v1/models |
获取可用模型列表 |
POST |
/v1/chat/completions |
Chat Completions API |
POST |
/v1/responses |
Responses API |
- 直接使用:
opencode/big-pickle - 带别名:
gpt5-nano(自动解析为gpt-5-nano) - 带前缀:
opencode/gpt5-nano
📖 详见 API 参考文档
USE_ISOLATED_HOME=false # 让 OpenCode 复用本地登录态- 查看可用模型:
curl http://127.0.0.1:10000/v1/models - 确认模型 ID 完全匹配
- 确认
DISABLE_TOOLS=true和OPENCODE_EXTERNAL_TOOLS_MODE=proxy-bridge已配置 - 确认传入的
tools符合 OpenAI 标准格式 - 开启调试:
OPENCODE_PROXY_DEBUG=true,查看代理日志中的工具解析情况 - 部分免费模型对工具调用的支持可能不稳定,可尝试
tool_choice: "required"强制调用
- 使用
stream: true的 Responses API - 发送
reasoning.effort或reasoning_effort
📖 完整指南: 故障排查
# 运行测试 npm test -- --runInBand # Docker 开发 docker compose up -d --build
MIT · 详见 LICENSE
感谢以下开源项目: