{"id":50728292,"url":"https://github.com/1mb-dev/shim","last_synced_at":"2026-06-10T06:02:42.972Z","repository":{"id":361573934,"uuid":"1248947908","full_name":"1mb-dev/shim","owner":"1mb-dev","description":"HTTP proxy: run Claude Code against OpenAI-compatible providers (DeepSeek/OpenAI/OpenRouter/Ollama) or pass through to Anthropic, with built-in request measurement.","archived":false,"fork":false,"pushed_at":"2026-05-31T08:57:41.000Z","size":638,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T10:18:36.671Z","etag":null,"topics":["anthropic","claude-code","deepseek","go","llm-proxy","ollama","openai","openrouter","prometheus"],"latest_commit_sha":null,"homepage":"https://1mb-dev.github.io/shim/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1mb-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T07:46:18.000Z","updated_at":"2026-05-31T08:57:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/1mb-dev/shim","commit_stats":null,"previous_names":["1mb-dev/shim"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/1mb-dev/shim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1mb-dev%2Fshim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1mb-dev%2Fshim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1mb-dev%2Fshim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1mb-dev%2Fshim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1mb-dev","download_url":"https://codeload.github.com/1mb-dev/shim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1mb-dev%2Fshim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34139185,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude-code","deepseek","go","llm-proxy","ollama","openai","openrouter","prometheus"],"created_at":"2026-06-10T06:02:41.985Z","updated_at":"2026-06-10T06:02:42.966Z","avatar_url":"https://github.com/1mb-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shim\n\nAn HTTP proxy that puts the Anthropic Messages API in front of OpenAI-compatible\nmodel providers, and measures every request it forwards. Point Claude Code at\nshim via `ANTHROPIC_BASE_URL`; its Messages-API calls are translated to OpenAI\nChatCompletions and routed to your configured upstream.\n\nshim is a measurement layer for Claude Code's upstream, not a way to get cheaper\ntokens. `/v1/metrics` reports per-request latency and token drift, every request\nshim rewrites in flight is logged, and `/v1/messages/explain` returns the exact\nupstream body shim would send before a request leaves the box.\n\nThe OpenAI-dialect providers (**deepseek**, **openai**, **openrouter**,\n**ollama**) are data rows in one preset registry: base URL, per-role model map,\nauth flag. Adding another is a row, not a file. **anthropic-passthrough** is the\nother transport, a transparent proxy to a native Anthropic-Messages endpoint with\nno translation. Select via `ADAPTER`.\n\nSingle static binary, stdlib-leaning, one runtime dependency (`pkoukk/tiktoken-go`;\ncl100k_base BPE tables embedded at compile time, no network fetch at startup).\nSee [Dependencies](#dependencies).\n\nA per-adapter translator carries two transport dialects: OpenAI ChatCompletions\n(the preset family) and identity (passthrough). \"What works\" is what's wired;\nanything under \"What doesn't\" returns a clear error rather than silently\nmisbehaving.\n\n## When NOT to use shim\n\nIf you only need DeepSeek and don't care about measurement, **skip shim\nentirely.** Per [DeepSeek's official Claude Code integration\nguide](https://api-docs.deepseek.com/quick_start/agent_integrations/claude_code),\nDeepSeek now serves a native Anthropic Messages API at\n`https://api.deepseek.com/anthropic`. Point Claude Code at it directly:\n\n```sh\nexport ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic\nexport ANTHROPIC_AUTH_TOKEN=\u003cyour DeepSeek API key\u003e\n```\n\nNo proxy needed.\n\n## When shim adds value\n\n- **Honest measurement.** `GET /v1/metrics` surfaces per-endpoint latency\n  (p50/p95/p99), the gap between shim's cl100k_base BPE count and the\n  upstream's claimed count, and a running tally of every request shim\n  rewrote in flight. See [Measurement](#measurement).\n- **Loud-fail visibility on heuristic drift.** When shim modifies your\n  traffic — model name rewrite, `stop_sequences` truncation past OpenAI's\n  cap of 4, etc. — it logs the event and increments a counter in\n  `/v1/metrics`. Silent forwarding of modified requests is a bug.\n- **Transparent observability in front of real Anthropic.** The\n  anthropic-passthrough adapter forwards requests and responses verbatim to a\n  native Anthropic endpoint — zero translation risk — so you get shim's\n  redacted logs, `/v1/metrics`, and loud-fail in front of Claude itself. See\n  [Transparent passthrough](#transparent-passthrough).\n- **Multi-provider routing.** Four OpenAI-dialect providers (deepseek, openai,\n  openrouter, ollama) ride one translator and one measurement layer, as data\n  rows in `internal/adapter/openaichat`. Adding the next is a row — base URL,\n  per-role model map, auth flag — not a new file.\n\n## Quick start\n\n```sh\nbrew install 1mb-dev/tap/shim         # or: go install github.com/1mb-dev/shim/cmd/shim@latest\nexport UPSTREAM_API_KEY=\u003cdeepseek key\u003e  # ADAPTER=deepseek by default; see Config for others\nshim \u0026                                # serves 127.0.0.1:8082\nANTHROPIC_BASE_URL=http://127.0.0.1:8082 ANTHROPIC_API_KEY=shim claude\n```\n\nThen watch what it did: `curl -s localhost:8082/v1/metrics | python3 -m json.tool`.\n\n---\n\n## What works\n\n- `POST /v1/messages` — Anthropic Messages API, non-streaming and streaming. `{\"stream\": true}` returns the canonical Anthropic SSE sequence (`message_start` → `content_block_start` → `content_block_delta` → `content_block_stop` → `message_delta` → `message_stop`). The translating presets buffer the upstream then emit that sequence in one burst — correct protocol, no per-token latency yet; passthrough streams live. See [streaming caveat](#what-doesnt-yet).\n- `POST /v1/messages/count_tokens` — cl100k_base BPE count (see [Measurement](#measurement)).\n- `POST /v1/messages/explain` — dry-run: returns the upstream request shim *would* send + every mutation it would apply (model rewrite, stop-sequence cap), **without calling the upstream**. The tangible \"loud-fail on drift\" view; reuses the real translation path. See [Measurement](#measurement).\n- `GET /v1/metrics` — JSON snapshot: per-endpoint latency p50/p95/p99, shim-vs-upstream token-delta totals, rewrite-event counts. See [Measurement](#measurement).\n- `GET /metrics` — the same signals in Prometheus text-exposition format (scrapeable). See [Measurement](#measurement).\n- `GET /health` (and the alias `/healthz`) — `{\"status\":\"ok\"}` (liveness); `GET /readyz` — `{\"status\":\"ready\"}` (readiness).\n- Translation: system blocks, user/assistant text, image blocks (base64 + URL), `stop_sequences` (capped at 4 per OpenAI's limit; over-cap requests are truncated and a `warn` log line emitted), `tools[]`, all `tool_choice` variants, `tool_use ↔ tool_result` roundtrip.\n- Thinking / `reasoning_content` roundtrip. The `thinking` request field passes through to the upstream; an upstream `reasoning_content` response becomes an Anthropic thinking block (`signature: \"shim-passthrough-v1\"`, constant, not verified on roundtrip), and thinking blocks echoed back on continuations translate back to `reasoning_content`. Thinking precedes tool_use in assistant turns; multiple thinking blocks concatenate into one `reasoning_content`. Live only for upstreams that surface reasoning (DeepSeek does; OpenAI hides it, so thinking blocks are a no-op there, not a bug). Rationale in [Thinking-block signatures](#thinking-block-signatures).\n- Adapters: the OpenAI-dialect **preset registry** (`deepseek` `https://api.deepseek.com/v1`, `openai` `https://api.openai.com/v1`, `openrouter` `https://openrouter.ai/api/v1`, `ollama` `http://localhost:11434/v1` — all translate) and **anthropic-passthrough** (`https://api.anthropic.com`, native Anthropic Messages — forwards verbatim). Ollama runs keyless; the rest need `UPSTREAM_API_KEY`. Select via `ADAPTER`; per-preset model maps in [`.env.example`](.env.example). See [Transparent passthrough](#transparent-passthrough).\n- Upstream response headers forwarded on an allowlist: `request-id`, `retry-after`, and the `anthropic-ratelimit-*` family (so clients can trace requests and back off). Content-framing and hop-by-hop headers are never forwarded — shim sets those itself.\n- Model mapping: Claude Code sends `claude-opus*`/`claude-sonnet*`/`claude-haiku*`; each preset maps the three roles to its own upstream models (e.g. deepseek routes opus → `deepseek-v4-pro`, sonnet/haiku → `deepseek-v4-flash`; the full per-preset table is in [`.env.example`](.env.example)). Precedence per role: `UPSTREAM_{OPUS,SONNET,HAIKU}_MODEL` env override \u003e preset role default \u003e `UPSTREAM_MODEL` catch-all (only for presets with no role default, e.g. ollama) \u003e preset default. The bare/`\u003crole\u003e-` hyphen anchor is deliberate — `claude-opus` and `claude-opus-4-8` both match opus, but `claude-opusxxx` must not. Non-claude names pass through unless `UPSTREAM_MODEL` is set. Every rewrite logs `info` and increments `rewrites.model` in `/v1/metrics`. Preset model IDs are verified-current (2026-05) but drift with vendor releases — override via the env vars above.\n- `shim run [args...]` launcher: locates `claude` on PATH, injects `ANTHROPIC_BASE_URL` + `ANTHROPIC_API_KEY=shim`, execs it, propagates exit code. Tested end-to-end with `claude --bare -p`.\n- Redacted-by-default JSON logs via `log/slog`. `Authorization`, prompt/message content, URL query strings, and credential-shaped keys are scrubbed at log-write time.\n- Cross-compiled binaries: `darwin/arm64`, `linux/amd64`, `linux/arm64`.\n\n## What doesn't (yet)\n\nThese all return a clear error — never silent forwarding.\n\n- **`thinking: {display: \"omitted\"}` / `redacted_thinking` blocks.** Anthropic supports a \"show me the signature but redact the content\" mode for thinking blocks. shim doesn't — there's no stateless path to reproduce a signature for absent content. Defer until a real user behind the feature exists.\n- **Live per-token streaming on the *translating* presets.** By design, shim drives these upstreams non-streaming and emits the canonical Anthropic SSE in one burst — correct protocol and event ordering, no per-token latency. Real per-token streaming is the headline post-1.0 enhancement and lands as a non-breaking change (the SSE event shape is unchanged). Passthrough already streams live.\n- **Prompt caching markers.** Not translated (passthrough forwards them verbatim, untranslated).\n- **Housekeeping short-circuits** (e.g. quota probes, title generation). Forwarded to upstream as normal traffic.\n- **OpenAI Responses / \"o\"-series reasoning API.** The preset family speaks chat-completions only; the Responses API is a different transport dialect, out of scope.\n- **TUI / GUI / chatbot wrappers.** Not in scope.\n\n**Streaming caveat (per dialect):** the **translating** presets are buffer-then-restream — shim drives the upstream non-streaming, then emits the canonical Anthropic SSE sequence in one burst (right protocol, no per-token latency benefit yet). The **passthrough** path streams the upstream's native Anthropic SSE through live, event-by-event, byte-for-byte.\n\n## Install\n\n```sh\nbrew install 1mb-dev/tap/shim\ngo install github.com/1mb-dev/shim/cmd/shim@latest\n```\n\nOr build from source (Go 1.25+):\n\n```sh\ngit clone https://github.com/1mb-dev/shim \u0026\u0026 cd shim\nmake build              # → ./shim\nmake build-all          # → dist/shim-{darwin-arm64,linux-amd64,linux-arm64}\n```\n\n## Dependencies\n\nRuntime (compile-time embedded; no network fetch at startup, no\ntoolchain required at runtime):\n\n- [`github.com/pkoukk/tiktoken-go`](https://github.com/pkoukk/tiktoken-go) — BPE tokenizer for cl100k_base counting on `/v1/messages/count_tokens` and `/v1/metrics` `token_delta.shim_total`.\n- [`github.com/pkoukk/tiktoken-go-loader`](https://github.com/pkoukk/tiktoken-go-loader) — embeds BPE tables (cl100k + o200k + p50k + r50k) via `go:embed`. shim only uses cl100k; the other three add ~5MB of dead weight to the binary.\n\nBoth are community ports (not OpenAI-official), pre-1.0, single-maintainer.\nThey are compile-time embedded, so the runtime supply-chain exposure is code\nvendored at build time, not fetched at startup. The token count is a\ncross-tokenizer approximation/drift signal, not a billing-grade count (see\n[docs/measurement.md](docs/measurement.md#token-counting)).\n\nBinary footprint: ~14 MB per platform (darwin-arm64, linux-amd64, linux-arm64).\nThe embedded tokenizer accounts for ~7 MB of that; the binary is still a\nsingle-file static drop-in.\n\n## Config\n\nCopy `.env.example` to `.env` and fill in `UPSTREAM_API_KEY`. All variables:\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `BIND_ADDR` | `127.0.0.1` | Listen address. **Do not bind 0.0.0.0** unless you accept that the proxy carries your upstream API key and has no auth of its own. |\n| `PORT` | `8082` | TCP port. |\n| `ADAPTER` | `deepseek` | `deepseek` / `openai` / `openrouter` / `ollama` (OpenAI-dialect, translating, buffered SSE) or `anthropic` (transparent passthrough, live SSE). Unknown values fail at startup. |\n| `UPSTREAM_API_KEY` | _required (except `ollama`)_ | Credential sent upstream — `Authorization: Bearer` for the OpenAI-dialect presets, `x-api-key` for anthropic-passthrough. `ollama` runs keyless (a key is still forwarded if set). |\n| `UPSTREAM_BASE_URL` | per-preset default | Upstream root. Empty → the chosen preset's default (deepseek `…/v1`, openai `…/v1`, openrouter `…/api/v1`, ollama `…:11434/v1`, anthropic `https://api.anthropic.com`). Set to point at a non-default host. |\n| `UPSTREAM_OPUS_MODEL` | (preset role default) | Override for `claude-opus*` on the active OpenAI-dialect preset; passthrough forwards the model name unchanged. |\n| `UPSTREAM_SONNET_MODEL` | (preset role default) | Override for `claude-sonnet*`. |\n| `UPSTREAM_HAIKU_MODEL` | (preset role default) | Override for `claude-haiku*`. |\n| `UPSTREAM_MODEL` | (empty) | Catch-all for non-claude names, and the role models for presets without role defaults (e.g. ollama). Empty = pass through. |\n| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error`. |\n| `LOG_REDACT` | `true` | Scrub secrets and prompt content from logs. Set `false` for local debugging only. |\n| `MAX_REQUEST_BYTES` | `1048576` | Oversize body returns HTTP 413 Anthropic-shaped error. |\n\n## Security model\n\nshim has **no built-in authentication.** It trusts the network boundary\nbetween itself and the client. Defaults assume one user, one machine:\n`BIND_ADDR=127.0.0.1` is loopback-only, and the inbound `Authorization`\nheader is discarded (shim authenticates upstream with `UPSTREAM_API_KEY`\nfrom `.env`). No inbound rate-limiting, per-route auth, or quota tracking.\n\nIf you bind to a non-loopback address, anyone on that network can route\nthrough shim, burning your upstream quota and exposing prompt content.\nDon't do it without an authenticating reverse proxy in front. shim emits a\nstartup `WARN` when `BIND_ADDR` is not loopback. This applies doubly to the\nkeyless `ollama` preset: with no upstream key gating abuse either, a wide bind\nis a fully open relay to your local model. shim has no inbound auth on *any*\nendpoint — `/v1/metrics`, `/health`, and the rest are open on the bind address.\n\nLogs scrub `Authorization`, prompt/message content, URL query strings,\nand credential-shaped keys by default (`LOG_REDACT=true`). Set\n`LOG_REDACT=false` only for local debugging.\n\n## Transparent passthrough\n\nSet `ADAPTER=anthropic` to run shim as a transparent proxy in front of a native\nAnthropic Messages endpoint:\n\n```sh\nADAPTER=anthropic\nUPSTREAM_BASE_URL=https://api.anthropic.com   # default; override for a compatible endpoint\nUPSTREAM_API_KEY=\u003cyour Anthropic key\u003e          # sent upstream as x-api-key\n```\n\nNo translation on this path: the request is forwarded byte-for-byte (so fields\nshim doesn't model — `metadata`, `top_k`, … — survive), the response is returned\nverbatim, streaming is live Anthropic-SSE, and upstream errors pass through with\ntheir status and body unchanged (the native envelope is already correct). shim\nforwards the client's `anthropic-version` / `anthropic-beta` and injects\n`2023-06-01` (logged) when absent.\n\nThe point is observability with zero translation risk: shim's redacted logs,\n`/v1/metrics`, and loud-fail in front of real Claude. `token_delta` here is a\ncl100k-vs-Anthropic drift signal, not a verification, since no translation\nhappens. If you want a transparent Anthropic proxy *without* measurement, skip\nshim and point Claude Code at the endpoint directly. Seam detail:\n[ADR 0002](docs/adr/0002-translator-seam-error-path.md).\n\n## Operational limits\n\nHardcoded (not env-configurable):\n\n| Limit | Value | Source |\n|---|---|---|\n| `ReadHeaderTimeout` | 10s | `internal/server/server.go` |\n| `WriteTimeout` | 200s | `internal/server/server.go` — caps streaming wall-clock |\n| `IdleTimeout` | 120s | `internal/server/server.go` |\n| `MaxHeaderBytes` | 1 MiB | `internal/server/server.go` |\n| Upstream `Client.Timeout` | 180s | `internal/server/server.go` (`newUpstreamClient`) |\n| Upstream `TLSHandshakeTimeout` | 10s | `internal/server/server.go` |\n| Upstream `ResponseHeaderTimeout` | 30s | `internal/server/server.go` |\n\nThe 200s server `WriteTimeout` is the hard upper bound on any single\nresponse (streaming or non-streaming); it's sized to outlive the 180s upstream\n`Client.Timeout` so an upstream cancellation surfaces as a recordable upstream\nerror rather than a server-side write timeout. The 180s ceiling covers\nreasoning-mode generations under the buffer-then-restream path.\n\n## Run\n\nA few ways:\n\n**Manual.** Start the server, point Claude Code at it:\n\n```sh\n./shim \u0026\nexport ANTHROPIC_BASE_URL=http://127.0.0.1:8082\nexport ANTHROPIC_API_KEY=shim   # any non-empty value works; shim auths upstream itself\nclaude\n```\n\n**Launcher.** `shim run` sets both vars and execs claude in one step:\n\n```sh\n./shim \u0026\n./shim run \"write a hello-world go program\"\n```\n\nThe launcher prints a single breadcrumb line to stderr (`shim run → claude=/path/to/claude, base=http://...`) so you can see what it resolved before claude's own output starts.\n\n**Service** (Homebrew). shim can run as a managed background daemon so it's always up — no manual `./shim \u0026`. Opt-in; install does not auto-start it:\n\n```sh\nbrew services start shim\n```\n\nA service has no working directory of its own, so put config where shim looks for it. It reads config in order: `SHIM_ENV_FILE`, then `./.env`, then `~/.config/shim/.env`:\n\n```sh\nmkdir -p ~/.config/shim\nprintf 'ADAPTER=deepseek\\nUPSTREAM_API_KEY=\u003cyour key\u003e\\n' \u003e ~/.config/shim/.env\n```\n\nThe keyless `ollama` preset needs no key — `brew services start shim` just works against a local Ollama.\n\n`shim version` prints the build version (set at release; `dev` for a plain `go build`).\n\n## Measurement\n\nshim's reason to exist. `GET /v1/metrics` returns a JSON snapshot since startup:\nper-endpoint latency (p50/p95/p99), the gap between shim's cl100k_base token count\nand the upstream's claimed count, how often shim rewrote a request in flight, and\ncounts of requests seen, upstream non-2xx, and recovered panics. `GET /metrics`\nserves the same aggregates in Prometheus text format.\n\n```sh\ncurl -s http://127.0.0.1:8082/v1/metrics | python3 -m json.tool\n```\n\n```json\n{\n  \"latency\":         {\"/v1/messages\": {\"p50\": 0.32, \"p95\": 0.98, \"p99\": 1.59, \"n\": 14}},\n  \"token_delta\":     {\"/v1/messages\": {\"shim_total\": 86, \"upstream_prompt_total\": 336, \"n\": 14}},\n  \"rewrites\":        {\"model\": 14, \"stop_sequences\": 2},\n  \"upstream_errors\": {\"/v1/messages\": {\"total\": 1, \"by_status\": {\"400\": 1}}},\n  \"panics_total\": 0\n}\n```\n\nState is in-memory and resets on restart; both endpoints are loopback-only by\ndefault. The token delta is a cross-tokenizer drift signal, not a billing-grade\ncount. Full field reference, the Prometheus metric table, and token-counting\nnotes: [docs/measurement.md](docs/measurement.md).\n\n## Errors and debugging\n\nOn an upstream non-2xx, shim logs one `upstream error` line (carrying\n`upstream_status`, `resolved_model`, and a capped `body_preview` of the upstream\nbody) and increments `upstream_errors` in `/v1/metrics`. The client gets an\nAnthropic-shaped error; the upstream body is logged, never echoed to the client.\n\n`body_preview` is operator-facing diagnostic and is **not** redacted — some\nupstreams echo a prompt fragment in their error body, so redact at your log sink\nif that matters. Details: [docs/measurement.md](docs/measurement.md#errors-and-debugging).\n\n### Thinking-block signatures\n\nshim attaches a constant `signature` (`shim-passthrough-v1`) to emitted thinking\nblocks and does not verify what clients send back: the loopback threat model makes\ntamper-evidence unnecessary, and DeepSeek discards the field. Deliberate — don't\nadd HMAC back as \"the missing fix\" (rationale in\n[docs/measurement.md](docs/measurement.md#thinking-block-signatures)).\n\n## Project layout\n\n```\ncmd/shim/             # CLI entry: shim, shim run\ninternal/\n  config/             # zero-dep .env loader\n  obslog/             # log/slog with redaction\n  adapter/            # Adapter interface + InboundHeaders ctx helper\n    openaichat/       # OpenAI-dialect core + preset registry (deepseek/openai/openrouter/ollama)\n    anthropic/        # native-Anthropic (transparent passthrough) adapter\n  translate/          # Anthropic ↔ OpenAI + per-adapter Translator seam (passthrough.go = identity)\n  tokens/             # cl100k_base BPE counter\n  measure/            # /v1/metrics collector (latency, token delta, rewrites)\n  launcher/           # shim run\n  server/             # HTTP server + handlers + error taxonomy\ntestdata/fixtures/    # recorded upstream responses for tests\n```\n\nAdding a provider depends on its transport dialect. An **OpenAI-dialect**\nprovider is a data row in `openaichat`'s preset registry — base URL, per-role\nmodel map, auth flag, optional headers — no new file. A **genuinely new\ndialect** (not OpenAI-chat, not native Anthropic) is a new sub-package under\n`internal/adapter/` implementing `adapter.Adapter` (including `Translator()`\nfor its dialect), wired into `cmd/shim/main.go`'s `registerAdapter` —\none branch per dialect, no `init()`-time registration.\n\n## License\n\n[MIT](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1mb-dev%2Fshim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1mb-dev%2Fshim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1mb-dev%2Fshim/lists"}