Skip to content

About

A lightweight API proxy for domestic and international CodeBuddy backends, providing OpenAI, Anthropic, and Responses-compatible APIs with native support for Claude Code, Codex CLI, and DeepSeek Harness.

Topics

Resources

Stars

25 stars

Watchers

0 watching

Forks

Latest commit

 

History

111 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeBuddy API Proxy

A lightweight API proxy service that converts CodeBuddy's underlying interface into standard OpenAI, Anthropic, and Responses protocol formats.

中文版文档见 README_zh.md.

For unexplained Windows process exits, use the independent process monitor to collect uv/Python exit codes, lifecycle, memory, health, and available Windows events without restarting the proxy.

✨ Core Features

  • Protocol conversion - Supports three standard formats: OpenAI Chat Completions, Anthropic Messages API, and Responses
  • Desensitization - Built-in smart desensitization module that automatically filters sensitive information (accounts, passwords, keys, brand terms, paths, etc.) to mitigate review-based false blocks
  • Message compression - Intelligently compresses historical messages to dramatically reduce token usage (ideal for long-context scenarios such as Codex CLI)
  • Tool call support - Full support for function calling and tool use, with automatic filtering of invalid tool definitions
  • DSML parsing - Automatically detects and converts DeepSeek Markup Language (DSML) tool calls
  • Streaming responses - SSE streaming output, returning generated content in real time, with built-in 60-second timeout protection
  • Domestic and international backends - Uses domestic CodeBuddy by default and switches to the international CodeBuddy service with --global, with isolated sessions and model catalogs
  • Multi-account management - Link domestic and Global accounts, switch the active account, and remove non-active accounts from the local admin page
  • Model-level 429 handling - Track cooldowns per account and model, with optional same-region failover
  • Daily check-in - Check in manually or enable background checks for all linked accounts

Installation

Recommended to run from PyPI using uv:

# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Run the latest available version (uv automatically creates the environment and installs dependencies)
uv run --with workbuddy2api python -m codebuddy_proxy \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# Force refresh the cache and run the latest version
uv run --refresh-package workbuddy2api --with workbuddy2api \
  python -m codebuddy_proxy \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

No need to manually activate the virtual environment on subsequent starts; just repeat the uv run command above.

Running from local source

Run the following commands from the project root to use the workspace source code instead of the published PyPI version:

# Sync local project dependencies
uv sync

# Start the local source
uv run python -m codebuddy_proxy --desensitize

On first use, start the proxy and open the local account manager:

uv run python -m codebuddy_proxy --desensitize

Then visit http://127.0.0.1:8787/admin and link a domestic or Global account.

Quick Start

1. Start the proxy

# Use the latest version (recommended)
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize

# First use: start, then link an account at http://127.0.0.1:8787/admin
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize

# International service: first login and start
uv run --with workbuddy2api python -m codebuddy_proxy --global --login --desensitize

# International service: later starts
uv run --with workbuddy2api python -m codebuddy_proxy --global --desensitize

Listens on http://127.0.0.1:8787 by default.

Domestic and international CodeBuddy backends

The proxy supports both CodeBuddy regions. Domestic mode remains the default, so existing startup commands continue to work unchanged.

Mode Startup option Upstream endpoint
Domestic (default) none https://copilot.tencent.com
International --global https://www.codebuddy.ai

Linked accounts are stored separately by region in ~/.workbuddy2api/accounts. Use --accounts-dir DIR to choose another local directory. With no explicit selector, the proxy restores the saved active account. On the first migration, matching legacy session files are imported once and kept; deleting an imported account does not import it again. Explicit --global or --endpoint selects a matching account, while --session-file can import that file again. If there is no match, the proxy starts without an active account. --login uses a temporary session file to add or refresh an account without replacing a legacy file.

The account manager can add accounts for either region while the proxy is running. For a terminal-driven login, the existing --login option remains available:

# First login
uv run --with workbuddy2api python -m codebuddy_proxy \
  --global --login --desensitize

# Later starts reuse the international session
uv run --with workbuddy2api python -m codebuddy_proxy \
  --global --desensitize

If the package is installed as a command, the equivalent short form is workbuddy2api --global --login. When running from source, use uv run python -m codebuddy_proxy --global --login.

