neipor/codebuddy-cli2api

GitHub: neipor/codebuddy-cli2api

该网关通过协议转换与多密钥缓存感知轮换,解决了腾讯 CodeBuddy 后端仅支持流式传输且拒绝远程图片的兼容性难题。

Stars: 1 | Forks: 0

# codebuddy-cli2api ![Python](https://img.shields.io/badge/python-3.11+-blue.svg) ![Dependencies](https://img.shields.io/badge/dependencies-0-success.svg) ![License](https://img.shields.io/badge/license-MIT-green.svg) ![Formats](https://img.shields.io/badge/formats-OpenAI%20·%20Anthropic%20·%20Gemini%20·%20Codex-orange.svg) ![Models](https://img.shields.io/badge/models-20+-purple.svg) 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, 代理服务, 兼容层, 开发工具, 无后门