4ROUTER / Integration

Keep your client. Connect the platform.

Update your base_url and key. Keep your existing OpenAI client. Model routing, usage billing and decision records come with the same call.

Protocol
OpenAI-compatible
Additional SDK
None required
Service model name
enthalpy-1
The path of a callPOST/v1/chat/completions
  1. Your clientmessages + enthalpy
  2. Routing & constraintsenthalpy-1
  3. Response & decision recordchoices + enthalpy_route

Changing base_url

OpenAI-compatible. A client that ignores the enthalpy and enthalpy_route objects still receives a fully valid chat completion.

curl -sS http://127.0.0.1:8080/v1/chat/completions \
  -H "Authorization: Bearer $ENTHALPY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "enthalpy-1",
    "messages": [{"role": "user", "content": "把这段 changelog 总结成三行"}],
    "enthalpy": {"cost_weight": 1, "explain": true}
  }'

model carries the service model name (ENTHALPY_SERVICE_MODEL_NAME, default enthalpy-1), meaning: you choose. Put the real id of a model in the pool there instead and the request becomes PINNED — the router is skipped and that model answers directly, while auth, admission, billing, and the decision log all stay in place. It is the escape hatch that makes moving over safe to try.

An unknown field inside the enthalpy block is a 400 (extra="forbid") — a typo is a loud error rather than a setting that is silently ignored. cost_weight takes a value in [0, 8], in units of one logit per log1p(est_cost / $0.01), and defaults to 0.

The enthalpy_route object

{
  "model": "kimi-k2.5",
  "enthalpy_route": {
    "selected_model": "kimi-k2.5",
    "billed_usd": "0.00044520",
    "router_latency_ms": 71.1,
    …
  }
}

Money is always a string: billed_usd is exact to 1e-8 of a dollar, and a JSON float would round away the precision the ledger took care to keep. router_latency_ms is the time the routing step spent on its own, reported alongside total_latency_ms, so the overhead of this layer can be seen and questioned by itself.

The full response body: usage, attempts, the candidate table, and the plan field
{
  "id": "chatcmpl-…",
  "object": "chat.completion",
  "model": "kimi-k2.5",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "…" },
                "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 41, "completion_tokens": 260, "total_tokens": 301 },
  "enthalpy_route": {
    "request_id": "…",
    "mode": "select",
    "selected_model": "kimi-k2.5",
    "attempts": [ { "step": 0, "model_id": "kimi-k2.5", "score": 14.678,
                    "accepted": true, "error": null,
                    "prompt_tokens": 41, "completion_tokens": 260,
                    "upstream_cost_usd": 0.000371, "latency_ms": 2841.0 } ],
    "upstream_cost_usd": 0.000371,
    "billed_usd": "0.00044520",
    "router_latency_ms": 71.1,
    "total_latency_ms": 2912.4,
    "accept_prob": null,
    "checkpoint_id": "…",
    "pool_fingerprint": "…",
    "notes": [],
    "candidates": [ /* with explain: true, one row per candidate */ ],
    "plan": null
  }
}

The base URL above is the loopback address the documentation uses. The hosted 4router.cn/v1 is still being connected to upstream credentials, and until it answers, no unreachable address is printed here.

The integration contract

Know what comes back.

A familiar interface, with explicit fields for the model, the charge, the stream and the failure.

01
response.model

Read the model that answered

You send enthalpy-1; the model field that comes back is the id of the model that actually generated the text. Billing, evaluation, and reproduction all follow that field.

02
stream: true

The decision arrives before the end

The enthalpy_route frame is emitted before data: [DONE] — a client stops as soon as it reads [DONE]. Read the frames in order; do not skip ahead.

03
max_cost_usd

Apply the budget before selection

enthalpy.max_cost_usd is not advisory. A candidate over budget is marked ineligible during scoring, and exclusion_reason states why.

eligible: falseexclusion_reason: "budget"

04
error.type

Make failures actionable

The error object includes a stable type, a readable message, optional structured context and a request_id. Handle failures by type and correlate the request with its logs.

402 · budget_exceeded429 · quota_exceeded

Run locally. Reproduce the result.

Go from the call to the implementation.

The examples target a local development endpoint. Start the service, run the evaluation and inspect the reports behind the result.

Open commands & reproduction checklist

Self-hosting and reproduction

Not one number on this page is typed by hand — web/crates/ui/build.rs generates them at compile time, as typed constants, from the JSON under reports/. Change an evaluation result and the page follows; drop a field and the build fails naming the file and the JSON path, rather than quietly rendering a 0.

Starting the full service

docker compose -f deploy/docker-compose.yml up -d
docker compose -f deploy/docker-compose.yml logs -f gateway

Reproducing the evaluation

# 1. Sample a matrix over your own pool (docs/TRAINING.md §1b quotes the cost)
enthalpy data harvest --dataset gsm8k --dataset math500 --split test \
    --limit 1000 --out data/matrices/mine.jsonl.gz

# 2. Train a checkpoint
enthalpy train sft --matrix data/matrices/mine.jsonl.gz --name gen1

# 3. Evaluate -- free, and it calls no provider
enthalpy eval --matrix data/matrices/mine.jsonl.gz \
    --checkpoint gen1 \
    --test-fraction 0.3 --split-seed 0 \
    --reference best_single \
    --sweep-cost \
    --out reports/gen1.json

--out writes both reports/gen1.json — every number, for regression — and reports/gen1.md, which carries the headline, the provenance line, the results table, the † note, the bound note, and the comparison table.

Before citing these numbers

Quoted from docs/BENCHMARKS.md §7:

  • The matrix, its dataset list, its --limit, and its --n are all stated
  • --test-fraction and --split-seed are stated
  • The reference router is named, and it is not a bound
  • The correct threshold is stated
  • Every Δ carries a confidence interval and a sign test p value beside it
  • An interval that crosses zero is reported as no conclusion
  • The pool fingerprint and the checkpoint_id are recorded beside the table
  • models used is not 1
  • The latency figures state the device, and the cold start has not been displaced by the warm p50

The raw artefacts are in the repository: reports/crb-gen2.json, reports/crb-gen2.sweep.json, reports/crb-gen2-vs-knn.json, reports/router-latency.cpu.json, and reports/stage0-routereval.report.json. Every number on the page can be traced to its source in one of them.

4ROUTER / SYSTEMS RESEARCH

Connect configuration choices to system behavior.

Understand how operators, KV state and scheduling shape an inference path.

Read the systems notes in Resources
ATTENTION / MEMORY

FlashAttention & PagedAttention: different memory problems

One optimizes data movement; the other organizes persistent state.

PREFIX / REUSE

RadixAttention: reuse prefixes, not answers

Reuse a prefix state with compatible computational history.

TIERS / TRANSFER

LMCache: placing KV state in a storage hierarchy

Avoiding prefill requires paying for state access.

Build with 4Router

Your next call starts here.