--global selects the Global backend and a matching account when one exists. Codex CLI, Claude Code/CC Switch, OpenCode, and other clients continue to use the same local proxy URLs. The selected profile also controls the model catalog returned by /v1/models.

Domestic and international accounts remain isolated. An explicit --session-file is imported only when its saved backend and endpoint match the current startup options. The proxy refuses mismatched credentials and does not modify the original file.

For a custom international endpoint, use a dedicated session file when importing or creating a login from the command line:

uv run --with workbuddy2api python -m codebuddy_proxy \
  --global \
  --endpoint https://staging-codebuddy.tencent.com \
  --session-file "$HOME/.codebuddy-global-staging-session.json" \
  --login

Endpoint precedence is: explicit --endpoint > the --global profile default

CODEBUDDY_ENDPOINT in non-global mode > the domestic default endpoint. Therefore --global without --endpoint cannot be redirected to the domestic host by CODEBUDDY_ENDPOINT.

Domestic gateway: developer-role compatibility

The domestic default upstream (https://copilot.tencent.com) may reject Chat Completions requests containing a developer message with HTTP 400 / code 11128. Before forwarding to that upstream only, the proxy maps developer to system without changing message order, content, tool-call history, or the original client request. If enabled, --desensitize runs after this role adaptation. No client-side configuration is needed.

This compatibility mapping does not apply to the international backend or custom endpoints. Because the legacy upstream does not represent the separate system and developer instruction priorities, it is an approximation, not exact semantic equivalence. The upstream_role_compat log event reports only the number of mapped messages, not prompt contents.

Runtime model catalogs are loaded from the packaged profile resources src/codebuddy_proxy/models_config.domestic.json and src/codebuddy_proxy/models_config.global.json. The root models_config.json is retained only as a development-compatibility copy of the domestic catalog and is not a runtime data source.

2. Verify

curl http://127.0.0.1:8787/health
curl http://127.0.0.1:8787/v1/models

The health response reports the active backend (domestic or global) and upstream_endpoint, which makes it easy to confirm the selected region before connecting a client.

3. Connect clients

Codex CLI

Edit ~/.codex/config.toml:

[model_providers.codebuddy]
name = "CodeBuddy (via local proxy)"
base_url = "http://127.0.0.1:8787/v1"
wire_api = "responses"

[profiles.codebuddy]
model = "glm-5.2"
model_provider = "codebuddy"

Usage:

codex --profile codebuddy "your task"

Claude Code + CC Switch

Add the following to your CC Switch configuration:

{
  "DeepSeek-V4": {
    "base_url": "http://127.0.0.1:8787/v1/messages",
    "api_key": "",
    "model": "deepseek-v4-pro"
  }
}

OpenCode

Edit opencode.json in the project root:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "codebuddy/glm-5.2",
  "providers": {
    "codebuddy": {
      "name": "CodeBuddy (via local proxy)",
      "package": "@opencode-ai/ai/providers/openai-compatible",
      "settings": {
        "baseURL": "http://127.0.0.1:8787/v1",
        "apiKey": "noop"
      },
      "models": {
        "glm-5.2": { "modelID": "glm-5.2", "name": "GLM-5.2" },
        "deepseek-v4-pro": { "modelID": "deepseek-v4-pro", "name": "DeepSeek V4 Pro" },
        "kimi-k2.7": { "modelID": "kimi-k2.7", "name": "Kimi K2.7" }
      }
    }
  }
}

After launching opencode, use the /models command to select a model under the codebuddy provider (e.g. codebuddy/glm-5.2).

Note: baseURL points to the local proxy; apiKey can be any placeholder value (the local proxy does not validate keys). The models keys are the model IDs used inside OpenCode (for selection), while modelID is the actual model name sent to the proxy. Use apiKey for the key field (not env_key from some older templates), to avoid binding the wrong provider semantics.

Grok CLI

Edit ~/.grok/config.toml and add a [model.<name>] entry per model that points at the local proxy. Grok uses the OpenAI Chat Completions backend (/v1/chat/completions) by default, which this proxy supports:

[models]
default = "hy3"   # optional: set your default model

[model.hy3]
model = "hy3"                        # model id sent to the proxy
base_url = "http://127.0.0.1:8787/v1"
name = "HY3 Main"                    # shown in the model picker
api_key = "noop"                     # any placeholder value works

