Kevinkv/#
Inference fabric — routing and cost ledger

Routing is commodity now. The ledger is not.

One endpoint sits in front of every model you use. You change a base URL, not your code. Responses come back stamped with what the request cost, what it would have cost on your default model, and which work item to bill it to — written to a database on your own disk. Four credible cheapest-model routers appeared or matured between June and July 2026. None of them returns you a receipt.

does work stateful data third party initiates — every path on this page is a real file in the shipped source tree

You change a base URL, not your code

The proxy speaks the Anthropic Messages API. Anything already pointed at Anthropic — Claude Code, an agent loop, a script, a CI job — points at the fabric instead by setting one environment variable. Request and response shapes are unchanged; the response gains one extra key.

Your tool

Any client that already speaks the Anthropic Messages API. No SDK swap, no wrapper library.

Fabric endpoint

Accepts POST /v1/messages. Also serves /v1/models and /health.

fabric_proxy/server.py

Router

Resolves model id to provider, enforces a hard per-call cost cap, records the reason for the decision.

fabric_proxy/router.py

The whole integration

Port resolves from the service's own env var — service names, never literals.

# point an existing tool at the fabric
ANTHROPIC_BASE_URL=http://<host>:$KEVIN_PORT_WALT

# optional per-request headers, all read by server.py
X-Kevin-Ticket:               KAN-1120
X-Kevin-Holon:                <work-unit id>
X-Kevin-Precompressed-Bytes:  <n>

What the router actually does today

  • Maps a model id to its provider by prefix, so one client can address every backend through one endpoint.
  • Enforces HARD_COST_CAP_USD — default 0.10 per call — and returns HTTP 402 rather than serving a request that would exceed it.
  • Optionally selects a model from a subscription tier, behind a feature flag.

It does not yet auto-downgrade to the cheapest capable model. That is marked as a future phase in router.py and we are not going to imply otherwise. It is also the part of this layer that stopped being worth anything: a hyperscaler ships a router free inside your account, a model vendor ships one that routes to itself, and an Apache-2.0 project ships one for nothing. The next two sections are the parts that are not free.

Competitors are inventory, not enemies

A router sold by a model vendor routes into that vendor's catalogue. This one has nothing to sell you at the model layer, so price convergence across providers becomes an arbitrage surface instead of a threat. Each backend is a small adapter that translates the Anthropic request shape to whatever dialect that provider speaks, and normalises the answer back.

Backends it can reach today

one adapter each, in fabric_proxy/providers/

BackendAdapter
Anthropicanthropic.py
OpenAIopenai_provider.py
Groqgroq.py
Geminigemini.py
Local Ollama, on your own GPUollama_provider.py

Four of those five speak the OpenAI chat-completions dialect upstream, which is why adding an OpenAI-compatible backend is a small adapter rather than a redesign. Together, Fireworks, Bedrock and a direct vLLM adapter are named on the roadmap and are not written. If you need one of them today, you are writing it.

Local models are metered, not exempt

Models served from your own hardware are registered at a real price of zero rather than left unpriced. An unpriced model silently costs $0.00 and silently disables the cost cap — a deliberate zero and an unknown model must not look alike in the ledger.

The adapter refuses to mislabel a call

The local proxy behind the Ollama adapter can soft-fall-back through free cloud providers and, as a last resort, a paid one. If it reports that something other than local inference served the request, the adapter raises OllamaFallbackError and fails the call rather than bill a paid request as free local compute. A ledger that quietly absorbs that substitution is worth nothing.

Second-order benefit, and the one operators feel first: provider catalogues churn. Models get deprecated on weeks of notice and free tiers get withdrawn on a date. A routing layer absorbs that; a direct integration in fifteen client codebases eats it.

Two savings axes, deliberately not collapsed

Every non-streaming response carries a kevin_value block computed for that exact request. It reports two independent things, and it reports them separately.

The stamp

Field names exactly as emitted by fabric_proxy/kevin_value.py. Values below are zeroed — this is the shape, not a measurement.

"kevin_value": {
  // routing axis
  "model_routed":             "…",
  "model_baseline":           "…",
  "provider":                 "…",
  "input_tokens":             0,
  "output_tokens":            0,
  "cost_routed_usd":          0.000000,
  "cost_baseline_usd":        0.000000,
  "routing_savings_usd":      0.000000,
  "routing_reason":           "…",
  "latency_ms":               0,
  // compression axis
  "precompressed_bytes":      0,
  "precompressed_tokens_est": 0,
  "compression_ratio":        1.0,
  "compression_savings_tokens":0,
  "compression_savings_usd":  0.000000,
  // rollup of both
  "naive_cost_usd":           0.000000,
  "total_savings_usd":        0.000000
}

Routing axis — a different model for the same tokens

cost_baseline_usd prices the same measured token counts on your declared default model. routing_savings_usd is the difference. Token counts come from the provider's own usage block on the response, not from an estimate.

Compression axis — fewer tokens for the same task

If the caller declares how many bytes of source it compressed before sending, the stamp prices the difference at the routed model's input rate. Both the ratio and the dollar figure are reported on their own.

Why a single number would be worthless

Routing savings and compression savings have different denominators and different failure modes. Routing is checkable against two public rate cards. Compression depends on what the caller claims it compressed, and on a caching baseline that is not yours.

Add them into one headline and no reader can attack either half, so the number cannot be falsified — which is the same as saying it cannot be verified. The rollup fields exist for convenience, and both components are always present beside them so you can take the claim apart.

The token estimate is an estimate

Precompressed size is converted to tokens at a flat four bytes per token — no tokenizer in the path. Good enough to size a saving, not exact. The constant is in compression_metric.py; go read it.

Compression input is caller-attested

The precompressed byte count arrives in a request header. The proxy cannot verify it. If you care about the compression axis, the number has to be produced by something you trust, and the routing axis is the one to lean on in an argument.

Prices are hand-reconciled

The rate table in config.py is static and dated, not fetched at runtime. It goes stale silently, so it carries its own reconciliation date and flags any model priced at zero outside the local providers. A counterfactual is only as honest as its rate card.

The ledger, and exactly what it holds

The stamp answers one request. The ledger is what makes it evidence: one row per call in SQLite on your own disk, inserted and never rewritten, indexed by the work item that caused the spend. None of the routers above ships this layer, which is the reason this page exists. (In the source and in the service table it is called Walt.)

Table fabric_calls

fabric_proxy/telemetry.py — at KEVIN_HOME/db/walt.db

ColumnWhy it is there
ts
request_id
When, and a unique id per call.
ticket
holon
stream_id
The work item, work unit and stream that caused the spend. Indexed. This is what makes cost attributable to a client, a ticket or a run instead of to a month.
session_id
tool_origin
task_type
Which caller, which tool, what kind of work.
model_routed
provider
Which backend actually served it.
input_tokens
output_tokens
Billable units, taken from the provider's usage block.
cost_routed_usd
latency_ms
What the call cost, and what the decision cost in time.
pub_verProvenance: identifies the writer that produced the row, so records written by different code are distinguishable rather than silently averaged together.

The write is synchronous, lock-guarded and single-row, and a failure is logged and dropped. Accounting must never be able to fail a request, and a queue that can silently back up is a worse lie than a missing row.

The limit, stated before you find it

The counterfactual is computed on every request and persisted alongside it: model_baseline, cost_baseline_usd and compression_savings_usd are ledger columns. So a savings rollup over a window is a query, not an estimate.

What it will not do is invent history. A row written before those columns existed has an unknown baseline, stored as unknown rather than as zero, because zero would read as "this request saved nothing" and quietly drag every historical average down. Those rows are excluded from the money columns and reported as a separate coverage figure, so the rollup always shows how much of the window it could actually account for.

Streaming responses are not ledgered

Streaming is passed straight through to Anthropic with no stamp and no ledger row. If your traffic is mostly streaming, the ledger will be mostly empty, and you should know that before you install rather than after.

A second sink exists, and is optional

A ClickHouse insert path carries the baseline columns and runs when a server is present. It is opt-in and silently skipped when absent, so on a normal install SQLite is the ledger that exists and the one that has rows.

We publish the schema rather than a savings figure on purpose. Our own ledger currently holds development traffic — overwhelmingly repeated test fixtures. It proves nothing about your workload and we will not quote it at you. The only number worth trusting is the one your own traffic produces on your own hardware, which is why the schema and the read path are the artifact and a percentage is not.

Where this is the wrong choice

This layer is narrow on purpose, and several of the cases below are ones we cannot win on any timescale that should matter to your decision.

It earns its place when…

  • You need cost attributable to a client or a ticket, not a single monthly invoice you divide by guesswork.
  • Data cannot leave your building, and a vendor control plane in the request path is disqualifying.
  • You already own GPUs and want them in the same pool as hosted models, addressed through one endpoint.
  • You run many workloads for many customers and the per-request record is a billable artifact, not a reporting nicety.
  • You are exposed to provider catalogue churn across more than one codebase.

Rent instead when…

  • You need SOC 2, a HIPAA BAA or FedRAMP. There are no compliance attestations here. In regulated procurement that is a disqualification, not a disadvantage, and no architecture argument repairs it.
  • Your inference budget is already committed spend. If marginal inference is pre-paid under an enterprise agreement, savings arithmetic is irrelevant to you.
  • Traffic is spiky or low-volume. Scale-to-zero on rented capacity beats an idle owned GPU, and it is not close.
  • Latency is the binding constraint. Purpose-built inference silicon holds the floor. Commodity hardware does not, and routing does not change that.
  • You need frontier-model quality at volume. Your own fleet will not serve it usefully; routing through to a rented endpoint is the honest answer, and this layer will do exactly that.
  • You have a platform team that wants to assemble it. vLLM, a semantic router and Prometheus will get you most of this. That team is not who this is for.
  • You want a chat window on one GPU. A desktop app is better at that than anything we would build.

One more, on vendor risk: we are small, and against a well-capitalised competitor that is a legitimate finding in a procurement review. The only answer that holds is structural — it runs on your hardware, the ledger is a SQLite file you own, and the deployment outlives the vendor.