Swap between LLM providers (Groq, OpenAI, Anthropic) through one interface, with automatic fallback and optional schema validation.
Project description
swapllm
Swap between LLM providers — Groq, OpenAI, Anthropic — through one interface, with automatic fallback on rate-limit/timeout/outage and optional schema-validated responses. Write your call site once against Router.complete(), and changing providers (or falling back to a second one when the first is down) is a config change, not a code change.
Install
pip install swapllm
Quickstart
from swapllm import Router, GroqProvider, OpenAIProvider, AnthropicProvider
router = Router(
providers=[
GroqProvider(api_key=..., model="llama-3.1-70b"),
OpenAIProvider(api_key=..., model="gpt-4o-mini"),
AnthropicProvider(api_key=..., model="claude-haiku-4-5"),
],
fallback_order=["groq", "openai", "anthropic"],
)
response = router.complete(
messages=[{"role": "user", "content": "..."}],
schema=MyPydanticSchema, # optional — validates before returning
)
Fallback behavior
Router.complete() tries each provider in fallback_order, in order, and returns the first successful completion.
Triggers fallback to the next provider:
- Rate limit (
429) - Timeout, or the request never reaching the provider at all
- A
5xxfrom the provider - A response that fails schema validation (when
schema=is given) — a provider "responding" with unusable content isn't success
Does not trigger fallback — propagates immediately instead:
ProviderRequestError(e.g.400/401/403, a malformed request, a bad API key) — this is almost always a caller misconfiguration, and switching providers would silently mask it rather than surface it- A plain
ValueErrorfrom malformed input (e.g. multiple or misplacedsystemmessages) — same reasoning: a different provider won't fix bad input
There is also no same-provider retry: a rate-limited or 5xx-ing provider won't un-limit itself in the next few seconds, so a failure always advances to the next provider, never the same one again. If every provider in fallback_order fails, Router.complete() raises AllProvidersFailedError with every provider's individual failure reason attached — it never silently returns None or an empty string.
Schema validation
schema= is optional. Pass a Pydantic model and Router.complete() parses the winning provider's text as JSON, validates it against the schema, and returns the validated model instance instead of a raw string. A leading/trailing markdown code fence (```json ... ```) is stripped first, since providers without strict JSON mode routinely wrap structured output that way.
Validation failure is treated as a provider failure, not a caller error — it triggers fallback to the next provider like any other retryable failure, since a provider returning malformed JSON isn't meaningfully different from a provider returning nothing at all.
Skip schema= entirely for the common case of just wanting a chat reply back as a string.
Testing / contributing
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
Tests are contract tests against mocked provider HTTP responses — no real API calls are made, and none are required to run the suite.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swapllm-0.1.0.tar.gz.
File metadata
- Download URL: swapllm-0.1.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1886c5ec204781313310cd7991adcb3cdac7b48776ed8c9223bbe475d4e4d46b
|
|
| MD5 |
478f73d0b382d6a66144d55f3987116a
|
|
| BLAKE2b-256 |
adabb440b4addf28fa047850621af62a712f3f68e56b3fe33229d62e8c7eefbb
|
File details
Details for the file swapllm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: swapllm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
941e8f97dfa7b4d33837d9fbfdd53faaad8859a549e7d9ffa6a5b986ae84ea78
|
|
| MD5 |
03ae5510e28e8d82ca6cdc82c5d12403
|
|
| BLAKE2b-256 |
e15d9f5fc06fdb9bd9e9607aca1f8e793061d284d1469da6f9ecd10535599f71
|