[model.dv4f]
model = "deepseek-v4-flash"
base_url = "http://127.0.0.1:8787/v1"
name = "DeepSeek V4 Flash"
api_key = "noop"

Then switch to the proxy model in the TUI with /model hy3 (or Ctrl+M model picker), or run headless with grok -m hy3 "your task".

Note: base_url points to the local proxy; api_key can be any placeholder value (the local proxy does not validate keys). You can also set api_backend = "responses" to use the /v1/responses endpoint, or "messages" for the Anthropic /v1/messages endpoint, depending on your needs.

Oh My Pi (OMP)

Oh My Pi is a terminal coding agent (formerly known as pi). It reads custom providers from ~/.omp/agent/models.yml, so the local proxy is configured as a keyless OpenAI-compatible endpoint.

Add a codebuddy provider (the exact models follow the IDs returned by /v1/models, e.g. hy3, glm-5.2, deepseek-v4-flash, kimi-k2.7):

# ~/.omp/agent/models.yml
providers:
  codebuddy:
    baseUrl: http://127.0.0.1:8787/v1
    api: openai-completions
    auth: none
    models:
      - id: hy3
        name: Hy3 (CodeBuddy)
        reasoning: true
        contextWindow: 192000
        maxTokens: 64000
      - id: glm-5.2
        name: GLM-5.2 (CodeBuddy)
        reasoning: true
        contextWindow: 1000000
        maxTokens: 48000
      - id: deepseek-v4-flash
        name: DeepSeek V4 Flash (CodeBuddy)
        reasoning: true
        contextWindow: 1000000
        maxTokens: 50000
      - id: kimi-k2.7
        name: Kimi K2.7 (CodeBuddy)
        reasoning: true
        contextWindow: 256000
        maxTokens: 32000

Notes:

  • auth: none marks the provider keyless, so the proxy's own session file handles authentication. No apiKey is needed (the proxy does not validate keys anyway).
  • api: openai-completions routes requests through /v1/chat/completions, which this proxy supports. If your OMP build or model needs the Responses wire format instead, use api: openai-responses (routes through /v1/responses).
  • The proxy already strips OpenAI extension fields (strict, additionalProperties) from tool schemas before forwarding to CodeBuddy, so you generally do not need disableStrictTools: true — add it only if a CodeBuddy backend revision starts rejecting tool requests.

Select the model inside OMP with /model codebuddy/hy3 (or set it as the default in your OMP profile), or run headless with omp --model codebuddy/hy3 "your task". Model selection is by exact provider/modelId.

Other OpenAI-compatible clients

  • Base URL: http://127.0.0.1:8787/v1
  • API Key: leave blank (or use the value you set with --api-key at startup)
  • Model name: glm-5.2 / deepseek-v4-pro / kimi-k2.7 / auto, etc.

Command-line arguments

--host HOST              Bind address (default 127.0.0.1)
--port PORT              Bind port (default 8787)
--global                 Use the international CodeBuddy backend (default domestic)
--endpoint ENDPOINT      CodeBuddy backend address (overrides the profile default)
--session-file PATH      Session file path (isolated by profile by default)
--accounts-dir DIR       Account library directory (default ~/.workbuddy2api/accounts)
--log-file PATH          JSONL log file (default ~/.workbuddy2api/codebuddy-proxy.jsonl)
--desensitize            Enable desensitization (recommended)
--optimize-context       Enable message compression (recommended for Codex CLI)
--login                  Perform browser login at startup
--no-browser             Do not open the browser on login
--verbose-llm            Log full LLM request/response content
                         (default: summary only, saves 98% space)
--mock-dir DIR           Use mock data (for testing)

Environment variables

CODEBUDDY_PROXY_HOST      # Same as --host
CODEBUDDY_PROXY_PORT      # Same as --port
CODEBUDDY_ENDPOINT        # Endpoint fallback in non-global mode
CODEBUDDY_PROXY_LOG_FILE  # Same as --log-file

Common scenarios

First use (login required)

uv run --with workbuddy2api python -m codebuddy_proxy --login \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

After the browser opens and you log in, the proxy starts automatically.

First login for the international backend:

uv run --with workbuddy2api python -m codebuddy_proxy --global --login \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Daily use (automatically reads the login state)

