Skip to main content

ProvizElekto

Smart LLM model router. Picks the best model for each call based on context size, rate limits, and capabilities — and retries automatically on failure.

Your app → pz.call(step, fn)               → CallResult
           pz.call_litellm(step, messages) → CallResult
                    ↕  (automatic)
           select → LLM call → report → retry on failure
                    ↕
              proviz-server (Rust)
          rate-limit state · catalog

Key difference from LiteLLM fallback: LiteLLM retries after failure. ProvizElekto picks the right model before the call — skipping models that are rate-limited or near their quota, can't fit the context, or lack required capabilities — then retries with the next eligible model automatically.

Two roles depending on the path

In the regular flow, the server is a pure router — it picks the model and returns credentials; your code makes the actual LLM call.

In the synchronous /complete flow, the server is the caller — it selects, calls the provider, and reports, all in one round-trip — so your code needs no litellm or provider SDK.

In the batch flow, the server becomes the caller:

# Regular: YOUR code calls the LLM
Your app → POST /select → ModelCandidate → your code → Mistral/OpenAI/...
                                                ↓
                                        POST /report

# Synchronous: the SERVER calls the provider for you
Your app → POST /complete → server selects + calls provider + reports → {text, usage, cost}

# Batch: the SERVER calls Mistral on your behalf
Worker A ──┐
Worker B ──┤ POST /batch/submit → server accumulates over window_secs
Worker C ──┘
                    ↓ server → POST Mistral /v1/batch/jobs (50% discount)
                    ↓ server polls until complete
Worker A ──┐
Worker B ──┤ GET /batch/result/{id} → response
Worker C ──┘

The batch path pools requests from all workers into a single Mistral job — the only way to qualify for Mistral's 50% batch discount. No individual worker can do this on its own, so the server acts as the aggregation point and makes the Mistral call itself.

Deployment note: when using batch, the server process (including Docker) must have the Mistral API key env vars set. In the regular flow, API keys only need to be present in the caller's environment.

Features

  • Context-aware selection - don't waste a 128k model on a 1k prompt
  • Proactive quota tracking - sliding-window counters (RPM/TPM/RPD/TPD) plus atomic in-flight reservations; avoids over-booking before any 429 fires
  • Provider-anchored windows - every successful call forwards x-ratelimit-remaining-* headers back to the server; the window floor is clamped to provider reality so internal estimates can't drift below what the provider actually sees
  • Scored selection - multi-component scoring: fast headroom (RPS/RPM/TPM, 25%), daily budget (RPD/TPD, 20%), quality (20%), cost (15%), latency (10%), traffic balance (10%). Over-quota models stay eligible with lower scores — AllModelsExhausted only fires when every model is in reactive 429 cooldown.
  • Tunable weights - override the cost/latency/quality weights above per request (cost_weight/latency_weight/quality_weight on /select and /complete) or once per group (proviz group set-weights); omitting them reproduces the built-in weights exactly, and no model is ever hard-excluded by a weight the way a hard filter would.
  • Measured per-step quality - POST /catalog/step-quality lets a caller push a real, task-specific quality score (e.g. a benchmark pass-rate) for a (model, step) pair, checked before the model's hand-curated global quality_score.
  • Traffic shaping - per-brand traffic_weight steers load proportionally across providers in a 5-minute rolling window; under-served brands get a higher score on the traffic component
  • Capability filtering - hard requirements for function calling, JSON mode
  • Language filtering - restrict selection to models declared to support a given language (ISO 639-1), so you never call a model in the wrong language
  • Quality floor - reject models below a quality threshold per step
  • Model groups - define named pools of models (e.g. "fast-chat", "coding-tier1") and restrict selection to that pool
  • Your keys, your models - curated catalog, no vendor proxy
  • Zero-infra - pip install proviz-elekto auto-starts the Rust server as a subprocess
  • Any language - HTTP API, not a library binding
  • Pluggable storage - SQLite (default) or PostgreSQL

Installation

ProvizElekto consists of a Rust server and various clients.

pip install proviz-elekto          # core only
pip install proviz-elekto[litellm] # + built-in LiteLLM integration

The proviz-server binary is bundled in the wheel.

CLI tool (proviz) is also included:

proviz --help

Documentation

Quickstart

With LiteLLM (recommended)

from proviz_elekto import ProvizElekto

pz = ProvizElekto(db_path="./proviz.db")
# or PostgreSQL: pz = ProvizElekto(database_url=os.environ["DATABASE_URL"])

result = pz.call_litellm(
    step="verdict",
    messages=[{"role": "user", "content": "Summarize this document..."}],
    estimated_tokens=2500,
    requires_json_mode=True,
)
print(result.provider, result.candidate.model_slug, result.total_tokens)
# → mistral mistral-small-latest 312

call_litellm() selects the best available model, calls it, reports the outcome, and retries with the next eligible model on any failure — automatically.

Without litellm (server-side /complete)

The server calls the provider for you — no litellm or provider SDK in your environment. Best for thin/non-Python callers and minimal dependency footprints.

result = pz.complete(
    step="verdict",
    messages=[{"role": "user", "content": "Summarize this document..."}],
    estimated_tokens=2500,
    response_format={"type": "json_object"},
)
print(result.brand, result.model, result.prompt_tokens, result.completion_tokens, result.cost_usd)
# → mistral mistral-small-latest 2487 312 0.00031

complete() does select + provider call + report in a single round-trip. On provider failure it excludes the model and retries the next-best candidate server-side (up to 4 attempts). Pass tools=/tool_choice= to get un-executed tool_calls back and drive the tool loop yourself. Any OpenAI-compatible provider (groq, mistral, ovh, scaleway) works.

The legacy /select + client-side call + /report flow (below) stays fully supported — use it when you want to own the provider call (streaming, custom SDK).

With a custom LLM caller

import anthropic

client = anthropic.Anthropic()

def my_llm(candidate):
    return client.messages.create(
        model=candidate.model_slug,
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
    )

result = pz.call("verdict", my_llm, estimated_tokens=100)
print(result.candidate.brand_slug, result.prompt_tokens)

Pass any callable that accepts a ModelCandidate and returns a response. ProvizElekto wraps it with the same select → report → retry loop.

Low-level API

If you need direct control over selection and reporting:

candidate = pz.select(step="verdict", estimated_tokens=2500)
try:
    response = my_llm_call(candidate)

    # Read provider rate-limit headers (Mistral/OpenAI style; Anthropic style also supported)
    hdrs = getattr(response, "_hidden_params", {}).get("additional_headers") or {}
    rem_req = hdrs.get("x-ratelimit-remaining-requests")
    rem_tok = hdrs.get("x-ratelimit-remaining-tokens")

    pz.report_success(
        candidate.model_id,
        estimated_tokens=candidate.estimated_tokens,  # releases in-flight reservation
        actual_tokens=response.usage.total_tokens,    # improves TPM window accuracy
        remaining_requests=int(rem_req) if rem_req is not None else None,
        remaining_tokens=int(rem_tok)   if rem_tok is not None else None,
    )
    # report_success is fire-and-forget — returns immediately, HTTP call runs in background
except RateLimitError as exc:
    msg = str(exc).lower()
    if "day" in msg or "daily" in msg:
        error_type = "tpd"
    elif "token" in msg:
        error_type = "tpm"
    else:
        error_type = "rpm"
    pz.report_rate_limit(candidate.model_id, error_type)  # synchronous — must complete before retry
except Exception:
    pz.report_error(candidate.model_id, "other")

estimated_tokens in each report call releases the in-flight reservation made at selection time. Omitting it is safe (legacy clients work unchanged) but leaves the in-flight counter inflated until the next selection clears it.

report_success is non-blocking: the HTTP call to proviz runs in a background daemon thread so the caller receives the LLM result without waiting for the round-trip. report_rate_limit and report_error remain synchronous because the model must be blocked in proviz before the retry select() call.

License

Apache-2.0

Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

proviz_elekto-0.16.2-py3-none-win_amd64.whl (4.5 MB view details)

Uploaded Python 3Windows x86-64

