Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codebuddy-cli2api

A zero-dependency gateway that turns Tencent CodeBuddy Code's CLI inference API into a universal, OpenAI-compatible endpoint — and a Hermes Agent provider plugin. Speaks OpenAI, Anthropic, Gemini, and Codex / Responses natively, with multi-key cache-aware rotation and vision support.

Python Dependencies License Formats Models

CodeBuddy Code (@tencent-ai/codebuddy-code) is Tencent's coding agent CLI. Its backend (POST https://www.codebuddy.cn/v2/chat/completions) speaks standard OpenAI Chat Completions wire format — but with two hard constraints:

  1. Streaming only. stream: false is rejected with 400. Yet most clients (Hermes subagents, compression, title-gen, health checks, the Anthropic / Gemini / Codex SDKs) issue non-streaming calls.
  2. Data-URI images only. Remote http(s) image URLs are rejected with 400.

This gateway sits in front of CodeBuddy and removes both constraints, while also translating between every major API format and rotating keys to maximize the per-account prompt cache.


✨ Features

  • Four API formats, one backend — OpenAI Chat Completions, Anthropic Messages, Google Gemini, and OpenAI Responses (Codex). Point any client at it.
  • Non-stream transparently supported — forces stream: true upstream and aggregates the SSE into a complete response object, so non-streaming clients just work.
  • Multi-key cache-aware rotation — prefix-affinity routing keeps each key's prompt cache warm; new prefixes go to the key with the highest cache-hit rate, tie-broken by fewest requests (even distribution). 401 disables, 429 backs off, 5xx fails over — all before any bytes reach the client.
  • Vision — normalizes images from all four formats to OpenAI image_url data URIs, and fetches + inlines remote URLs (which CodeBuddy rejects).
  • Zero dependencies — Python 3.11+ standard library only. No pip install.
  • Hermes Agent plugin — a drop-in ProviderProfile that routes Hermes through the gateway. No core modifications.

🏗️ Architecture

   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)

The key trick: CodeBuddy is stream-only, so the gateway always streams upstream and then either relays the SSE (for streaming clients) or aggregates it into a single JSON object (for non-streaming clients). Every non-stream call in every format flows through the same aggregator.


🔬 Reverse-engineered API (verified)

Reverse-engineered from @tencent-ai/[email protected] via packet capture (Node zlib/https injection) and confirmed with curl.

Endpoint POST https://www.codebuddy.cn/v2/chat/completions
Auth Authorization: Bearer ck_… or X-API-Key: ck_… (both accepted)
Body Standard OpenAI Chat Completions; stream: true required
Response OpenAI SSE — data:{chunk}\n\n … data:[DONE]; delta.{content,reasoning_content,tool_calls}; usage in the final chunk
Caching Per-key prompt cache; usage.prompt_cache_hit_tokens / prompt_cache_miss_tokens
Vision image_url with data URIs only (http URLs → 400); models glm-4.6v, glm-5v-turbo, deepseek-v4-pro, hy3, kimi-k2.6, …
Catalog No server-side REST catalog (/v2/models, /v2/config all 404); bundled in the npm package's product.cloudhosted.json
Default model hy3

⚠️ Keys starting with ck_ are issued by www.codebuddy.cn and are rejected by www.codebuddy.ai with 401 {"message":"not_found"}. The gateway defaults to the .cn host.


📦 Project layout

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

🚀 Quick start

# 1. Clone & enter
git clone https://github.com/neipor/codebuddy-cli2api.git
cd codebuddy-cli2api

# 2. Set your CodeBuddy key (from the CodeBuddy CLI login)
export CODEBUDDY_API_KEY=ck_yourkeyid.yoursecret

# 3. Run — no install step, stdlib only
python3 -m codebuddy_gateway            # listens on http://127.0.0.1:8787
python3 -m codebuddy_gateway --print-models   # print the model catalog

