Skip to main content

Route LLM calls through one send_message() across OpenAI-compatible providers and Anthropic's native Messages API; keep a JSONL ledger of every request and response for offline cost reconciliation.

Project description

llm-router-ledger

Route any LLM call through one send_message() and keep a JSONL ledger of every request and response for offline cost reconciliation.

Provider support

Status Adapter Providers
Supported direct Anthropic
Supported OpenAI-compat Azure OpenAI, DeepSeek, Local Ollama, MiniMax, OpenAI, OpenRouter, Qwen, Zhipu / GLM
Supported via OpenRouter ByteDance Seed, Xiaomi MiMo
Planned direct Gemini
  • All "Supported" rows in 0.1.2 are live-smoke-verified end-to-end.
  • Anthropic requires the optional [anthropic] extra: uv pip install llm-router-ledger[anthropic].
  • For ByteDance Seed and Xiaomi MiMo, use provider: openrouter with the appropriate model id.

Install

uv pip install llm-router-ledger

Quickstart

Set OPENROUTER_API_KEY in .env and create llm_endpoints.yaml in the working directory. The fastest path is to copy examples/llm_endpoints.example.yaml to llm_endpoints.yaml in your working directory and edit it.

from llm_router_ledger import UsageTracker, send_message

tracker = UsageTracker(
    log_path="logs/usage.jsonl",
    project_id="my-blog",
)
text, usage, gen_id = send_message(
    endpoint_name="openrouter-mimo-v2-flash",
    system="You are concise.",
    user="Explain prompt caching in two sentences.",
    tracker=tracker,
)

Or against a local Ollama server, with no API costs:

text, usage, gen_id = send_message(
    endpoint_name="local-llama",
    system="You are concise.",
    user="Explain prompt caching in two sentences.",
    tracker=tracker,
)
  • send_message() returns (response_text, usage_dict, generation_id).
  • UsageTracker appends paired llm_request / llm_response events to the JSONL log, stamped with project_id, run_tag, run_label, and purpose for later grouping.

JSONL ledger schema

  • UsageTracker writes two events per send_message() call: an llm_request before the call, and an llm_response after.
  • Both share a request_id so they can be paired. Top-level fields on each event include project_id, provider, model, purpose, run_tag, run_label, and timestamp.
  • The llm_response event additionally carries usage (with prompt_tokens, completion_tokens, total_tokens) and a response preview.

Identifying a response for billing reconciliation: the response id is routed to one of two fields based on prefix:

  • generation_id: set when the id starts with "gen-" (OpenRouter convention). Use this when joining against OpenRouter's CSV export, which calls the column generation_id.
  • provider_response_id: set for everything else. OpenAI, Azure OpenAI, Ollama, and most direct-provider endpoints return ids like "chatcmpl-..." that land here. Use this when joining against OpenAI-family billing exports or any provider-native log that exposes a chat completion id.

Exactly one of the two fields is populated per llm_response event; queries that join the ledger to billing data should COALESCE over both or branch on provider.

CLI

llm-router-ledger list                          # show configured endpoints
llm-router-ledger validate llm_endpoints.yaml   # validate the YAML
llm-router-ledger stale --days 30               # endpoints with stale pricing
llm-router-ledger chat --endpoint openrouter-mimo-v2-flash --system "You are concise." --user "Hello." --log-path logs/usage.jsonl --project-id my-project

Env vars

Variable Purpose
LRL_RUN_TAG Stamped on every JSONL event.
LRL_RUN_LABEL Stamped on every JSONL event.
LRL_CONFIG_PATH Default YAML path when load_config() is called with no argument.

Development

git clone https://github.com/nirmalyaghosh/llm-router-ledger
cd llm-router-ledger
uv sync --extra dev
pytest tests/unit

Verify a local Ollama setup end-to-end with python examples/smoke_test_ollama.py (see prerequisites at the top of the script).

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

llm_router_ledger-0.1.2.tar.gz (2.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llm_router_ledger-0.1.2-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file llm_router_ledger-0.1.2.tar.gz.

File metadata

  • Download URL: llm_router_ledger-0.1.2.tar.gz
  • Upload date:
  • Size: 2.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for llm_router_ledger-0.1.2.tar.gz
Algorithm Hash digest
SHA256 30467169b188ec1556b94ebe0aff85342f7ee7b0159e98cfe5b12fb46d2a9458
MD5 4d02b159c278bea537b18b9c13e14f7c
BLAKE2b-256 52adbc7f83693fa0f51a60e5484b15f5d33b1b06ffe58f96a4d2a5487d529c0a

See more details on using hashes here.

File details

Details for the file llm_router_ledger-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for llm_router_ledger-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a853b42f998185c1ff4b2ed512cf36add6b0b9a7cc412d6bc6b79012a2f98e5b
MD5 d852d6bd344eff9f4c5442391e63e54a
BLAKE2b-256 d0e026da35cd662384eeb039ae3f20eb22dbef8f46bdc5b41ed54f98271614d1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page