dispatch-relay
A provider-agnostic LLM layer with three injected seams. Resolve a model, dispatch a call across any provider, and relay usage to a sink your application owns — instead of the library tracking it for you. Pure-stdlib core, zero runtime dependencies.
pip install dispatch-relay
Who it's for: anyone running more than one LLM provider who wants one consistent dispatch + usage-attribution surface, with the host application in control of config resolution, usage recording, and the actual transport. The "relay, not track" name is the contract: usage is relayed to your sink (a database, a log, nothing) — the library never decides where it lands.
This is the dependency-light foundation increment: the three injected-interface seams + the pure cost model. (Caching and the higher-level façade arrive in a later increment and bring langchain-core etc. with them; this increment is pure-stdlib.)
Renamed from
omega-llm.import omega_llmstill works as a deprecated alias that re-exportsdispatch_relay(with aDeprecationWarning) — migrate toimport dispatch_relay.
The 3 injected seams (dispatch_relay.interfaces)
Each is a @runtime_checkable typing.Protocol (structural typing — a host satisfies the contract WITHOUT importing this library) + a dependency-light default impl.
| Seam | Method(s) | Default impl | A host can back it with |
|---|---|---|---|
ConfigSource |
resolve(key, role, default) → model_id |
DefaultConfigSource (os.getenv(f"{KEY}_MODEL") or default) |
a config store (role → global → env → default) |
UsageSink |
record(*, provider, role, caller, model, tier, input_tokens, output_tokens, cache_read=0, cache_creation=0, cost_usd=0.0, cost_usd_raw=0.0, billing="metered", **extra) → None |
NoOpUsageSink (no-op) |
a usage store / time-series table |
DispatchBackend |
supports(*, provider, role, tier) → bool + dispatch(*, provider, model, messages, tier, role, caller, **kwargs) → LLMResponse |
DefaultDispatchBackend (direct SDK via injected llm_factory; supports→True) |
subscription lanes / custom transports |
cache_read and cache_creation are separate fields on UsageSink.record and on UsageRecord — summing them undercounts Anthropic. billing marks the lane: "metered" ($-tracked SDK) vs "subscription" ($0).
Value types & core-owned facts (dispatch_relay.core)
@dataclass(frozen=True)
class UsageRecord: # input_tokens, output_tokens, cache_read=0, cache_creation=0, model=""
@dataclass(frozen=True)
class LLMResponse: # text, usage: UsageRecord | None, raw: Any
The provider-facts live in dispatch_relay.core (one place, never duplicated per backend):
DEFAULTS: dict[str, str]— the abstract-key → model-id table. The core passesdefault=DEFAULTS[key]intoConfigSource.resolve.extract_usage(provider, raw) → UsageRecord | None— the single place that knows each provider's usage-from-raw shape. Anthropic dual-path: preferraw.response_metadata["usage"](the uncached remainder), fall back toraw.usage_metadataonly if absent (using the wrong one double-counts). The model name comes fromraw.response_metadata["model_name"](both Anthropic and Gemini surface it there — a real LangChainAIMessagehas no top-level.modelattribute), falling back to"". ReturnsNonewhen no usage metadata is present.resolve_usage(response, provider, model) → UsageRecord | None— the locked reconciliation rule: resolveresponse.usage if response.usage is not None else extract_usage(provider, response.raw), then stamp the authoritativemodel— the dispatch call knows the configuredmodel, so the dispatch-arg model always wins over whatever the raw echoed (viadataclasses.replace). ReturnsNoneunchanged when there's no usage (the subscription lane).LLMResponse.usageis a real escape hatch — a backend MAY pre-populate it; else the core extracts.
Both shipped backends return LLMResponse(usage=None); the core extracts usage. The DefaultDispatchBackend derives text from raw.content: a str passes through; an Anthropic content list has its type=="text" blocks joined (non-text blocks skipped); anything else falls back to str(raw). That fallback is only the default backend's degenerate case — real subscription backends (raws are dicts, not strings) construct text explicitly and pass usage=None with billing="subscription".
The pure cost model (dispatch_relay.cost)
estimate_cost(*, prompt, tier="flash", provider="gemini", output_tokens_max=1024, cache_hit_ratio=0.0, role="agents") -> dict — a single source of cost truth. Pricing tables for Gemini / Anthropic / OpenAI, the Gemini Flex 50% rebate gate, Anthropic + OpenAI cache-ratio math. Zero deps.
Usage
from dispatch_relay import estimate_cost, DefaultConfigSource, DEFAULTS
DefaultConfigSource().resolve("gemini_flash", "council", DEFAULTS["gemini_flash"])
# -> "gemini-2.5-flash" (env GEMINI_FLASH_MODEL wins if set)
estimate_cost(prompt=10_000, tier="sonnet", provider="anthropic", output_tokens_max=512)
Authors
Pierre Samson and Claude. MIT licensed.
Metadata
Release files for dispatch-relay 0.0.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 | Provenance | Actions |
|---|---|---|---|---|
| dispatch_relay-0.0.1.tar.gz | 41.9 kB | Without provenance | Details for dispatch_relay-0.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dispatch_relay-0.0.1-py3-none-any.whl | Python 3 | none | any | Details for dispatch_relay-0.0.1-py3-none-any.whl |
Total release size: 72.9 kB
Release files / dispatch_relay-0.0.1.tar.gz
| Download URL | dispatch_relay-0.0.1.tar.gz |
|---|---|
| Size | 41.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d7589dd948a756b01ac32b81d11486f47c3721b21f90855de08a2beb4babf1cb
|
|
BLAKE2b-256 checksum How to use checksums |
3d43c8b383370f18821e79fe11897d9d356d6e305b635a2b1e96b7e9b1844b1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Release files / dispatch_relay-0.0.1-py3-none-any.whl
| Download URL | dispatch_relay-0.0.1-py3-none-any.whl |
|---|---|
| Size | 31.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3961e206df4c04a237e4e0c9e829b87bfdfb7b2befe3e7d6f54ef23830398b74
|
|
BLAKE2b-256 checksum How to use checksums |
3260f4c25443266d38f858e98c8a5ac924ae36b35c7f45c508101943fdbe6935
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Security for dispatch-relay 0.0.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.