Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agent-fallback-sentinel
Lightweight production middleware for resilient LLM calls: retries, fallback routing, circuit-breaking, and rigid schema validation.
Lightweight, production-grade middleware for resilient LLM calls — retries, automated fallback routing, circuit-breaking, and rigid schema validation.
LLM APIs rate-limit. They return malformed JSON. They go down at 3am.
When that happens mid-agent-workflow, your entire pipeline dies — and your users see a 500.
agent-fallback-sentinel wraps your LLM calls in a resilience layer that:
KeyError: 'action' in prod.Zero dependencies beyond pydantic.
pip install agent-fallback-sentinel
(Coming to PyPI. For now: pip install git+https://github.com/Jamie643/agent-fallback-sentinel.git)
from pydantic import BaseModel
from agent_fallback_sentinel import AgentSentinel
class Output(BaseModel):
status: str
action: str
sentinel = AgentSentinel(max_retries=2, cooldown=1.0)
result = sentinel.execute_with_fallback(
primary_fn=call_openai,
fallback_fn=call_anthropic,
schema=Output,
)
# -> {"status": "success", "action": "retry_job"}
That's it. If call_openai rate-limits or returns garbage, call_anthropic runs automatically. If both fail, you get a SentinelCircuitBreaker with every captured error inside.
tenacity?tenacity is great at retries. It doesn't know about:
Think of this as tenacity + pydantic + failover, purpose-built for LLM calls.
| Argument | Default | Description |
|---|---|---|
max_retries | 2 | Retry attempts for the primary provider (excludes the first try). |
cooldown | 1.0 | Seconds between retries on a given provider. |
fallback_max_retries | 1 | Retry attempts for the fallback provider. |
sleep_fn | time.sleep | Injectable for testing — pass lambda _: None to run instantly. |
on_event | None | Callable[[str, dict], None] for observability hooks. |
on_event| Event | When |
|---|---|
attempt | Before each attempt on a provider. |
failover | When primary is exhausted and we switch to fallback. |
success | After a provider returns a schema-valid result. |
trip | When the circuit breaker opens. |
max_retries + 1 times.fallback_max_retries + 1 times.SentinelCircuitBreaker.errors contains every captured exception, in order.See examples/openai_anthropic_failover.py for a complete OpenAI → Anthropic failover with schema validation.
git clone https://github.com/Jamie643/agent-fallback-sentinel.git
cd agent-fallback-sentinel
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
pytest
aexecute_with_fallback)MIT — see LICENSE.
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
pydanticEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pydantic>=2.0 | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.4; extra == "dev" | runtime |
pytest-covEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pytest-cov>=4.1; extra == "dev" | runtime |
mypyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | mypy>=1.8; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | ruff>=0.4; extra == "dev" | runtime |
pre-commitEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.9",
"evaluation": "NOT_EVALUATED"
} | pre-commit>=3.6; extra == "dev" | runtime |
bdist_wheel · 7.19 KB · Python py3
sdist · 7.92 KB · Python source
Shared repository data · GitHub
Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:570bb3fa537ee4304edfdd0f7e751dc67992ab389c3ba02e1459fff0d14915eb