llm-router
Ecosystem role and current integration status: component roadmap. The public cross-repository plan is owned by the Agentic Security Harness ecosystem roadmap.
A tiny, dependency-light async LLM router with role tiers and per-call cost logging.
One call() interface for the tested OpenAI-compatible request shape and a
separate Yandex AI Studio path. Provider compatibility depends on each endpoint's
current API contract and must be verified before use.
The public library demonstrates a cheap-to-chief routing pattern with offline tests. It does not publish or verify a production deployment claim. No SDKs or models are hardcoded in the routing logic.
The repository now publishes a source-owned, offline invocation-receipt contract and is
therefore contract_only in the ecosystem. Its source tree builds the unique distribution
agentic-llm-router==0.2.1, imported as llm_router. Exact publication is established by
PyPI release history, not source
metadata alone. It is not automatically activated or invoked by Harness.
Supply-chain boundary: the generic PyPI name
llm-routerbelongs to another project. Do not install or declare that coordinate for this repository. The only planned public distribution coordinate isagentic-llm-router.
Why
In agentic systems most LLM calls are cheap bulk work (extract, classify, filter) and a few are high-stakes (the final decision). Paying flagship prices for everything is wasteful; juggling provider SDKs is annoying. llm-router gives you:
- Role tiers —
cheap/mid/chief/audit, each mapped to a model via env. Route volume tocheap, escalate only candidates tochief. - Provider flexibility — a custom
OPENAI_BASE_URLcan target endpoints that implement the tested request/response contract; Yandex AI Studio has a separate path. Provider identity, terms, availability, and exact compatibility are external gates. - Per-call cost — every call returns token counts and cost in USD + a configurable local currency (set
LLM_FX/LLM_CCY). Aggregate the dicts to a budget log. - Budget helpers — aggregate usage records, check a daily cap, and estimate savings
versus sending the same tokens to the
chiefmodel. - Resilience — retries on
429/5xxwith exponential backoff. - Canonical receipt contract — strict, digest-bound attempt, usage, pricing, and FX evidence without credentials, endpoints, prompts, output text, response bodies, or exception messages.
Features
- Single
async call(role, system, user) -> (text | None, usage)interface. - Four configurable role tiers, models set per provider via env.
- OpenAI-compatible and Yandex AI Studio providers.
json_mode=True→ addsresponse_format={"type":"json_object"}(OpenAI-compatible).- Cost estimation from an override-able price table (
LLM_PRICE_<MODEL>_IN/OUT_USD_PER_1M). - Budget helpers for logs you own:
summarize_usage,budget_status, andbuild_savings_report. - Zero secrets cached at import — all config read live from env.
- Importing
llm_router, includingfrom llm_router import call, performs no provider I/O and starts no child process. Theaiohttpnetwork dependency is loaded only when the caller explicitly invokescall()with provider configuration present. - ~150 LOC, one runtime dependency (
aiohttp).
Install
python -m pip install agentic-llm-router==0.2.1
For editable development use python -m pip install -e .[dev]. Requires Python 3.9+.
CI builds and audits the exact installed wheel on Linux and Windows. Historical
Harness v1.4.0 and v1.5.0 use Router 0.2.0 for their passive router extra;
they do not include the passive-import repair. A new Harness release must update that
pin after Router 0.2.1 is published. Installation does not configure credentials,
choose a provider, or make a provider call.
Version 0.2.1 packages the passive-import repair from PR #12: ordinary package import
does not load aiohttp or start a child process. Source and installed-wheel tests install
process/network audit denial before import. Provider calls and production compatibility
are not exercised or certified by those tests. Published 0.2.0 bytes are unchanged.
Quickstart
import asyncio
from llm_router import call
async def main():
text, usage = await call("cheap", "You are concise.", "Name 3 primary colors.")
print(text)
print(usage) # {provider, model, role, input_tokens, output_tokens,
# total_tokens, cost_usd, cost_local, currency}
asyncio.run(main())
Set at least a provider + key first (see Configuration). For OpenAI:
export OPENAI_API_KEY=sk-...
Providers
| Provider | Set | Auth |
|---|---|---|
| OpenAI | LLM_PROVIDER=openai (default), OPENAI_API_KEY |
Bearer |
| Alibaba Qwen | OPENAI_BASE_URL=<dashscope compatible-mode/v1> + OPENAI_API_KEY |
Bearer |
| OpenRouter / Together / Ollama / vLLM | OPENAI_BASE_URL=<their /v1> + OPENAI_API_KEY |
Bearer |
| Yandex AI Studio | LLM_PROVIDER=yandex, YANDEX_API_KEY, YANDEX_FOLDER_ID |
Api-Key |
The base URL must NOT include
/chat/completions— the router appends it. Yandex requiresYANDEX_FOLDER_ID(or an explicitYANDEX_<ROLE>_MODEL); otherwisemodel_forraises a clear configuration error (fail-fast) instead of sending an empty model.
Roles
from llm_router import call, model_for
model_for("cheap") # -> e.g. "gpt-4o-mini" (or your LLM_CHEAP_MODEL)
model_for("chief") # -> e.g. "gpt-4o"
# pattern: cheap for volume, chief only when it matters
facts, u1 = await call("cheap", EXTRACT_PROMPT, raw_text)
if looks_important(facts):
verdict, u2 = await call("chief", DECIDE_PROMPT, facts, json_mode=True)
Cost logging
text, usage = await call("cheap", sys, user)
# usage["cost_usd"] -> e.g. 0.0001
# usage["cost_local"] -> cost_usd * LLM_FX
# usage["currency"] -> LLM_CCY (e.g. "RUB")
Append each usage to a JSONL file and you have a per-call budget log. Prices come from a small built-in table and are illustrative — override per model:
export LLM_PRICE_GPT_4O_MINI_IN_USD_PER_1M=0.15
export LLM_PRICE_GPT_4O_MINI_OUT_USD_PER_1M=0.60
Summarize a batch of usage records:
from llm_router import summarize_usage, budget_status, build_savings_report
usages = [u1, u2] # dicts returned by call()
print(summarize_usage(usages).as_dict())
print(budget_status(usages, limit_usd=1.00).as_dict())
print(build_savings_report(usages, counterfactual_role="chief").as_dict())
LLM_BUDGET_USD_DAY can be used as a default budget cap for budget_status(...).
The router stays stateless; you decide where the JSONL budget log lives.
Canonical invocation receipts
router-invocation-receipt-v1.0 is a separate offline
interchange surface for already-observed sanitized values. It uses canonical UTF-8 JSON,
domain-separated content identities, contiguous attempt accounting, strict token totals,
and integer nano-unit cost arithmetic. Pricing and FX inputs bind caller-supplied source
artifact digests; those digests are evidence references, not authenticity proofs.
The receipt builder never calls a provider and the existing call() return value is
unchanged. A receipt contains digests of request, response, output, model, and producer
identity—not their raw bytes. It always declares invoice_authoritative=false and
operational_authority=none.
Deterministic hashes are content-minimizing, not anonymizing: they remain linkable and can be guessed when the source space is small. A receipt is not automatically safe to publish.
from llm_router import InvocationAttemptV1
attempt = InvocationAttemptV1(
attempt_index=1,
outcome="network_error",
http_status=None,
reason_code="provider.network_error",
response_payload_sha256=None,
)
# Supply only already-observed sanitized values; see docs/invocation-receipt.md.
Configuration (env)
| Variable | Default | Purpose |
|---|---|---|
LLM_PROVIDER |
openai |
openai (compatible) or yandex |
OPENAI_API_KEY |
— | key for the OpenAI-compatible endpoint |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
point at Alibaba/OpenRouter/Ollama/... |
LLM_CHEAP_MODEL / LLM_MID_MODEL / LLM_CHIEF_MODEL / LLM_AUDIT_MODEL |
gpt-4o-mini / gpt-4o-mini / gpt-4o / gpt-4o | role → model |
YANDEX_API_KEY, YANDEX_FOLDER_ID |
— | Yandex AI Studio |
YANDEX_<ROLE>_MODEL |
wraps gpt://<folder>/<name>/latest |
override a Yandex role model URI |
LLM_FX |
1.0 |
USD → local currency multiplier |
LLM_CCY |
USD |
local currency label |
LLM_DEFAULT_TIMEOUT |
60 |
per-call timeout (s) |
LLM_MAX_RETRIES |
2 |
retries on 429/5xx |
LLM_PRICE_<MODEL>_IN/OUT_USD_PER_1M |
from table | override price per model |
LLM_BUDGET_USD_DAY |
unset | optional cap used by budget helpers |
See .env.example.
Examples & tests
python examples/basic.py # one call
python examples/role_tiers.py # cheap vs chief + cost
python -m pytest -q # offline unit tests (no network)
What each example shows and what it does not prove: examples/README.md.
Docs
- Component roadmap — source-owned ecosystem role, platform evidence, historical projections, and integration gates.
- Project map — modules, what exists today vs not included, reviewer checklist.
- Use cases — who this is for, practical workflows, limitations.
- Operating model — role budgets, usage records, escalation gates, and residual risk.
- Invocation receipt V1 — canonical codec, attempt state machine, fixed-point arithmetic, privacy boundary, and non-claims.
- Harness ecosystem roadmap — the canonical public ordering for cross-repository integration work.
Limitations / non-goals
- Chat completions only (no streaming, embeddings, tools/function-calling, vision — kept intentionally small).
- One system + one user message per call (no multi-turn history helper).
- The price table is illustrative; confirm real prices with your provider.
- Not a full framework — it's a focused routing + cost-logging utility you drop into your own agent loop.
- Not the portfolio flagship, policy authority, or security boundary. Larger systems own their own validation, authorization, storage, and safety rules.
License
MIT — see LICENSE.
Metadata
Release files for agentic-llm-router 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agentic_llm_router-0.2.1.tar.gz | 26.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentic_llm_router-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.4 kB
Release files / agentic_llm_router-0.2.1.tar.gz
| Download URL | agentic_llm_router-0.2.1.tar.gz |
|---|---|
| Size | 26.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aab613771d108ce5ca7a48d13f1ec03bebb78544ba7de64fc00201cdf69847a3
|
|
BLAKE2b-256 checksum How to use checksums |
e9703dec01bab74b8bf9a0f6b8987ff534186a125c43a37ca82c2e1718937891
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / agentic_llm_router-0.2.1-py3-none-any.whl
| Download URL | agentic_llm_router-0.2.1-py3-none-any.whl |
|---|---|
| Size | 20.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e37bdfa7fcb96a3afe35231309676592e23b27c1a2147808e6dc3f7f1da9b59
|
|
BLAKE2b-256 checksum How to use checksums |
d1d2f6a66e8a2d9c877463a8c639c75ec595c860121e694aec950f4aa180814d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log