# 4. Smoke test (non-stream → the gateway aggregates for you)
curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"hy3","messages":[{"role":"user","content":"say hi"}]}'

📡 Endpoints

Method Path Format
POST /v1/chat/completions OpenAI Chat Completions
POST /v1/messages Anthropic Messages
POST /v1beta/models/{m}:generateContent Gemini (non-stream)
POST /v1beta/models/{m}:streamGenerateContent Gemini (stream, SSE)
POST /v1/responses OpenAI Responses / Codex
GET /v1/models · /v1beta/models · /models model catalog
GET /health health check
GET /debug/keys rotation stats (per-key hit rates)

Client auth: if GATEWAY_API_KEY is set, clients must send it in Authorization: Bearer, x-api-key, x-goog-api-key, or ?key=. If unset, no client auth is required (the gateway owns the upstream credential; run it on localhost).

Curl examples — all four formats

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"}'

Tool calls, reasoning_content, and multi-turn conversations all work across every format.


🔑 Multi-key cache-aware rotation

CodeBuddy's prompt cache is per-account. The gateway exploits this with three routing rules:

  1. Prefix affinity — sha256(system + first user message) sticks to one key, so that key's cache stays warm. (Verified: the 2nd request with the same prefix returned 704 cache-hit tokens vs 0 cold.)
  2. New prefix → highest hit-rate key — a brand-new prefix is assigned to the healthy key with the highest cache-hit rate, tie-broken by fewest requests (even distribution), then LRU.
  3. Failover before first byte — 401 disables the key (and reassigns its prefixes); 429 backs off 30s; 5xx/429 are retried on another key before any bytes are sent to the client.
   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)

Configure with CODEBUDDY_API_KEYS (comma/newline-separated) or CODEBUDDY_KEYS_FILE (one per line). Inspect live stats:

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 / images

Send images in any format's native shape — OpenAI image_url, Anthropic image/source, Gemini inline_data/file_data, Codex input_image. The gateway normalizes them all to OpenAI image_url data URIs, and if you pass an http(s) URL it fetches and inlines it (CodeBuddy rejects remote URLs). Use a vision-capable model: glm-4.6v, glm-5v-turbo, deepseek-v4-pro, hy3, kimi-k2.6, minimax-m2.7, hunyuan-2.0-instruct, …

# Synthetic 2×2 red PNG (no image file needed)
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 integration

The plugins/model-providers/codebuddy/ directory is a Hermes ProviderProfile plugin — no Hermes core changes required.

# 1. Install the plugin into Hermes' user plugin dir
mkdir -p ~/.hermes/plugins/model-providers
cp -r plugins/model-providers/codebuddy ~/.hermes/plugins/model-providers/

# 2. Set the key (the gateway reads it; Hermes passes it through as Bearer)
echo 'CODEBUDDY_API_KEY=ck_yourkeyid.yoursecret' >> ~/.hermes/.env

# 3. Start the gateway
python3 -m codebuddy_gateway &

# 4. Configure Hermes — in ~/.hermes/config.yaml:
#    model:
#      provider: codebuddy
#      default: hy3

# 5. Verify & run
hermes doctor     # health check → gateway's /v1/models
hermes            # chat (streaming); subagents/compression use non-stream aggregation

