m1saka 2517b4f730 Add OpenAI Responses API (/v1/responses) endpoint
Translate Responses API requests (input→messages, instructions→system,
max_output_tokens→max_tokens, text.format→response_format, flat tools→nested
{function:{…}}) to upstream chat/completions, then convert responses back to
Responses format (streaming SSE event lifecycle + non-streaming JSON).

Verified against OpenAI migration guide and Python SDK Response model:
- Echo back required fields parallel_tool_calls/tool_choice/tools
- Include content:[] in reasoning items, logprobs:[] in output_text parts
- Support function_call/function_call_output multi-turn input items
- Map usage fields prompt_tokens→input_tokens, completion_tokens→output_tokens
2026-08-20 10:02:11 +08:00

Zhanlu Proxy

一个本地 Go 代理服务,用于读取湛卢(v1.4.2)插件凭据,按插件认证规则换取模型 API Key,并暴露 OpenAI 兼容接口。

当前实现包含:

  • 登录页支持插件默认的移动云手机号验证码登录:验证码校验后按插件流程调用 /api/acepilot/zhanlu/v1/login 获取用户资料,再通过 SM2 签名调用 /user/api/v2/external/key/get-or-create 换取模型 API Key,凭据自动保存到本地 JSON。
  • OpenAI 兼容接口:/v1/models(优先从 /gateway/v1/model/info 拉取模型列表)、/v1/chat/completions(携带 Authorization: Bearer <apiKey> 请求 {modelBaseUrl}/chat/completions)、/v1/responsesResponses API,自动转换请求/响应格式,兼容新版 SDK 与 AI 编码工具)。
  • 湛卢登录签名逻辑:RSA authorization、SHA-256 query hash、HMAC-SHA1 Signature,用于 v1.4.2 的 v1/login 认证。
  • 模型 API Key 换取签名逻辑:SM3 摘要 + SM2 签名(X-Auth-Signature/X-Auth-Timestamp/X-Auth-Nonce)。
  • 上游 SSE 直接透传为 OpenAI SSE;非流式请求在本地聚合为 OpenAI Chat Completion JSON。
  • OpenAI 函数/工具调用:支持 toolstool_choice、流式 delta.tool_calls、非流式 message.tool_calls 以及 role: tool 结果续传。
  • Token 消耗统计:流式与非流式请求均解析上游 usage,按模型/按日/最近明细写入本地 SQLite(zhanlu.db),凭据也一并持久化在同一个库中。管理页提供 GET /admin/stats 可视化与 GET /api/stats JSON 接口。
  • 可用模型:管理后台新增「可用模型」tab,实时拉取上游 /gateway/v1/model/info 返回的模型列表,并提供单模型可用性及首字延时(TTFT)探测(GET /api/modelsPOST /api/models/test)。

运行

go run ./cmd/zhanlu-proxy

默认监听:

http://127.0.0.1:8080

打开首页会自动跳转到管理登录页:

http://127.0.0.1:8080/

systemd 服务示例

假设二进制文件放在:

/opt/zhanlu-proxy/zhanlu-proxy

数据库放在:

/opt/zhanlu-proxy/zhanlu.db

创建环境变量文件 /etc/zhanlu-proxy/zhanlu-proxy.env

ZHANLU_LISTEN_ADDR=:8080
ZHANLU_DB_FILE=/opt/zhanlu-proxy/zhanlu.db
ZHANLU_MOBILE_LOGIN_BASE_URL=https://ecloud.10086.cn
ZHANLU_MOBILE_MODEL_BASE_URL=https://ecloud.10086.cn/api/query/aigateway
ZHANLU_UPSTREAM_TIMEOUT=300s
ZHANLU_LOGIN_PASSWORD=change-this-login-password
OPENAI_COMPAT_API_KEY=change-this-local-secret

创建 systemd service 文件 /etc/systemd/system/zhanlu-proxy.service

[Unit]
Description=Zhanlu OpenAI-compatible proxy
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/opt/zhanlu-proxy
EnvironmentFile=/etc/zhanlu-proxy/zhanlu-proxy.env
ExecStart=/opt/zhanlu-proxy/zhanlu-proxy
Restart=always
RestartSec=3
User=zhanlu-proxy
Group=zhanlu-proxy