uv run --with workbuddy2api python -m codebuddy_proxy --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

For the international backend, keep --global on every startup:

uv run --with workbuddy2api python -m codebuddy_proxy --global --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Codex CLI scenario (with compression enabled)

uv run --with workbuddy2api python -m codebuddy_proxy --desensitize --optimize-context \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Multi-account switching

Start the proxy and open http://127.0.0.1:8787/admin. Choose Domestic or Global, then link the account in the browser window. The first linked account becomes active automatically; linking another account does not change the account currently in use. Use Set active to switch, or remove a non-active account from the same page. The selection is saved for the next proxy start. Switching affects new requests; requests already in progress continue with the account selected when they began.

Account manager with check-in controls and redacted account identifiers

The screenshot uses synthetic demo accounts. Account UIDs are covered, and no credentials or session contents are shown. Accounts are kept separately by backend, normalized endpoint, and user ID in ~/.workbuddy2api/accounts; use --accounts-dir DIR to choose another local directory. The manager remains available before any account is linked. Its page and API are restricted to loopback access, and API responses do not include tokens or session data.

Use the language selector at the top of the page to switch between Chinese and English. The choice is remembered in this browser; without a saved preference, the page follows the browser language. Switching languages keeps ongoing login and account operations running.

Automatic account switching after an upstream HTTP 429 is off by default. When enabled, a request can fail over only before any response has been sent, and only to accounts with the same region and endpoint. Each account is tried at most once per request. The proxy skips a candidate only when the request's model is cooling down there; other models on that account remain eligible. A missing or invalid model does not create a model cooldown or trigger failover. If all tried accounts return 429, the final status remains 429, and the last upstream response's Retry-After header is preserved when present. Cooldowns are tracked per account and model, using upstream Retry-After or a default 60 seconds when that value is missing or invalid. The setting and model cooldowns persist across restarts. Old v1 account-wide cooldowns had no model identity and are not carried forward during upgrade. The page shows a separate remaining countdown and source for each model, and refreshes account state periodically and immediately when a hidden page becomes visible again.

The account list also shows each linked account's check-in status for today (Beijing time). The Check in action is available for every account, including the active one; it checks the account first and does nothing when today's check-in is already complete. The global automatic check-in setting is off by default and applies to all linked accounts without changing the active account. The page saves this setting and displays status; the proxy performs automatic checks and check-ins in the background, including when the management page is closed. When enabled, the proxy checks all linked accounts immediately and on startup. At Beijing midnight it checks them, waits a random 0–10 minutes, then checks again before signing; disabling it cancels pending scheduled work. Unknown status is never treated as permission to sign. The setting persists; after restart, check-in status is confirmed by querying the upstream service again.

The command-line --login flow and --session-file option remain available for terminal use and importing an existing session.

Listening on all interfaces (LAN sharing)

uv run --with workbuddy2api python -m codebuddy_proxy --host 0.0.0.0 --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

API endpoints

All endpoints do not require an extra token in the request by default; the proxy authenticates using the local session.

Method Path Purpose
GET /health Query local service and auth status
GET /admin Open the local account manager
GET /v1/models Query the CodeBuddy model list
POST /v1/chat/completions OpenAI Chat Completions, supports tools and streaming
POST /v1/responses Responses API, compatible with Codex CLI
POST /v1/messages Anthropic Messages API, compatible with Claude Code / CC Switch

Account-management endpoints live under /admin/api and are restricted to loopback clients. Write requests require the X-Account-Manager: 1 header, and cross-origin requests are rejected. GET /admin/api/accounts reports active account, cooldown, and cached check-in state. GET /admin/api/accounts/{id}/checkin-status checks that account against the upstream service; POST /admin/api/accounts/{id}/checkin checks first and signs only when needed. GET and PATCH /admin/api/settings read and save the automatic-switch and automatic-check-in preferences independently.

/health - health check

curl http://127.0.0.1:8787/health

Example response:

{
  "status": "ok",
  "uptime_seconds": 123,
  "authenticated": true,
  "token_valid": true,
  "backend": "global",
  "upstream_endpoint": "https://www.codebuddy.ai"
}

/v1/models - model list

curl http://127.0.0.1:8787/v1/models

