Documentation
RelPrim
Reliability primitives for operations that cross process, network or provider boundaries. Retries, timeouts, fallbacks, circuit breakers, validation, idempotency and rate-limit recovery for Python asyncio, with execution reports and structured events.
RelPrim is a small, fully typed Python library that wraps an async call to
something you do not control (a model provider, a payment gateway, an internal
service) in explicit reliability behavior. Every execution produces an
execution report, returned with the value on success and attached to the
exception on failure, and can emit structured events that describe what
happened: which attempts ran, why they failed, whether a fallback was used.
pip install relprim
A first example
from relprim import resilient
async def call_backup(prompt: str) -> str:
return await backup_client.generate(prompt)
@resilient(retries=3, timeout=10, fallback=call_backup)
async def generate(prompt: str) -> str:
return await primary_client.generate(prompt)
result = await generate("Summarize this ticket")
result.value # the string returned by primary_client or call_backup
result.report.attempt_count # how many attempts ran, including the fallback
result.report.to_dict() # a JSON-ready description of the execution
retries=3 allows three retries after the first attempt, timeout=10 bounds
each attempt to ten seconds, and the fallback runs once the retries are
exhausted. If everything fails, OperationExecutionError is raised with the
execution report attached. The decorated function keeps its signature, and type
checkers see the result as OperationResult[str].
The primitives
| Primitive | What it does |
|---|---|
| Retry | Retries selected exceptions with exponential backoff and jitter. |
| Timeouts | Bounds each attempt with an asyncio deadline. |
| Fallbacks | Tries named backup operations, in order, after the primary fails. |
| Circuit breakers | Stops calling a dependency that keeps failing, then probes it. |
| Rate limits | Waits for a provider’s retry-after hint, within a maximum wait. |
| Validation | Rejects results that are well-formed calls but unusable values. |
| Idempotency | Coordinates duplicate calls and replays successful results. |
| Structured events | Describes each execution step to sinks you choose. |
Each primitive is a plain object you construct and pass in. Policies are immutable; circuit breakers and idempotency stores hold state that you share deliberately between operations. You can use one primitive on its own or compose several into a single operation, and RelPrim always applies them in the same documented execution order.
Two ways to configure an operation
The @resilient decorator is the fastest way to adopt RelPrim. Simple
options such as retries=3 or timeout=10 cover most integrations, and each
one has an advanced counterpart that accepts a full policy object.
The builder API composes the same policies explicitly. It suits operations assembled at runtime and gives the strongest static type checking:
from relprim import RetryPolicy, TimeoutPolicy, async_operation, fallback_chain
result = await (
async_operation("generate", primary_client.generate)
.with_retry(RetryPolicy(max_attempts=4))
.with_timeout(TimeoutPolicy(seconds=10))
.with_fallbacks(fallback_chain(("backup", backup_client.generate)))
.run("Summarize this ticket")
)
Both produce the same behavior. Core concepts explains the model they share.
Optional integrations
The core package has no dependencies. Integrations are installed as extras and
imported from their own modules, so import relprim never loads them.
| Extra | Module | Provides |
|---|---|---|
relprim[otel] |
relprim.opentelemetry |
OpenTelemetryEventSink, which adds events to the active span |
relprim[jsonschema] |
relprim.jsonschema |
json_schema_validator |
relprim[pydantic] |
relprim.pydantic |
pydantic_validator |
| (none) | relprim |
SQLiteEventStore, built on the standard library |
What RelPrim is not
RelPrim is a reliability layer inside your application, worker or service. It is not an HTTP client or provider SDK, a workflow engine, a task queue or an observability backend, and it does not replace Temporal, Celery or OpenTelemetry. It does not hide side effects behind guarantees it cannot keep: for example, idempotency coordinates duplicate calls but is not an exactly-once guarantee for external side effects.
Where to go next
- Getting started
Install RelPrim and wrap a flaky call with retry, timeout and fallback.
- Core concepts
Operations, attempts, reports and the fixed execution order.
- Examples
Complete, runnable scenarios: failover, invalid responses, duplicate payments and more.
- Structured events
The event and metadata contract, and how to consume it.
- API reference
Every public name, grouped by domain.
Project
- Source and issues: github.com/bart-rozycki/relprim
- Package: pypi.org/project/relprim
- Supported Python: Python 3.11–3.14
- License: Apache-2.0
- Compatibility promise: stability policy