[Install]
WantedBy=multi-user.target

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now zhanlu-proxy
sudo systemctl status zhanlu-proxy

查看日志:

journalctl -u zhanlu-proxy -f

登录与凭据

移动云手机号验证码登录

打开 /login 后输入手机号并点击“获取验证码”。实现按插件默认登录分支工作:

如果设置了 ZHANLU_LOGIN_PASSWORD/login 只负责管理密码登录。密码正确后服务会设置 HttpOnly 会话 Cookie,并跳转到 /admin/login/admin/login 才是手机号验证码登录湛卢的页面,之后才能查看凭据状态、获取短信验证码、保存凭据或使用备用 SSO 管理接口。

  • 生成 16 位一次性 secret
  • 使用插件内置 RSA 公钥加密手机号和 secret
  • 调用公网接口 /api/query/acepilot-h5/manager/code/getAuthCode 发送验证码。
  • 输入验证码后调用 /api/query/acepilot-h5/manager/code/checkCode
  • 使用本次 secret AES 解密响应中的 aksklicense,得到 AccessKeySecretKeyToken
  • 按插件 v1.4.2 流程调用 /api/acepilot/zhanlu/v1/loginRSA+HmacSHA1 签名 URL + plugin_type=zhanlu_ide 请求头)获取用户资料(email/组织/团队)。
  • 用 SM2 私钥签名调用 {mobileModelBaseUrl}/user/api/v2/external/key/get-or-create 换取模型 apiKey
  • 凭据(含 apiKeymodelBaseUrl、email 等)会写入本地 SQLite 数据库(zhanlu.db),后续 OpenAI 兼容接口自动使用。

默认数据库保存在当前执行目录:

zhanlu.db

可以通过环境变量覆盖:

$env:ZHANLU_DB_FILE="E:\path\to\zhanlu.db"

凭据仅持久化在数据库中,不再使用 JSON 文件。

手机号验证码登录使用 ZHANLU_MOBILE_LOGIN_BASE_URL,默认公网地址来自插件配置(兼容旧环境变量 ZHANLU_SERVER_BASE_URL):

$env:ZHANLU_MOBILE_LOGIN_BASE_URL="https://ecloud.10086.cn"
$env:ZHANLU_MOBILE_MODEL_BASE_URL="https://ecloud.10086.cn/api/query/aigateway"

灵犀(内网)SSO 的 /auth/start/auth/callback 仍保留为备用接口,但不是 /login 的默认主流程。

OpenAI 兼容接口

健康检查

curl http://127.0.0.1:8080/healthz

模型列表

curl http://127.0.0.1:8080/v1/models

Chat Completions

非流式:

curl http://127.0.0.1:8080/v1/chat/completions `
  -H "Content-Type: application/json" `
  -d '{"model":"zhanlu/auto","messages":[{"role":"user","content":"hello"}],"stream":false}'

流式:

curl -N http://127.0.0.1:8080/v1/chat/completions `
  -H "Content-Type: application/json" `
  -d '{"model":"zhanlu/auto","messages":[{"role":"user","content":"hello"}],"stream":true}'

工具调用

请求中的 toolstool_choice 会传给湛卢模型。流式响应返回增量 delta.tool_calls;非流式响应会把分片聚合为完整的 message.tool_calls,并保留 finish_reason: "tool_calls"。执行工具后,将 assistant 的 tool_callsrole: "tool" 结果放回 messages 再发起请求即可得到最终回答。

Responses API

代理还兼容 OpenAI Responses APIPOST /v1/responses),支持使用该 API 的客户端(如新版 OpenAI SDK、Cursor 等 AI 编码工具)直接对接。代理会将 Responses API 请求格式转换为上游 chat/completions 格式,再将响应转回 Responses 格式。

请求中的 input(字符串或消息数组)、instructions(系统提示词)、max_output_tokenstemperaturetop_ptoolstool_choiceresponse_format 等参数均会翻译并透传给上游。tools 格式自动从 Responses 扁平结构转换为 Chat Completions 嵌套 {function:{…}} 结构。多轮对话中的 function_callfunction_call_output 输入项也会自动转换为对应的 assistant tool_callsrole: tool 消息。

非流式:

curl http://127.0.0.1:8080/v1/responses `
  -H "Content-Type: application/json" `
  -d '{"model":"zhanlu/auto","input":"hello","stream":false}'

流式:

curl -N http://127.0.0.1:8080/v1/responses `
  -H "Content-Type: application/json" `
  -d '{"model":"zhanlu/auto","input":"hello","stream":true}'

流式响应返回标准 Responses API SSE 事件序列(response.createdresponse.output_item.addedresponse.output_text.deltaresponse.output_text.doneresponse.output_item.doneresponse.completed),思考模型额外包含 response.reasoning_summary_text.delta 事件,工具调用包含 response.function_call_arguments.delta 事件。Token 统计同样记录在 SQLite 中。

如果设置了本地 OpenAI 兼容 API Key,需要带 Authorization

$env:OPENAI_COMPAT_API_KEY="local-secret"
curl http://127.0.0.1:8080/v1/models `
  -H "Authorization: Bearer local-secret"

配置项

环境变量 默认值 说明
ZHANLU_LISTEN_ADDR :8080 本地监听地址
ZHANLU_MOBILE_LOGIN_BASE_URL https://ecloud.10086.cn 移动云公网登录 Base URL(兼容旧变量 ZHANLU_SERVER_BASE_URL
ZHANLU_MOBILE_MODEL_BASE_URL https://ecloud.10086.cn/api/query/aigateway 移动云公网模型网关 Base URL
ZHANLU_UPSTREAM_PATH /chat/completions 模型网关聊天接口路径
ZHANLU_DB_FILE zhanlu.db 本地 SQLite 数据库路径,凭据与 token 统计均存于此,默认当前执行目录
ZHANLU_STATS_DISABLED false 设为 true 关闭 token 用量记录(仅停止写入统计,凭据存储不受影响)
ZHANLU_ACCESS_KEY 直接从环境变量提供 AccessKey
ZHANLU_SECRET_KEY 直接从环境变量提供 SecretKey
ZHANLU_TOKEN 直接从环境变量提供 Token
ZHANLU_API_KEY 直接从环境变量提供已换取的模型 API Key
ZHANLU_SSO_BASE_URL http://4c.hq.cmcc 灵犀内网 SSO 备用页面 Base URL,非默认手机号登录流程
ZHANLU_SSO_EXCHANGE_URL http://rdcloud.4c.hq.cmcc/cmdevops-aiplus-agent-gateway/api/acepilot/zhanlu/authToken 灵犀 SSO 换取用户资料的备用接口
ZHANLU_TOKEN_DECRYPT_KEY 3jw7woww2rvhla6k 解密 SSO 返回用户资料字段的 AES key(插件内置默认值)
ZHANLU_PUBLIC_KEY_PEM 插件内置签名公钥 签名 URL 中 authorization 使用的 RSA 公钥,通常不需要设置
ZHANLU_PHONE_PUBLIC_KEY_PEM 插件内置手机号登录公钥 手机号验证码登录加密手机号和一次性 secret 使用的 RSA 公钥,通常不需要设置
ZHANLU_APIKEY_AUTH_SM2_PRIVATE_KEY 插件内置 SM2 私钥 换取模型 API Key 时 X-Auth-Signature 使用的 SM2 私钥,通常不需要设置
ZHANLU_PLUGIN_VERSION 1.4.2 请求头 plugin_version
ZHANLU_UPSTREAM_TIMEOUT 300s 上游请求超时
ZHANLU_STREAM_IDLE_TIMEOUT 300s 预留的流式空闲超时配置
ZHANLU_LOGIN_PASSWORD /login 管理页面密码;设置后登录成功跳转到 /admin/login 管理湛卢凭据
ZHANLU_DEBUG false 调试模式,错误信息更详细但会脱敏敏感 query
OPENAI_COMPAT_API_KEY 本地 OpenAI 兼容接口鉴权 key

凭据优先级

