Skip to main content

llm-pycascade

Resilient cascading LLM inference with automatic failover, circuit breaking, and retry cooldowns.

A faithful Python port of the llm-cascade Rust crate.

Features

  • Cascade inference — define ordered fallback chains across multiple LLM providers
  • Automatic failover — if a provider fails, the next in the cascade is tried immediately
  • Circuit breaking / cooldowns — providers that fail repeatedly are put on exponential backoff
  • Retry-After support — respects HTTP 429 Retry-After headers
  • Failed-prompt persistence — conversations that exhaust all providers are saved as timestamped JSON
  • Multi-provider — OpenAI, Anthropic, Google Gemini, and Ollama out of the box
  • OpenAI-compatible — works with vLLM, LiteLLM, Together AI, and any OpenAI-compatible endpoint
  • Tool/function calling — first-class support across all providers
  • Async — built on httpx and aiosqlite for non-blocking I/O
  • Keyring integration — optional keyring package for secure API key storage

Architecture

┌──────────────┐     ┌──────────────────────────────────────────┐
│   User Code  │────▶│              run_cascade()               │
└──────────────┘     │                                          │
                     │  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
                     │  │ Entry 1 │─▶│ Entry 2 │─▶│ Entry 3 │  │
                     │  │OpenAI   │  │Anthropic│  │ Gemini  │  │
                     │  │gpt-4o   │  │claude.. │  │gemini.. │  │
                     │  └─────────┘  └─────────┘  └─────────┘  │
                     │       ▼            ▼            ▼        │
                     │  ┌─────────────────────────────────────┐ │
                     │  │        Cooldown Tracker (SQLite)     │ │
                     │  │  ┌───────────────────────────────┐  │ │
                     │  │  │ is_on_cooldown() / set_cooldown│  │ │
                     │  │  └───────────────────────────────┘  │ │
                     │  └─────────────────────────────────────┘ │
                     │       ▼            ▼            ▼        │
                     │  ┌─────────────────────────────────────┐ │
                     │  │      Attempt Log (SQLite)            │ │
                     │  │  ┌───────────────────────────────┐  │ │
                     │  │  │ log_attempt(status, latency,  │  │ │
                     │  │  │             tokens)           │  │ │
                     │  │  └───────────────────────────────┘  │ │
                     │  └─────────────────────────────────────┘ │
                     │       ▼ (all failed)                     │
                     │  ┌─────────────────────────────────────┐ │
                     │  │  save_failed_conversation() → JSON   │ │
                     │  └─────────────────────────────────────┘ │
                     └──────────────────────────────────────────┘

Installation

pip

pip install llm-pycascade

uv

uv add llm-pycascade

With optional keyring support

pip install llm-pycascade[keyring]

Configuration

llm-pycascade can be configured from a TOML file (defaults to ~/.config/llm-pycascade/config.toml) or directly from a Python dict — useful on stateless machines where no config file can exist:

from llm_pycascade import config_from_dict

config = config_from_dict({
    "providers": {
        "openai": {"type": "openai", "api_key_env": "OPENAI_API_KEY"},
        "ollama": {"type": "ollama"},
    },
    "cascades": {
        "primary": {
            "entries": [
                {"provider": "openai", "model": "gpt-4o"},
                {"provider": "ollama", "model": "llama3.1"},
            ]
        }
    },
})

The dict uses the same schema as the TOML file (see Configuration for all options, including inline API keys via api_key_literal).

Equivalent TOML:

# config.example.toml — full example configuration

[providers.openai]
type = "openai"
api_key_env = "OPENAI_API_KEY"

[providers.anthropic]
type = "anthropic"
api_key_env = "ANTHROPIC_API_KEY"

[providers.gemini]
type = "gemini"
api_key_env = "GEMINI_API_KEY"

[providers.ollama]
type = "ollama"
# No API key needed for local Ollama

[cascades.primary]
entries = [
    { provider = "openai",    model = "gpt-4o" },
    { provider = "anthropic", model = "claude-sonnet-4-20250514" },
    { provider = "gemini",    model = "gemini-2.0-flash" },
    { provider = "ollama",    model = "llama3.1" },
]

[cascades.fast]
entries = [
    { provider = "anthropic", model = "claude-haiku-4-20250507" },
    { provider = "openai",    model = "gpt-4o-mini" },
    { provider = "ollama",    model = "mistral" },
]

[database]
path = "~/.local/share/llm-pycascade/db.sqlite"

[failure_persistence]
dir = "~/.local/share/llm-pycascade/failed_prompts"

Usage

Basic cascade

import asyncio
from llm_pycascade import (
    AppConfig,
    Conversation,
    init_db,
    load_config,
    run_cascade,
)

async def main():
    config = load_config()
    db_path = config.database.path.replace("~", "/home/user")
    conn = await init_db(db_path)

    conversation = Conversation.single_user_prompt("Explain quantum computing in one sentence.")

    try:
        response = await run_cascade("primary", conversation, config, conn)
        print(response.text_only())
    finally:
        await conn.close()

asyncio.run(main())

With tools

from llm_pycascade import ContentBlock, Message, ToolDefinition

tools = [
    ToolDefinition(
        name="get_weather",
        description="Get the current weather for a city",
        parameters={
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"},
            },
            "required": ["city"],
        },
    )
]

conversation = Conversation.with_tools(
    messages=[Message.user("What's the weather in Tokyo?")],
    tools=tools,
)

response = await run_cascade("primary", conversation, config, conn)

for block in response.content:
    if block.type == ContentBlockType.TEXT:
        print(f"Text: {block.text}")
    elif block.type == ContentBlockType.TOOL_CALL:
        print(f"Tool call: {block.name}({block.arguments})")

Environment variable override

export LLM_PYCASCADE_CONFIG=/path/to/custom/config.toml

Key Types

Type Module Description
run_cascade() cascade Main entry point — runs a named cascade
AppConfig config Top-level configuration (from TOML file or dict)
config_from_dict() config Build an AppConfig from a plain Python dict
load_config() config Load an AppConfig from a TOML file
ProviderConfig config Per-provider settings (type, API key, base URL)
CascadeConfig config Ordered list of provider/model entries
CascadeEntry config Single provider/model pair in a cascade
Conversation models Multi-turn conversation with optional tools
Message models Single message (role + content)
MessageRole models Enum: system, user, assistant, tool
LlmResponse models Provider response with content blocks and usage
ContentBlock models Discriminated union: text or tool_call
ToolDefinition models Tool/function schema definition
ProviderError error Provider-level failure (HTTP, parse, missing key)
CascadeError error All-providers-exhausted failure

Cooldown & Backoff

Failure # Cooldown Duration Capped At
1st 60s —
2nd 120s —
3rd 240s —
4th 480s —
5th 960s —
6th+ 1920s+ 3600s (1h)

Special cases:

  • HTTP 429 with Retry-After: uses the header value directly instead of exponential backoff
  • Cooldown check: the entry is silently skipped if still on cooldown
  • Cooldown expiry: cooldowns are checked against real-time; expired entries are automatically available

License

MIT — see LICENSE.

Metadata

Release files for llm-pycascade 0.2.0

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

Source distribution (sdist)

Source distribution for llm-pycascade 0.2.0
File Size Uploaded Provenance Actions
llm_pycascade-0.2.0.tar.gz 86.6 kB With provenance Details for llm_pycascade-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for llm-pycascade 0.2.0
File Interpreter ABI Platform Actions
llm_pycascade-0.2.0-py3-none-any.whl Python 3 none any Details for llm_pycascade-0.2.0-py3-none-any.whl

Total release size: 116.6 kB

Release files / llm_pycascade-0.2.0.tar.gz

Download URL llm_pycascade-0.2.0.tar.gz
Size 86.6 kB
Tags Source
SHA-256 checksum
How to use checksums
33c13c1a244405574f2326d42f36ee7c01815228146d3351ba5b5b300c806d76
BLAKE2b-256 checksum
How to use checksums
97f5073f6605c9b10f8826ba13d50cf1ee4e4941a0a31eaac982019830ab07a3
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 Aug 17, 2026.

Transparency log

Release files / llm_pycascade-0.2.0-py3-none-any.whl

Download URL llm_pycascade-0.2.0-py3-none-any.whl
Size 30.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d44ddfeb45beeb4df9b54847d0793d1290219f0a2cf2e7663fdbb79187019f54
BLAKE2b-256 checksum
How to use checksums
75cd5c878e5f0be8ad4c49890a97dc53eedb815b27003a83e5aeff3312e404a2
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 Aug 17, 2026.

Transparency log

Security for llm-pycascade 0.2.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for llm_pycascade-0.2.0.tar.gz

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 Aug 17, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

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