Skip to content

feat(cc-bridge): native Anthropic Messages adapter for Claude Code - #45

Open
chaoshou-coder wants to merge 2 commits into
basecompute:mainfrom
chaoshou-coder:feat/anthropic-messages-adapter
Open

feat(cc-bridge): native Anthropic Messages adapter for Claude Code#45
chaoshou-coder wants to merge 2 commits into
basecompute:mainfrom
chaoshou-coder:feat/anthropic-messages-adapter

Conversation

@chaoshou-coder

Copy link
Copy Markdown

PR description — feat/anthropic-messages-adapter → baseRT

What / Why

Adds cc-bridge: a native Anthropic Messages API adapter that lets Claude
Code talk to a local basert serve directly (/v1/messages
/v1/chat/completions), with native SSE streaming (thinking / text / tool_use
blocks), keep-alive pings, and cancellation propagation.

The protocol is generated natively instead of via LiteLLM, because LiteLLM was
measured to fail in two compounding ways (see
docs/proposals/native-anthropic-adapter.md and TECHNICAL.md §1):

  1. LiteLLM's openai→anthropic non-streaming conversion always returns empty
    content (use_chat_completions_api: true drops content when provider returns reasoning_content BerriAI/litellm#27492, open).
  2. Claude Code drops a streaming connection after ~36s without a first byte
    and retries non-streaming — straight into bug 1. baseRT's ~35s prefill of a
    23.5k-token prompt triggers this on every heavy request.

Security posture

  • --master-key is required: every inbound /v1/messages and
    count_tokens request must present it (x-api-key or Authorization: Bearer), otherwise rejected with an Anthropic-style 401 before the body
    is parsed.
  • Binds to 127.0.0.1 by default; 0.0.0.0 requires an explicit --host.
  • The harm-classifier short-circuit (--allow-classifier-bypass) is off by
    default
    and only matches a narrow heuristic (single user message, no
    tools, non-streaming, non-local model name). Disabling Claude Code's
    online harm detection is an operator decision, documented in TECHNICAL.md
    §3.7.
  • Debug dumping (--debug-dir) is off by default.
  • Token accounting is honest: /v1/messages/count_tokens and streaming
    message_start usage report an upper-bound estimate, never a fabricated 0.

What is cc-bridge?

A standalone deployment component, explicitly part of the baseRT ecosystem
(own directory, own venv, own tests). The engine-side gaps it works around are
tracked as upstream issues, not papered over in this PR:

Why issues and PR are opened together

The gaps this adapter works around (external chat templates, per-request
thinking control, single-slot queueing) are product requirements that belong
in the engine, not workarounds frozen into a bridge. Opening issues #42#44
side-by-side with this PR is deliberate: the adapter is an honest bridge
today, and each issue tracks the engine change that would let the
corresponding workaround be removed. I would rather file a requirement and
ship the piece I can build than file a requirement alone — and I am happy to
do the engine-side work too, if maintainers point me at the right files,
knowing it will take me longer than it would take you.

Tests

cc-bridge/tests/ — 38 pytest cases, all green locally:

  • request conversion golden cases (system merging, thinking, tools,
    tool_result ordering, sampling passthrough)
  • authentication (pure function + endpoint-level 401s, auth-before-parse)
  • token estimation (non-negative, monotonic, CJK weighting)
  • SSE lifecycle end-to-end through a fake upstream (normal stream, empty
    stream via [DONE] and abrupt close, truncation → max_tokens,
    classifier-bypass gating)

Run: cd cc-bridge && python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt && .venv/bin/python -m pytest

Note: baseRT's CI is C++/Rust only; we deliberately did not add a Python job
in this PR. If the maintainers want it covered in CI, we'll add a job in a
follow-up.

Intentional deviations from the review

A note from the contributor

This is my first open-source contribution, and I want to be upfront about
what kind of contributor I am. I do not know C or Rust, and my Python is
basic. What I can reliably offer is careful work on the parts that do not
need deep engine knowledge: integration glue like this adapter, tests,
triage, and documentation — I plan to follow up with a Chinese translation of
the website and repository docs, and more issues and PRs after this one. I
have the time to keep contributing, and I would rather earn my place here as
someone who ships working, reviewed code than as someone who only files
requests. Any process or quality feedback is genuinely welcome — I expect to
make beginner mistakes, and I will learn from them.

Thank you for building baseRT for Apple Silicon users. That is why I am here.

Real-session evidence

A full multi-turn Claude Code tool-use session ran against this branch on
2026-08-12 (01:21–01:34, 29 requests, ~13 minutes): WebSearch-style tools,
tool_calls/tool_result loops with context growing from 22.4k to 26.0k
prompt tokens, streaming and non-streaming paths, and one client disconnect
at 01:34:19 that baseRT logged as [disconnect] client closed connection; aborted stream after 1 token(s) — the adapter's abort propagation working
end-to-end.

The complete request/response records (raw /v1/messages bodies and exact
SSE streams, plus the matching basert serve log) are packaged as
cc-bridge-tool-run-logs.tar.gz (560 KB) — I will attach it in a comment to
this PR. Representative basert log lines:

[01:22:56] <-- 200 Qwen-AgentWorld-35B-A3B prompt=22520 completion=1460 prefill=716 t/s decode=76 t/s 50751ms tool_calls
[01:25:33] <-- 200 Qwen-AgentWorld-35B-A3B prompt=24346 completion=209 prefill=662 t/s decode=75 t/s 39566ms tool_calls
[01:31:19] <-- 200 Qwen-AgentWorld-35B-A3B prompt=25950 completion=1098 prefill=602 t/s decode=70 t/s 58904ms stop
[01:34:19] --> POST /v1/chat/completions model=Qwen-AgentWorld-35B-A3B stream max_tokens=32000
[disconnect] client closed connection; aborted stream after 1 token(s) (model=Qwen-AgentWorld-35B-A3B)

Changelog

CHANGELOG.md gets an [Unreleased] entry; the root README gains a cc-bridge
section.

Thin protocol bridge letting Claude Code talk to a local baseRT server:
/v1/messages <-> /v1/chat/completions with native SSE streaming, thinking
and tool blocks, keep-alive pings, and cancellation propagation. Inbound
requests require a master key; binds to 127.0.0.1 by default; the
classifier-bypass short-circuit is off unless explicitly enabled.
Includes pytest unit and end-to-end tests.
English docs for the adapter: setup and flags (README.md), protocol and
security details (TECHNICAL.md), a Claude Code client guide, a
chat-template analysis with a vendored froggeric v21.3 reference (MIT),
and a root README/CHANGELOG entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant