Skip to main content

sandhi-gateway

Python binding for Sandhi — the metering layer for AI agents. The Rust core, in-process via PyO3: virtual keys, budgets, and neutral usage-event metering with zero network hop. Keep making your own provider calls; hand the response to Sandhi to meter it.

pip install sandhi-gateway   # import as: import sandhi_gateway

The bare name sandhi on PyPI is an unrelated Sanskrit-linguistics library; this binding is published as sandhi-gateway. The crate and GitHub repo are sandhi.

Source builds require Rust 1.88 or newer for the locked dependency graph. Wheels retain the CPython 3.11+ stable ABI and require the GIL; the dependency upgrade does not certify free-threaded execution. The local coverage harness validates CPython 3.11–3.13.

Usage

import json
import sandhi_gateway as sg

gw = sg.Gateway(sink_path="usage.jsonl")           # events append as JSONL (+ in-memory)
gw.add_virtual_key("vk_alice", subject="alice", group="platform", upstream="anthropic")
gw.set_budget("group:platform", 1_000_000)

# ... you make your own provider call and get the raw response JSON ...
event = gw.meter(
    "vk_alice", "anthropic", "claude-x", response_json,
    session_id="conv_7",
)
# event["tokens_in"], event["cache_read_tokens"], event["subject_id"], ...
print(gw.spent("group:platform"))                  # budget recorded
print(gw.check_budget("group:platform", 5000))     # True/False

# Just parse usage (same Rust parsers as the proxy), no attribution:
sg.parse_usage("openai", response_json)            # {tokens_in, tokens_out, cache_*, reasoning_*}

Typed persistent provider runtime

New integrations should reuse a typed provider handle. Its inputs and outputs are Sandhi's versioned neutral chat documents; provider-native JSON is encoded and decoded in Rust.

runtime = sg.ProviderRuntime()
provider = runtime.provider("openrouter", "openai/gpt-4o", api_key)
request = {
    "schema_version": "1",
    "model": "openai/gpt-4o",
    "messages": [{"role": "user", "content": "hello"}],
}
response = json.loads(await provider.complete_json(json.dumps(request)))

async for event_json in provider.stream_json(json.dumps(request)):
    event = json.loads(event_json)  # response_start, text_delta, tool_call_*, usage, finish

The JSON bridge carries typed v1 data, not provider-native JSON. The handle retains its HTTP pool, circuit breaker, retry policy, and timeouts. Invalid documents fail before network I/O; runtime failures raise SandhiProviderError, whose message contains a serialized ProviderErrorV1. runtime.provider() resolves a known endpoint from Sandhi's catalog; runtime.openai_compat() is the explicit custom-endpoint escape hatch.

Canonical streams may emit accounting-only usage events before content or after a numeric usage snapshot. When usage.completeness is "unavailable" and a valid cache_read_observation is present, merge that metadata without adding its counters or replacing the last numeric snapshot/completeness. It is not a second final usage verdict. These events preserve evidence for consumers that stop reading after content; transparent and translated provider wire responses are unchanged.

provider_spec() exposes stable Rust-owned wire facts (canonical slug, aliases, base URL, and model endpoint routing). Static and per-call headers cannot override credentials or other transport-owned framing. Sandhi validates typed request invariants before HTTP; callers still own model selection and policy.

Custom / unknown providers (host escape hatch)

# (a) register a host parser callback for a provider Sandhi doesn't know:
gw.register_parser("myprovider", lambda body: {"tokens_in": 30, "tokens_out": 12,
                                               "cache_creation_tokens": 0, "cache_read_tokens": 0})
gw.meter("vk_alice", "myprovider", "model", response_json)   # uses your callback

# (b) or skip parsing and pass counts directly:
gw.meter_tokens("vk_alice", "myprovider", "model", tokens_in=30, tokens_out=12)

meter() parses the usage at the source (the same cache-split logic as the reverse proxy), attributes it to the virtual key's subject/group, records the budget, emits the neutral usage event (matching usage-event.v1.schema.json), and returns it for local display. Unknown key → KeyError; bad JSON → ValueError.

Usage snapshots (in-process aggregation)

import json

rows = json.loads(gw.usage_snapshot_json("subject"))   # busiest subject first
rows[0]["billable_tokens"]                             # the quantity budgets enforce on
json.loads(gw.usage_snapshot_json("total"))[0]         # one grand-total row
json.loads(gw.usage_snapshot_json("session", 256))     # bound distinct keys to 256

Folds the events recorded so far into usage-aggregate.v1 rows for one dimension — subject (user), group, provider, model, key (virtual_key), session, or total — using the same fold the reverse proxy, the sandhi CLI, and the dashboard read. Neutral units only, never dollars. The optional second argument caps distinct keys (default 1024); everything past it folds into a single "(overflow)" row, so a long-lived process loses per-key detail but never the sum. Unknown dimension → ValueError.

Apache-2.0. See the main README and ADR-0001.

Metadata

Release files for sandhi-gateway 0.14.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for sandhi-gateway 0.14.0
File
sandhi_gateway-0.14.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
sandhi_gateway-0.14.0-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details

Total release size: 11.6 MB

Release files / sandhi_gateway-0.14.0-cp311-abi3-win_amd64.whl

Download URL sandhi_gateway-0.14.0-cp311-abi3-win_amd64.whl
Size 2.6 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
af6e4a650694bde3f64a7d4b7af0b6de1f9efa44b6e6e444760d388ed7f69403
BLAKE2b-256 checksum
How to use checksums
ec613eda62a9b2357ba1c133430777efdb07cced3779d179bdd1c7f1e91a94ac
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 Oct 15, 2026.

Transparency log

Release files / sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.1 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
6b7d4e993104f18ae8509d0bf27c1589f0a7229d0b7667aeb1c7ffb6719fc14b
BLAKE2b-256 checksum
How to use checksums
bfe1e43d5b13534a03f289f0bacd9c9b7480b0af95549a5dd6e9c1a002081cdd
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 Oct 15, 2026.

Transparency log

Release files / sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL sandhi_gateway-0.14.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 3.1 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
bc59bcc0efe1d6d83fac4c0d10fdcdb675a040ad765ed3f7d81b77549c8dbb57
BLAKE2b-256 checksum
How to use checksums
4b5d803b3266790f958c36097cc20beb537d36b8932c0e0e48e45c624df56974
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 Oct 15, 2026.

Transparency log

Release files / sandhi_gateway-0.14.0-cp311-abi3-macosx_11_0_arm64.whl

Download URL sandhi_gateway-0.14.0-cp311-abi3-macosx_11_0_arm64.whl
Size 2.8 MB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
38901b14404add4a4dd79a4eda4da28495552b64b5c636db1dbbb477560a7f76
BLAKE2b-256 checksum
How to use checksums
b35f7fcdb7acd2bb2fe300384e2104d18eae3438773593c546c579663b772240
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 Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.14.0 This release

4 release files

0.13.0

4 release files

0.12.0

4 release files

0.10.1

3 release files

0.10.0

3 release files

0.9.1

3 release files

0.9.0

3 release files

0.8.0

3 release files

0.7.0

3 release files

0.6.1

3 release files

0.6.0

3 release files

0.5.1

3 release files

0.5.0

3 release files

0.4.0

3 release files

0.3.0

3 release files

0.2.1

3 release files

0.2.0

3 release files

0.1.6

3 release files

0.1.5

3 release files

0.1.4

3 release files

0.1.3

3 release files

0.1.2

3 release files

0.1.1

3 release files

0.1.0

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page