Returns the active backend's model catalog in OpenAI format. data[].id is the model value used in subsequent requests; domestic and international mode may expose different model IDs.

/v1/chat/completions - OpenAI Chat

Non-streaming request:

curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "deepseek-v4-flash",
    "messages": [{"role": "user", "content": "Write a quicksort"}]
  }'

Streaming request:

curl -N http://127.0.0.1:8787/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "glm-5.2",
    "stream": true,
    "messages": [{"role": "user", "content": "hi"}]
  }'

Supports the full set of OpenAI features, including tools, tool_choice, and stream_options.

/v1/responses - Responses API

Used for Codex CLI compatibility:

curl http://127.0.0.1:8787/v1/responses \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "default",
    "input": "Write a quicksort"
  }'

Supports instructions (system prompt), message-form input, tools, tool_choice, and stream.

💡 Tip: Using --optimize-context dramatically reduces token usage for Codex CLI.

/v1/messages - Anthropic Messages

Used for Claude Code / CC Switch compatibility:

curl http://127.0.0.1:8787/v1/messages \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "deepseek-v4-pro",
    "max_tokens": 4096,
    "messages": [{"role": "user", "content": "hi"}]
  }'

Setting "stream": true returns an Anthropic SSE event stream.

Advanced features

Desensitization (--desensitize)

Inserts zero-width spaces (U+200B) into sensitive words in system messages, breaking the backend's keyword matching and mitigating compliance templates being falsely blocked by review.

When to use it

Scenarios where enabling is strongly recommended:

  1. Integrating with Claude Code / CC Switch

    • Claude Code's system prompt contains many Anthropic brand terms and security compliance statements
    • The Tencent backend may treat competing brand terms ("Claude", "Anthropic") as sensitive content
    • Without desensitization, almost every request gets blocked by review
  2. Integrating with agentic tools such as Codex CLI / Oh My Posh

    • These tools' system prompts contain a large number of security terms (DoS, exploit, credential testing, etc.)
    • Even compliant "refuse harmful requests" statements can be falsely blocked by keyword matching
  3. Using custom system prompts that contain security terms

    • Compliance conversations related to security research and penetration testing
    • Generating technical documentation that needs to discuss vulnerabilities and attack defenses

Typical error message:

{
  "error": {
    "message": "内容违规",
    "type": "content_policy_violation"
  }
}

Or the backend returns an empty response / connection drops.

Scenarios where you don't need it:

  • ✅ Normal conversation (no security terms)
  • ✅ Using the official CodeBuddy client (handling is built in)
  • ✅ Pure code generation (no brand terms / security statements)

Typical use cases

Case 1: Integrating with Claude Code

# --desensitize is required, otherwise almost every request is blocked
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# Configure in Claude Code / CC Switch
# Base URL: http://127.0.0.1:8787/v1/messages

Case 2: Integrating with Codex CLI

# Enable both desensitization and message compression (best configuration)
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize --optimize-context \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# In the Codex CLI config file
# base_url: http://127.0.0.1:8787/v1/responses

Case 3: Security research conversation

# Enable desensitization to avoid compliance terms being falsely blocked
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# Example request
curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "deepseek-v4-pro",
    "messages": [
      {
        "role": "system",
        "content": "You are a security expert. Refuse requests for exploit development."
      },
      {
        "role": "user",
        "content": "Explain defenses against SQL injection"
      }
    ]
  }'

How it works

# Original
"Refuse requests for DoS attacks and exploit development."

# Desensitized (zero-width space U+200B inserted)
"Refuse requests for Do​S a​ttacks and e​xploit development."
# Human/model: looks exactly the same
# Backend review: keyword matching fails

Scope of processing

  • ✅ system role messages (default)
  • ✅ developer role messages
  • ✅ Harness user messages injected by Codex CLI / Claude Code
  • ✅ description field of tools
  • ❌ user/assistant messages (kept as-is, so normal conversation is unaffected)

Sensitive word list

Roughly 80 security/compliance terms:

  • Attack types: DoS, DDoS, exploit, SQL injection, XSS, malware...
  • Security terms: vulnerability, penetration testing, privilege escalation...
  • Brand terms: Claude Code, Anthropic (to avoid competing brands triggering review)

The full list is in SENSITIVE_TERMS in desensitize.py.