hermes model lists CodeBuddy models straight from the gateway's catalog. The profile's base_url (http://127.0.0.1:8787/v1) is overridable via model.base_url in config.yaml.


🔌 Other clients

Because the gateway speaks all four formats, any client works:

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"}

🧠 How it works

  1. Parse the inbound request in the client's format (formats/*.py) into a canonical OpenAI Chat Completions body, normalizing images along the way.
  2. Pick a key via KeyPool.pick() — prefix-affinity, then cache-hit-rate.
  3. Stream upstream with stream: true forced on, replaying the CLI's routing headers (X-Product, X-IDE-Type, …) for fingerprint parity.
  4. Tee the usage from the final chunk back into the pool (record()), so the next routing decision is cache-aware.
  5. Emit the result in the client's format: relay SSE for streaming clients, or aggregate the chunks into one JSON object for non-streaming clients.

If the upstream errors before any bytes are sent, _connect() retries on another key — the client never sees the failure.


⚙️ Configuration

All config is via environment variables (see .env.example).

Variable Default Description
CODEBUDDY_API_KEY — A single CodeBuddy key (ck_…). Required.
CODEBUDDY_API_KEYS — Multiple keys, comma- or newline-separated (enables rotation).
CODEBUDDY_KEYS_FILE — Path to a file with one key per line (# comments allowed).
CODEBUDDY_BASE_URL https://www.codebuddy.cn CodeBuddy backend. ck_ keys need the .cn host.
CODEBUDDY_API_PATH /v2/chat/completions Upstream chat path.
GATEWAY_HOST 127.0.0.1 Gateway listen address.
GATEWAY_PORT 8787 Gateway listen port.
GATEWAY_API_KEY — Optional shared secret clients must present.
CODEBUDDY_DEFAULT_MODEL hy3 Model used when a request omits one.
CODEBUDDY_TIMEOUT 180 Upstream request timeout (seconds).
IMAGE_FETCH_MAX_BYTES 20971520 Cap when inlining a remote image.
GATEWAY_LOG_LEVEL INFO Logging level.
CODEBUDDY_RESOURCE_BASE_URL https://copilot.tencent.com Upstream for OAuth resource endpoints (see below).
CODEBUDDY_AUTH_FILE auto WorkBuddy/CodeBuddy session file for OAuth resources.
CODEBUDDY_REMOTE — 1 binds 0.0.0.0; requires GATEWAY_API_KEY.
CODEBUDDY_AUTO_CHECKIN 1 Auto-claim the daily check-in credit once per UTC day.

🎫 WorkBuddy 资源端点(OAuth)与远程部署

除 chat 外,网关还透传 WorkBuddy/CodeBuddy 的免费/付费资源端点。这些端点强制 Bearer <OAuth accessToken>(ck_ key 无效),网关自己持有 OAuth session(默认读 桌面 WorkBuddy 的 workbuddy-desktop.info 或 CLI 的 ~/.codebuddy/auth/*.info,自动 刷新),客户端只需 GATEWAY_API_KEY,永远接触不到 token。chat 路径不变,仍走 ck_ key(无有效 key 时自动回落到 OAuth session)。

网关路径 上游 说明
POST /v1/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 启动时自动签到一次。


🛠️ Troubleshooting

  • 401 {"message":"not_found"} — you're hitting www.codebuddy.ai; ck_ keys are for www.codebuddy.cn. Leave CODEBUDDY_BASE_URL at its default.
  • 400 Non-stream chat request is currently not supported — only happens if you bypass the gateway and hit CodeBuddy directly with stream: false. Use the gateway (it forces streaming and aggregates).
  • 400 invalid parameter value (images) — you passed an http image URL directly to CodeBuddy. The gateway fetches & inlines it; direct mode doesn't.
  • Hermes subagent / compression fails — you're in direct mode. Use the gateway; it aggregates non-stream calls transparently.
  • no CodeBuddy API keys configured — set CODEBUDDY_API_KEY (or CODEBUDDY_API_KEYS / CODEBUDDY_KEYS_FILE) before starting the gateway.

📄 License

MIT — see LICENSE.

CodeBuddy is a product of Tencent. This project is an independent interoperability adapter and is not affiliated with or endorsed by Tencent. Use your own API credentials in accordance with CodeBuddy's terms.

About

Universal cli2api gateway for Tencent CodeBuddy Code — OpenAI/Anthropic/Gemini/Codex formats, multi-key cache-aware rotation, vision, + Hermes Agent plugin. Python stdlib only, zero deps.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages