neipor/codebuddy-cli2api
GitHub: neipor/codebuddy-cli2api
该网关通过协议转换与多密钥缓存感知轮换,解决了腾讯 CodeBuddy 后端仅支持流式传输且拒绝远程图片的兼容性难题。
Stars: 1 | Forks: 0
# codebuddy-cli2api





CodeBuddy Code (`@tencent-ai/codebuddy-code`) 是腾讯的编程代理 CLI。其后端
(`POST https://www.codebuddy.cn/v2/chat/completions`) 使用标准的
OpenAI Chat Completions 通信格式 —— 但有两个硬性限制:
1. **仅支持流式传输 (Streaming only)。** `stream: false` 会被拒绝并返回 `400` 错误。然而,大多数客户端
(Hermes 子代理、压缩、标题生成、健康检查、Anthropic /
Gemini / Codex SDK)会发起非流式调用。
2. **仅支持 Data-URI 图片。** 远程 `http(s)` 图片 URL 会被拒绝并返回 `400` 错误。
本网关位于 CodeBuddy 前端并消除了这两个限制,同时支持在各种主流 API 格式之间进行转换,并通过轮换密钥来最大化每个账户的 prompt cache。
## ✨ 功能特性
- **四种 API 格式,一个后端** —— 支持 OpenAI Chat Completions、Anthropic Messages、
Google Gemini 和 OpenAI Responses (Codex)。可将任何客户端指向它。
- **透明支持非流式传输** —— 强制在上游开启 `stream: true` 并将
SSE 聚合为一个完整的响应对象,因此非流式客户端
也能正常工作。
- **支持多密钥的缓存感知轮换** —— 前缀亲和路由确保每个密钥的
prompt cache 保持活跃;新的前缀会被分配给 **缓存命中率**
最高的密钥**,并以最少请求数作为平局判定(实现负载均衡)。401 会禁用密钥,429
会进行退避,5xx 会进行故障转移 —— 所有这些都在数据到达客户端之前完成。
- **Vision** —— 将来自所有四种格式的图像标准化为 OpenAI `image_url`
data URI,并**获取并内联**远程 URL(CodeBuddy 会拒绝远程 URL)。
- **零依赖** —— 仅使用 Python 3.11+ 标准库。无需 `pip install`。
- **Hermes Agent 插件** —— 一个可直接插入的 `ProviderProfile`,用于引导 Hermes
通过本网关路由。无需修改核心代码。
## 🏗️ 架构
```
OpenAI SDK ────────┐
Anthropic SDK ──────┤
Gemini SDK ────────┼──► ┌──────────────────────────────────────────┐
Codex CLI ────────┤ │ codebuddy_gateway │
Hermes Agent ───────┘ │ (127.0.0.1:8787, stdlib) │
│ │
│ 1. parse request in client's format │
│ 2. normalize images → data URIs │
│ 3. pick key (prefix-affinity + cache) │
│ 4. POST codebuddy.cn/v2 (stream:true) │
│ 5. aggregate SSE → full response │
│ 6. emit in client's format │
└──────────────────────────────────────────┘
│
▼
https://www.codebuddy.cn/v2/chat/completions
(GLM · DeepSeek · Kimi · Hunyuan · MiniMax · Hy3)
```
关键技巧在于:CodeBuddy 仅支持流式传输,因此网关**始终**在上游进行流式传输,
然后要么转发 SSE(对于流式客户端),要么将其聚合
为一个单一的 JSON 对象(对于非流式客户端)。每种格式的每一个非流式调用
都通过同一个聚合器进行处理。
## 🔬 逆向工程 API(已验证)
通过对 `@tencent-ai/codebuddy-code@2.118.2` 进行数据包抓取
(注入 Node `zlib`/`https`)并进行逆向工程,且已通过 `curl` 确认。
| | |
|---|---|
| **Endpoint** | `POST https://www.codebuddy.cn/v2/chat/completions` |
| **Auth** | `Authorization: Bearer ck_…` **或** `X-API-Key: ck_…`(两者均接受) |
| **Body** | 标准 OpenAI Chat Completions;**必须设置 `stream: true`** |
| **Response** | OpenAI SSE — `data:{chunk}\n\n` … `data:[DONE]`; `delta.{content,reasoning_content,tool_calls}`; 最后一个 chunk 包含 `usage` |
| **Caching** | 基于单个密钥的 prompt cache;`usage.prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` |
| **Vision** | `image_url` 仅支持 **data URIs**(http URLs → `400`);支持的模型有 `glm-4.6v`, `glm-5v-turbo`, `deepseek-v4-pro`, `hy3`, `kimi-k2.6`, … |
| **Catalog** | 无服务端 REST 目录(`/v2/models`, `/v2/config` 均为 `404`);内置于 npm 包的 `product.cloudhosted.json` 中 |
| **Default model** | `hy3` |
## 📦 项目结构
```
codebuddy_gateway/ # the universal gateway (stdlib only)
common.py # config, model catalog, routing headers, errors
upstream.py # stream_codebuddy() — forces stream:true, yields OpenAI chunks
aggregate.py # aggregate() — assembles a full chat.completion from SSE
images.py # normalize images from all 4 formats → data URIs; fetch http
rotation.py # KeyPool — prefix-affinity + cache-aware multi-key rotation
server.py # ThreadingHTTPServer, routing, SSE flush, usage tee
__main__.py # CLI entry: python -m codebuddy_gateway
formats/
openai.py # passthrough OpenAI Chat Completions
anthropic.py # Anthropic Messages ⇄ OpenAI
gemini.py # Google Gemini ⇄ OpenAI
codex.py # OpenAI Responses / Codex ⇄ OpenAI
plugins/model-providers/codebuddy/ # Hermes Agent provider plugin
__init__.py # CodeBuddyProfile(ProviderProfile)
plugin.yaml # plugin manifest
requirements.txt .env.example README.md LICENSE
```
## 🚀 快速开始
```
# 1. 克隆并进入
git clone https://github.com/neipor/codebuddy-cli2api.git
cd codebuddy-cli2api
# 2. 设置你的 CodeBuddy key(来自 CodeBuddy CLI 登录)
export CODEBUDDY_API_KEY=ck_yourkeyid.yoursecret
# 3. 运行 — 无需安装步骤,仅使用 stdlib
python3 -m codebuddy_gateway # listens on http://127.0.0.1:8787
python3 -m codebuddy_gateway --print-models # print the model catalog
# 4. 冒烟测试(非 stream → 网关会为你进行聚合)
curl http://127.0.0.1:8787/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"hy3","messages":[{"role":"user","content":"say hi"}]}'
```
## 📡 端点
| Method | Path | Format |
|---|---|---|
| POST | `/v1/chat/completions` | OpenAI Chat Completions |
| POST | `/v1/messages` | Anthropic Messages |
| POST | `/v1beta/models/{m}:generateContent` | Gemini (非流式) |
| POST | `/v1beta/models/{m}:streamGenerateContent` | Gemini (流式, SSE) |
| POST | `/v1/responses` | OpenAI Responses / Codex |
| GET | `/v1/models` · `/v1beta/models` · `/models` | 模型目录 |
| GET | `/health` | 健康检查 |
| GET | `/debug/keys` | 轮换统计信息(每个密钥的命中率) |
**客户端认证:** 如果设置了 `GATEWAY_API_KEY`,客户端必须在
`Authorization: Bearer`、`x-api-key`、`x-goog-api-key` 或 `?key=` 中发送它。如果未设置,
则不需要客户端认证(网关掌握上游凭证;请在 localhost 上运行)。
### Curl 示例 —— 四种格式
```
GW=http://127.0.0.1:8787
# OpenAI(stream)
curl -sS $GW/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"glm-4.7","stream":true,"messages":[{"role":"user","content":"hi"}]}'
# Anthropic
curl -sS $GW/v1/messages -H 'Content-Type: application/json' -H 'anthropic-version: 2023-06-01' \
-d '{"model":"glm-4.7","max_tokens":50,"messages":[{"role":"user","content":"hi"}]}'
# Gemini
curl -sS "$GW/v1beta/models/glm-4.7:generateContent" -H 'Content-Type: application/json' \
-d '{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}'
# Codex / Responses
curl -sS $GW/v1/responses -H 'Content-Type: application/json' \
-d '{"model":"glm-4.7","input":"hi"}'
```
工具调用、`reasoning_content` 以及多轮对话在每种格式下
均可正常工作。
## 🔑 多密钥缓存感知轮换
CodeBuddy 的 prompt cache 是**按账户划分的**。网关利用以下三条路由规则来优化它:
1. **前缀亲和性** —— `sha256(system + first user message)` 会绑定到一个密钥,
使得该密钥的 cache 保持活跃。(已验证:具有相同
前缀的第 2 次请求返回了 **704 个 cache-hit tokens**,而冷启动为 0。)
2. **新前缀 → 命中率最高的密钥** —— 全新的前缀会被分配给
**缓存命中率**最高的健康密钥**,以**最少请求数**作为平局判定(实现负载均衡),然后按 LRU 处理。
3. **首字节前故障转移** —— 401 禁用密钥(并重新分配其前缀);429 退避 30 秒;在*任何*数据发送给客户端*之前*,5xx/429 会在另一个密钥上重试。
```
request ──► prefix_hash = sha256(system + first user msg)
│
seen before? ──yes──► sticky key (warm cache)
│ no
▼
pick healthy key with highest cache-hit rate
(tiebreak: fewest requests, then LRU)
```
使用 `CODEBUDDY_API_KEYS`(以逗号/换行符分隔)或
`CODEBUDDY_KEYS_FILE`(每行一个)进行配置。查看实时统计信息:
```
curl -sS http://127.0.0.1:8787/debug/keys
# {
# "keys": [
# {"key_tail":"…a1b2c3","requests":4,"cache_hit_tokens":704,
# "cache_miss_tokens":816,"hit_rate":0.4632,"prefixes":3,
# "state":"active","last_used":"2026-07-11T22:18:01Z"}
# ],
# "total_keys":1
# }
```
## 🖼️ Vision / 图像
以任何格式的原生形态发送图像 —— OpenAI `image_url`、Anthropic
`image`/`source`、Gemini `inline_data`/`file_data`、Codex `input_image`。网关将它们全部标准化为 OpenAI `image_url` **data URIs**,如果你传入一个 `http(s)` URL,它会抓取并内联该 URL(CodeBuddy 会拒绝远程 URL)。请使用支持 Vision 的模型:`glm-4.6v`、`glm-5v-turbo`、`deepseek-v4-pro`、`hy3`、`kimi-k2.6`、`minimax-m2.7`、`hunyuan-2.0-instruct` 等。
```
# 合成的 2×2 红色 PNG(无需图像文件)
IMG=$(python3 -c "import base64,struct,zlib; w=h=2; raw=b''.join(b'\x00'+b'\xff\x00\x00'*w for _ in range(h));
def c(t,d):
x=t+d; return struct.pack('>I',len(d))+x+struct.pack('>I',zlib.crc32(x)&0xffffffff)
print(base64.b64encode(b'\\x89PNG\\r\\n\\x1a\\n'+c(b'IHDR',struct.pack('>IIBBBBB',w,h,8,2,0,0,0))+c(b'IDAT',zlib.compress(raw))+c(b'IEND',b'')).decode())")
curl -sS http://127.0.0.1:8787/v1/chat/completions -H 'Content-Type: application/json' \
-d "{\"model\":\"glm-4.6v\",\"messages\":[{\"role\":\"user\",\"content\":[
{\"type\":\"text\",\"text\":\"what color is this?\"},
{\"type\":\"image_url\",\"image_url\":{\"url\":\"data:image/png;base64,$IMG\"}}]}]}"
```
## 🤖 Hermes Agent 集成
`plugins/model-providers/codebuddy/` 目录是一个 Hermes `ProviderProfile`
插件 —— 无需修改 Hermes 核心代码。
```
# 1. 将 plugin 安装到 Hermes 的用户 plugin 目录中
mkdir -p ~/.hermes/plugins/model-providers
cp -r plugins/model-providers/codebuddy ~/.hermes/plugins/model-providers/
# 2. 设置 key(网关会读取它;Hermes 会将其作为 Bearer 传递)
echo 'CODEBUDDY_API_KEY=ck_yourkeyid.yoursecret' >> ~/.hermes/.env
# 3. 启动网关
python3 -m codebuddy_gateway &
# 4. 配置 Hermes — 在 ~/.hermes/config.yaml 中:
# model:
# provider: codebuddy
# default: hy3
# 5. 验证并运行
hermes doctor # health check → gateway's /v1/models
hermes # chat (streaming); subagents/compression use non-stream aggregation
```
`hermes model` 会直接从网关的目录中列出 CodeBuddy 模型。该配置的 `base_url` (`http://127.0.0.1:8787/v1`) 可以通过 `config.yaml` 中的 `model.base_url` 进行覆盖。
## 🔌 其他客户端
由于网关支持所有四种格式,因此任何客户端都可以使用:
| Client | Setting |
|---|---|
| **Codex CLI** | `OPENAI_BASE_URL=http://127.0.0.1:8787/v1` |
| **OpenAI SDK** | `base_url="http://127.0.0.1:8787/v1"` |
| **Anthropic SDK** | `base_url="http://127.0.0.1:8787"` |
| **Google Gen AI SDK** | `client_options={"api_endpoint":"http://127.0.0.1:8787"}` |
## 🧠 工作原理
1. **解析** 以客户端格式(`formats/*.py`)传入的请求,转化为
标准的 OpenAI Chat Completions 请求体,并在此过程中标准化图像。
2. **选择密钥** 通过 `KeyPool.pick()` —— 先遵循前缀亲和性,然后基于缓存命中率。
3. **上游流式传输** 强制开启 `stream: true`,重放 CLI 的路由请求头(`X-Product`, `X-IDE-Type`, …)以保持指纹一致性。
4. **分流用量** 从最后一个 chunk 反馈到池(`record()`)中,以便
下一次路由决策具备缓存感知能力。
5. **输出** 以客户端格式返回结果:为流式客户端转发 SSE,
或为非流式客户端将 chunks 聚合为一个 JSON 对象。
如果上游在任何字节发送之前出错,`_connect()` 会尝试另一个密钥 —— 客户端永远不会看到失败。
## ⚙️ 配置
所有配置均通过环境变量进行(参见 `.env.example`)。
| Variable | Default | Description |
|---|---|---|
| `CODEBUDDY_API_KEY` | — | 单个 CodeBuddy 密钥 (`ck_…`)。必填。 |
| `CODEBUDDY_API_KEYS` | — | 多个密钥,以逗号或换行符分隔(启用轮换功能)。 |
| `CODEBUDDY_KEYS_FILE` | — | 每行包含一个密钥的文件路径(允许使用 `#` 注释)。 |
| `CODEBUDDY_BASE_URL` | `https://www.codebuddy.cn` | CodeBuddy 后端。`ck_` 密钥需要 `.cn` 主机。 |
| `CODEBUDDY_API_PATH` | `/v2/chat/completions` | 上游聊天路径。 |
| `GATEWAY_HOST` | `127.0.0.1` | 网关监听地址。 |
| `GATEWAY_PORT` | `8787` | 网关监听端口。 |
| `GATEWAY_API_KEY` | — | 可选的、客户端必须提供的共享密钥。 |
| `CODEBUDDY_DEFAULT_MODEL` | `hy3` | 请求中省略模型时使用的默认模型。 |
| `CODEBUDDY_TIMEOUT` | `180` | 上游请求超时时间(秒)。 |
| `IMAGE_FETCH_MAX_BYTES` | `20971520` | 内联远程图像时的字节大小上限。 |
| `GATEWAY_LOG_LEVEL` | `INFO` | 日志记录级别。 |
| `CODEBUDDY_RESOURCE_BASE_URL` | `https://copilot.tencent.com` | 用于 OAuth 资源端点的上游(见下文)。 |
| `CODEBUDDY_AUTH_FILE` | auto | 用于 OAuth 资源的 WorkBuddy/CodeBuddy 会话文件。 |
| `CODEBUDDY_REMOTE` | — | `1` 绑定 `0.0.0.0`;需要设置 `GATEWAY_API_KEY`。 |
| `CODEBUDDY_AUTO_CHECKIN` | `1` | 每个 UTC 日自动领取一次每日签到积分。 |
## 🎫 WorkBuddy 资源端点(OAuth)与远程部署
除 chat 外,网关还透传 WorkBuddy/CodeBuddy 的免费/付费资源端点。这些端点强制
`Bearer `(`ck_` key 无效),网关自己持有 OAuth session(默认读
桌面 WorkBuddy 的 `workbuddy-desktop.info` 或 CLI 的 `~/.codebuddy/auth/*.info`,自动
刷新),客户端只需 `GATEWAY_API_KEY`,永远接触不到 token。chat 路径不变,仍走
`ck_` key(无有效 key 时自动回落到 OAuth session)。
| 网关路径 | 上游 | 说明 |
|---|---|---|
| `POST /1/checkin` | `/v2/billing/meter/daily-checkin` | 每日签到(空 body) |
| `POST /v1/checkin-status` | `/billing/meter/checkin-status` | 当日签到状态 |
| `POST /v1/checkin-activity` | `/v2/billing/meter/checkin-activity-status` | 活动状态 |
| `POST /v1/asr` | `/agenttool/v1/asr` | 录音转文字,multipart(字段 `audio`+`fileName`) |
| `POST /v1/tts` | `/agenttool/v1/tts` | 语音合成,JSON `{text}`,SSE 流式返回 |
| `POST /v1/tempkey` | `/agenttool/v1/tempkey` | 临时云凭证 |
| `POST /v1/search` / `/v1/web_search` | `/agenttool/v1/search` | 联网搜索 |
| `POST /v1/webfetch` | `/agenttool/v1/webfetch` | 抓取网页为 markdown |
| `POST /v1/images/generations` | `/v2/images/generations` | 文生图(`/v1` 别名把 `data.data[]` 归一为 `data[]`) |
| `POST /v1/images/edits` | `/v2/images/edits` | 图生图 |
| `POST /v1/videos/generations` | `/v2/videos/generations` | 文生视频(返回 task id) |
| `POST /v1/videos/tasks` | `/v2/videos/tasks` | 轮询视频任务 |
上游原始路径(`/agenttool/v1/...`、`/v2/images/...` 等)也直接可用,行为一致。
**域名勘误**:`www.codebuddy.ai` 会对 OAuth token 直接 401(边缘网关拒绝);实测
`https://copilot.tencent.com`(桌面 WorkBuddy 默认端点)与 `www.codebuddy.cn` 全端点
可用,默认取前者,用 `CODEBUDDY_RESOURCE_BASE_URL` 覆盖。
**自动签到**:网关启动后内置守护线程,每个 UTC 日自动领一次 `/v1/checkin`(每日
100 积分),不依赖任何客户端(OMP 不开、端口不暴露也照常执行),失败每小时重试,
`CODEBUDDY_AUTO_CHECKIN=0` 关闭。
**远程部署(令牌跨机器,不暴露端口)**:默认网关只监听 `127.0.0.1`,其它机器用
SSH 隧道访问(不用开 `CODEBUDDY_REMOTE`):
```
# 持有 session 的机器(常开/云主机)
GATEWAY_API_KEY=<随机串> python3 -m codebuddy_gateway
# 其它机器:一条隧道,之后所有客户端都指 127.0.0.1:8787
ssh -N -L 8787:127.0.0.1:8787 user@gateway-host
curl http://127.0.0.1:8787/health -H "Authorization: Bearer <随机串>"
```
可选:确实需要直连时(如 Tailscale 网段内)再设 `CODEBUDDY_REMOTE=1` 绑定
`0.0.0.0`,此时未设 `GATEWAY_API_KEY` 会拒绝启动。首次部署无 session 文件时运行
`python3 -m codebuddy_gateway --login` 走浏览器外链登录。
**OMP / oh-my-pi 集成**:`~/.omp/agent/extensions/workbuddy-tools.ts` 注册
`workbuddy` provider(`--model workbuddy/hy3`)与 `codebuddy_checkin`/`codebuddy_asr`/
`codebuddy_tts` 工具,并在每次 session 启动时自动签到一次。
## 🛠️ 故障排除
- **`401 {"message":"not_found"}`** —— 你当前访问的是 `www.codebuddy.ai`;`ck_`
密钥适用于 `www.codebuddy.cn`。请将 `CODEBUDDY_BASE_URL` 保持为默认值。
- **`400 Non-stream chat request is currently not supported`** —— 只有在你绕过网关并直接使用 `stream: false` 访问 CodeBuddy 时才会发生。请使用网关(它会强制流式传输并进行聚合)。
- **`400 invalid parameter value` (图像)** —— 你直接向 CodeBuddy 传递了一个 `http` 图片 URL。网关会抓取并内联该图片;而直接模式不会。
- **Hermes 子代理 / 压缩失败** —— 你正处于直接模式。请使用网关;它会透明地聚合非流式调用。
- **`no CodeBuddy API keys configured`** —— 在启动网关之前设置 `CODEBUDDY_API_KEY`(或 `CODEBUDDY_API_KEYS` / `CODEBUDDY_KEYS_FILE`)。
## 📄 许可证
MIT —— 详见 [LICENSE](LICENSE)。
CodeBuddy 是腾讯的产品。本项目是一个独立的互操作性
适配器,不附属于腾讯,也未获得腾讯的认可。请依照 CodeBuddy 的条款使用你自己的 API
凭证。
标签:AI大模型, API网关, Python, SOC Prime, 代理服务, 兼容层, 开发工具, 无后门