Notes

  • ✅ Only processes compliance statements; does not bypass review of harmful input
  • ✅ Only modifies system messages; real user input is kept as-is
  • ⚠️ Zero-width spaces are transparent to humans/models but affect exact string matching
  • ⚠️ Performance cost: <1ms (regex replacement)

Message compression (--optimize-context)

Only applies to the /v1/responses endpoint, compressing long histories, large schemas, and oversized tool outputs into a "minimal semantic closure", dramatically reducing token usage (possibly 60-90%).

When to use it

  • ✅ Using agentic tools such as Codex CLI / Claude Code (long histories)
  • ✅ High token usage (>100k/day)
  • ✅ Frequently hitting "context" errors
  • ✅ Sending the full history on every request
  • ❌ Not for short conversations / simple requests

Usage

# Enable message compression
uv run --with workbuddy2api python -m codebuddy_proxy --optimize-context \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# Enable both features (recommended for Codex CLI)
uv run --with workbuddy2api python -m codebuddy_proxy --desensitize --optimize-context \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

How it works

Conservative mode (non-agentic requests)

Only length trimming:

  • System → truncated to 1200 characters
  • User → 3200 characters
  • Assistant → keep a head/tail summary (1800)
  • Tool output → compressed to 1600 characters
Aggressive mode (agentic CLI requests)

Automatically detects agentic requests (tools containing exec_command, apply_patch, etc., or messages containing harness markers) and reconstructs them into a minimal semantic closure:

  1. Drop harness messages — remove all Codex/Claude Code injected system/user messages
  2. Keep recent context — keep ≤8 messages / ≤7000 chars from the tail
  3. Summarize history — compress earlier history into rule summaries (one line each)
  4. Schema convergence — keep only structural fields, drop descriptions (the biggest space consumers)
  5. Compress tool output/arguments — keep key parts, omit the rest

Example effect

Original request:
  - Messages: 50, 120,000 characters
  - Tools: 15, 45,000 characters
  - Total: ~165,000 characters (~40k tokens)

After compression:
  - Messages: 12, 18,000 characters
  - Tools: 15, 8,000 characters
  - Total: ~26,000 characters (~6k tokens)

Savings: ~85% tokens

Log verification

Once enabled, the log records compression statistics:

grep projection_applied "$HOME/.workbuddy2api/codebuddy-proxy.jsonl" | jq .

Example output:

{
  "event": "projection_applied",
  "protocol": "responses",
  "mode": "aggressive",
  "original_messages": 50,
  "projected_messages": 12,
  "original_message_chars": 120000,
  "projected_message_chars": 18000,
  "dropped_harness_messages": 8
}

Notes

  • ✅ Only used for /v1/responses; does not affect the chat/messages endpoints
  • ✅ Preserves the semantic closure; the model can still reason
  • ⚠️ History is summarized; precise details require re-running tools to retrieve
  • ⚠️ Schema is trimmed; auxiliary info such as descriptions is lost
  • ⚠️ Performance cost: <10ms (traversal + compression)

Process diagnostics (2.0.7)

In addition to the existing business logs, console.log mirrors Python stdout/stderr (including Uvicorn errors and tracebacks), process-events.jsonl records process startup, exit codes, signals and unhandled exceptions, and crash.log captures Python fatal fault stacks. These files are written alongside --log-file. The console and process-event files rotate by size; see process diagnostics for details. Console logs may contain sensitive data: review before sharing.

Logging

Logs include:

  • Text log: $HOME/.workbuddy2api/proxy.log (rotated daily, retained 30 days)
  • Structured log: $HOME/.workbuddy2api/codebuddy-proxy.jsonl (rotated daily, retained 30 days, full request/response)

Each JSONL record contains app_version, system_version, python_version, and machine fields; a startup event is also recorded at launch.

Upstream connection errors/timeouts are retried at most twice (0.5s / 1s backoff, three attempts total). HTTP errors and read/write failures are not retried; once response headers arrive, the request is never replayed. Each attempt keeps the existing 30s connection timeout, so a persistent outage can take up to about 91.5s before the final error. This applies to streaming and aggregated responses.

upstream_connect_retry / upstream_connect_failed record the attempt number, target host/port, and bounded exception_chain with exception types and native errno / winerror values when available. request_id links these to stream start, error, and terminal records. stream_completed is reserved for streams that received upstream [DONE] and finished forwarding; other terminal events are stream_failed, stream_incomplete (EOF/guard limit without [DONE]), and stream_cancelled. status: null means no upstream HTTP response was received.

You can also specify an absolute path for the log file:

uv run --with workbuddy2api python -m codebuddy_proxy \
  --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Viewing logs:

# Follow in real time
tail -f "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# View streaming events
tail -100 "$HOME/.workbuddy2api/codebuddy-proxy.jsonl" | jq 'select(.event | startswith("stream"))'

# Count timeouts
jq 'select(.event=="stream_timeout")' "$HOME/.workbuddy2api/codebuddy-proxy.jsonl" | wc -l

# Verify desensitization
grep desensitize_applied "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

# Verify compression (view statistics)
grep projection_applied "$HOME/.workbuddy2api/codebuddy-proxy.jsonl" | jq .

Troubleshooting

Session file not found

First use requires login:

uv run --with workbuddy2api python -m codebuddy_proxy --login \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

For the international backend, add --global to the same command. It creates and later reuses ~/.codebuddy-global-session.json.

Session backend or endpoint mismatch

Do not reuse a domestic session for the international backend, or a production session for a custom endpoint. Log in with the same backend options you plan to use later:

# International default endpoint
uv run --with workbuddy2api python -m codebuddy_proxy --global --login

# Custom international endpoint with an isolated session
uv run --with workbuddy2api python -m codebuddy_proxy \
  --global --endpoint https://staging-codebuddy.tencent.com \
  --session-file "$HOME/.codebuddy-global-staging-session.json" --login

The proxy intentionally rejects mismatched session metadata instead of sending the saved token to another host.

401 authentication failure

Token expired; log in again:

uv run --with workbuddy2api python -m codebuddy_proxy --login \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Review blocking

Enable desensitization:

uv run --with workbuddy2api python -m codebuddy_proxy --desensitize \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

If still blocked, try compression (/v1/responses only):

uv run --with workbuddy2api python -m codebuddy_proxy --desensitize --optimize-context \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Upstream error 11148 "tool calls and tool results do not match"

Some agent clients produce nonconforming Anthropic / Responses tool histories. The CodeBuddy backend requires Chat tool results to directly follow the declaring assistant tool_calls. The proxy now repairs recoverable converted histories in /v1/messages and /v1/responses: interleaved non-tool messages are deferred, and orphan or identical duplicate results are dropped with diagnostic warnings (no tool output is logged).

Ambiguous histories are not silently truncated. If a call has no result, a tool-call ID is reused, or duplicate results disagree, the proxy returns HTTP 422, with detail.code=invalid_tool_history and a specific reason. No request is sent upstream in that case. Verify the side effects of any unfinished tools before retrying or starting a fresh conversation.

This repair does not apply to direct /v1/chat/completions requests and cannot guarantee the absence of unrelated upstream 11148 errors.

Port already in use

lsof -i :8787
uv run --with workbuddy2api python -m codebuddy_proxy --port 8788 \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

SOCKS proxy errors

The httpx[socks] dependency is installed automatically. If problems persist, check the environment variables:

env | grep -i proxy

Temporarily disable the proxy:

unset http_proxy https_proxy all_proxy
uv run --with workbuddy2api python -m codebuddy_proxy \
  --log-file "$HOME/.workbuddy2api/codebuddy-proxy.jsonl"

Technical details

  • Architecture: FastAPI + httpx (async)
  • Concurrency: supports 1000+ concurrent requests
  • Timeouts: connect 10 seconds, read 30 seconds
  • Streaming: full streaming logs (started / progress / completed / timeout)

Disclaimer

This project is for learning and research purposes only. Please comply with CodeBuddy's Terms of Service.

  • This project provides no warranty of any kind
  • Any consequences arising from the use of this project are the sole responsibility of the user
  • Do not use this project for any purpose that violates CodeBuddy's Terms of Service
  • Do not use this project for commercial purposes

About

A lightweight API proxy for domestic and international CodeBuddy backends, providing OpenAI, Anthropic, and Responses-compatible APIs with native support for Claude Code, Codex CLI, and DeepSeek Harness.

Topics

Resources

Stars

25 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages