Skip to content

v0.6.0 — point any SDK at localhost

Choose a tag to compare

@fire17 fire17 released this 06 Aug 22:50
· 129 commits to main since this release

apiplan serve runs a local server that speaks OpenAI's and Anthropic's wire shapes exactly. Change one base URL and existing code answers from the subscriptions you already pay for — no API key, no per-token bill. Still zero dependencies.

apiplan serve                 # http://127.0.0.1:8787
from openai import OpenAI
client = OpenAI(api_key="not-needed", base_url="http://127.0.0.1:8787/v1")
client.chat.completions.create(model="opus", messages=[...])   # Claude, in OpenAI's shape
export OPENAI_BASE_URL=http://127.0.0.1:8787/v1
export ANTHROPIC_BASE_URL=http://127.0.0.1:8787

The dialect and the backend are independent

The path decides the response shape. The model field decides who answers. So /v1/chat/completions with model: "opus" returns Claude in OpenAI's format, and /v1/messages with model: "sol" returns GPT in Anthropic's.

That is the point: most tooling speaks exactly one dialect, and this makes every model reachable from all of it.

endpoint shape
POST /v1/chat/completions OpenAI chat, streaming and not
POST /v1/messages Anthropic messages, streaming and not
POST /v1/audio/speech OpenAI speech — instructions steers delivery, like tts --as
POST /v1/images/generations OpenAI images, b64_json
GET /v1/models either shape; the caller's auth header picks which

Errors come back in the caller's own envelope, so both SDKs parse failures correctly.

Verified with the real SDKs

Not curl — the official openai and @anthropic-ai/sdk packages, installed fresh: both dialects, both directions (Claude through OpenAI's SDK, GPT through Anthropic's), streaming on both, plus audio.speech.create and images.generate.

Two bugs this surfaced, both fixed at the root

  • Anthropic replied with an empty string. build() leaves stream to the caller and the CLI sets it at call time; without it Anthropic returns a plain JSON body, which the SSE reader parsed as zero events. Silent, not an error.
  • --max-tokens was broken on every OpenAI model, and had been. The codex backend rejects max_output_tokens outright (400). Rarely hit from the CLI; every API client sets it by default, so the server hit it immediately. The parameter is no longer sent, and the CLI now says the flag is ignored there rather than dropping it quietly.

Safety

Binds 127.0.0.1 only — it hands out your subscription to anything that can reach it. Set APIPLAN_API_KEY to require a key (enforced on Authorization and x-api-key); --port / --host to move it.

134 tests · 7/7 budgets · CI green on ubuntu, macos and windows.