Skip to content

feat(proxy): serve the Anthropic Messages API#170

Open
OnlyTerp wants to merge 6 commits into
sybil-solutions:mainfrom
OnlyTerp:terp/anthropic-messages-api
Open

feat(proxy): serve the Anthropic Messages API#170
OnlyTerp wants to merge 6 commits into
sybil-solutions:mainfrom
OnlyTerp:terp/anthropic-messages-api

Conversation

@OnlyTerp

@OnlyTerp OnlyTerp commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes Local Studio a native ANTHROPIC_BASE_URL target — the integration llama.cpp (Jan 2026), Ollama, and LM Studio all shipped this year because Claude Code and other Anthropic-wire-protocol agents became the default way people use local models for real work.

  • POST /v1/messages — translates Anthropic requests to OpenAI internally (llama.cpp's approach) and forwards to the running backend, so it works for every engine. Handles: system (string or blocks), text/image(base64+url)/tool_use/tool_result content blocks, tools + tool_choice (auto/any/tool), stop_sequences, temperature/top_p.
  • Streaming — full Anthropic SSE event sequence (message_startcontent_block_start/delta/stopmessage_deltamessage_stop) translated from OpenAI chunks by a pure state machine, including input_json_delta for streamed tool calls and text→tool block transitions. 15s ping keepalive (same Cloudflare-prefill rationale as the OpenAI path).
  • POST /v1/messages/count_tokens — flattens the request and tokenizes upstream, with a fallback chain (/tokenize → TabbyAPI's /v1/token/encode).
  • Observability for free: requests through /v1/messages are recorded by the existing inference accounting — per-model requests, tokens, TTFT show up in the usage page. None of the other local Anthropic endpoints have this.
  • Errors are Anthropic-shaped ({type:"error",error:{...}}), and the model-not-running guard behaves exactly like the OpenAI route (proxy never launches models).

Live validation — RTX 5090, TabbyAPI/ExLlamaV3 backend (branch of #168)

$ ANTHROPIC_BASE_URL=http://127.0.0.1:8188 ANTHROPIC_AUTH_TOKEN=local-studio \
    claude -p "Reply with exactly: LOCAL STUDIO OK" --model llama32-1b-exl3
"LOCAL STUDIO OK"        # real Claude Code v2.1.179, end to end
  • Non-streaming /v1/messages: correct message shape, stop_reason mapping
  • Streaming: verified full event sequence; message_delta carried real usage ({"input_tokens":39,"output_tokens":19})
  • count_tokens: {"input_tokens":27} via the TabbyAPI fallback
  • Usage page after the Claude Code turn: llama32-1b-exl3 | requests: 3 | tokens: 27171 | avg TTFT 931ms

Test plan

  • 10 unit tests: request translation (system/blocks/tools/tool_choice/images/tool_result splitting), response translation (text + tool_use + stop reasons), stream state machine (text stream, text→tool transition, usage propagation), SSE line parsing, prompt flattening
  • bun run typecheck, node scripts/validate-shared-contracts.mjs
  • Full pre-push check:quality gate
  • Live Claude Code session against the 5090 (above)

Note: fresh-clone npm ci (without --legacy-peer-deps) currently fails on main — the frontend lockfile looks out of sync with the pinned deps; npm install resolves. Happy to PR the regenerated lockfile separately if wanted.

@OnlyTerp OnlyTerp requested a review from 0xSero as a code owner July 2, 2026 02:41
@0xSero

0xSero commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Solid translation layer — clean state machine, real tests, and the live Claude Code validation is convincing. One blocker: main inlined services/inference-client into http/local-fetch.ts (3c146e9), so this compiles against a stale import path — GitHub reports it mergeable but typecheck fails post-merge; please rebase and update the import. Two asks with the rebase: pass through reasoning_content as Anthropic thinking blocks (or explicitly note the drop) — local reasoning models otherwise lose thinking output that the OpenAI route preserves via reasoning.ts — and consider mirroring the OpenAI route's strict-model 404 for unmatched models instead of forwarding upstream. With those this is ready.

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.

2 participants