服务启动时按以下优先级加载凭据:

  1. 环境变量 ZHANLU_ACCESS_KEYZHANLU_SECRET_KEYZHANLU_TOKENZHANLU_API_KEY
  2. ZHANLU_DB_FILE 数据库中持久化的凭据行。

登录页面保存后,运行中的服务会立即使用新凭据并写入数据库。若环境中只有 AK/SK/Token 而没有 apiKey,首次调用聊天接口时会自动按插件流程换取 API Key 并回写数据库。

Token 消耗统计

代理在 /v1/chat/completions 完成后解析上游 usage(流式路径在透传 SSE 的同时旁路解析末块 usage,非流式路径在聚合时解析),将以下维度写入 ZHANLU_DB_FILE

  • 时间、模型、流式/非流式
  • prompt_tokens / completion_tokens / total_tokens / reasoning_tokens(思考模型)
  • cached_tokens(来自 prompt_tokens_details.cached_tokens,提示缓存命中的 token 数)与缓存命中率
  • 请求状态(success / upstream_error)与耗时

缓存命中率为 cached_tokens / prompt_tokens,仅在模型/网关支持 prompt 缓存且上游返回 cached_tokens 时非零。

管理页(需先登录管理页面)提供:

  • GET /admin/stats:可视化页面,展示总览、按模型、按日柱状、最近请求明细,带重置按钮。
  • GET /api/statsJSON 接口,支持 since/untilRFC3339)、modellimit 查询参数。
  • POST /api/stats/reset:清空统计(凭据不受影响)。

设置 ZHANLU_STATS_DISABLED=true 可停止写入统计。

可用模型

管理后台「可用模型」tab(位于「Token 统计」之后)实时展示当前上游接口返回的模型列表,并提供单模型可用性与延时探测:

  • 进入 tab 时自动拉取一次,也可随时点击「刷新」重新获取。
  • 每个模型行可单独「测试」,或点击「全部测试」依次探测所有模型。
  • 探测向上游 /chat/completions 发送一条极简流式请求(hi),测量首字延时(TTFT,首个 SSE 数据块到达时间)与总耗时,并在收到首个内容块后立即关闭连接,避免消耗额外 token。
  • 探测请求不计入 Token 统计。

对应 JSON 接口(需先登录管理页面):

  • GET /api/models:实时返回上游模型列表 {"ok":true,"models":["GLM-4.7",...]}
  • POST /api/models/test:请求体 {"model":"<model_id>"},返回 {"ok":true,"model":"...","available":true,"ttft_ms":123,"total_ms":456}{"ok":true,"model":"...","available":false,"error":"...","total_ms":789}

探测超时上限为 30 秒(modelTestTimeout);凭据未配置或 API Key 缺失时 GET /api/modelsPOST /api/models/test 会按需自动换取 API Key,与聊天接口一致。

安全说明

  • zhanlu.db 数据库包含明文 AccessKeySecretKeyTokenapiKey,请不要提交到仓库。
  • 默认保存在当前执行目录的 zhanlu.db(建议通过 ZHANLU_DB_FILE 指向受保护路径)。
  • 建议设置 ZHANLU_LOGIN_PASSWORD,避免公网暴露的 /admin/login 被直接访问。
  • 错误响应默认不会返回签名 URL,避免泄露 AccessKeyauthorizationSignature
  • ZHANLU_DEBUG=true 时会返回更详细错误,但仍会对敏感 query 参数脱敏。

已知限制

  • 灵犀内网 SSO/auth/start/auth/callback)为备用接口,主流程是移动云手机号验证码登录。
  • 手机号验证码接口可能有风控或频率限制;请按正常登录频率使用。
  • 模型网关对 OpenAI stream:false 请求由代理负责聚合流式响应。

验证

go test ./...
go build ./cmd/zhanlu-proxy

本地端点验证:

go run ./cmd/zhanlu-proxy
curl http://127.0.0.1:8080/healthz
curl http://127.0.0.1:8080/v1/models
S
Description
No description provided
Readme
402 KiB
v0.2.1
Latest
2026-08-23 22:16:32 +08:00
Languages
Go 81.1%
HTML 17.7%
Shell 1.2%