proviz_elekto-0.16.2-py3-none-musllinux_1_2_x86_64.whl (5.5 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

proviz_elekto-0.16.2-py3-none-manylinux_2_36_x86_64.whl (5.3 MB view details)

Uploaded Python 3manylinux: glibc 2.36+ x86-64

proviz_elekto-0.16.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.2 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

proviz_elekto-0.16.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (9.7 MB view details)

Uploaded Python 3macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file proviz_elekto-0.16.2-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for proviz_elekto-0.16.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 3bf5b134a74ab7dc0612cb23d8b9a9c3cba9dc7289fdef3fac7aaea205b5d6aa
MD5 762b68bf8382d00789c116cb68085315
BLAKE2b-256 0efa9b729db2e3c86c8579d31030ff4c30388f33a030b5a40037687ab6033fda

See more details on using hashes here.

Provenance

The following attestation bundles were made for proviz_elekto-0.16.2-py3-none-win_amd64.whl:

Publisher: release.yml on JustGui/proviz-elekto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proviz_elekto-0.16.2-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for proviz_elekto-0.16.2-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 94e6c5075d33bbf2c40414e13fc0eb3cfa51e8bb3f2160ac13890d18b696b9e4
MD5 2aca1246902f77afe67e0d7b76940417
BLAKE2b-256 af10295f6c215e842059c02a5ab926a70f5006866343cbc988e55193b20fb5ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for proviz_elekto-0.16.2-py3-none-musllinux_1_2_x86_64.whl:

Publisher: release.yml on JustGui/proviz-elekto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proviz_elekto-0.16.2-py3-none-manylinux_2_36_x86_64.whl.

File metadata

File hashes

Hashes for proviz_elekto-0.16.2-py3-none-manylinux_2_36_x86_64.whl
Algorithm Hash digest
SHA256 af4da4a3b112389a9a820001063785b6ca0ba1fe48c878c89825e482a4c12f98
MD5 5a3c80da8aec0f3df4372f4a3c446b4b
BLAKE2b-256 26c6a87919291d915dcf0511843e1e9fe4f5663391ff9ca1ccbcc0fc70cd0312

See more details on using hashes here.

Provenance

The following attestation bundles were made for proviz_elekto-0.16.2-py3-none-manylinux_2_36_x86_64.whl:

Publisher: release.yml on JustGui/proviz-elekto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proviz_elekto-0.16.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for proviz_elekto-0.16.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2b72317f1afd3a7223057e185bde4edbca47439cb82e062557002584f244e5ee
MD5 ab18788f20e8406a4c6f270c904c9319
BLAKE2b-256 84053d071d38bcbdff9f9b72a0a0a99739ae205e58ab4bb8995c1b061afa8d16

See more details on using hashes here.

Provenance

The following attestation bundles were made for proviz_elekto-0.16.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on JustGui/proviz-elekto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proviz_elekto-0.16.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for proviz_elekto-0.16.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 c855141b882da1c90b8791892cfd8f95c63e4a6630bd4cdf005c633dc1ae2059
MD5 8edf3be7e1f037c049cc5d82e244b8b5
BLAKE2b-256 d5fcbeb1f544e9640dbcc37f6406620fd58781e52054be023826625f46495180

See more details on using hashes here.

Provenance

The following attestation bundles were made for proviz_elekto-0.16.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on JustGui/proviz-elekto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.16.4

5 files

0.16.3

5 files

This release

0.16.2 This release

5 files

0.16.1

5 files

0.16.0

5 files

0.15.6

5 files

0.15.5

5 files

0.15.4

5 files

0.15.3

5 files

0.15.2

5 files

0.15.0

5 files

0.14.2

5 files

0.14.1

5 files

0.14.0

5 files

0.13.1

5 files

0.13.0

5 files

0.12.1

5 files

0.12.0

5 files

0.11.2

5 files

0.11.1

5 files

0.11.0

5 files

0.10.10

5 files

0.10.9

5 files

0.10.8

5 files

0.10.7

5 files

0.10.6

5 files

0.10.5

5 files

0.10.4

5 files

0.10.3

5 files

0.10.2

5 files

0.10.1

5 files

0.10.0

5 files

0.9.7

5 files

0.9.6

5 files

0.9.5

5 files

0.9.4

5 files

0.9.3

5 files

0.9.2

5 files

0.9.1

5 files

0.9.0

5 files

0.8.5

5 files

0.8.4

4 files

0.7.1

4 files

0.7.0

4 files

0.6.1

4 files

0.6.0

4 files

0.5.0

4 files

0.4.8

4 files

0.4.6

4 files

0.4.4

4 files

0.4.3

4 files

0.4.2

4 files

0.4.1

4 files

0.4.0

4 files

0.3.0

4 files

0.2.4

4 files

0.2.3

4 files

0.2.2

4 files

0.2.1

4 files

0.2.0

4 files

0.1.5

4 files

0.1.4

4 files

0.1.3

4 files

Supported by

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