dr-providers
| Repo Definitions (terms, contracts) | dr-serialize | dr-wire |
|---|
dr-providers makes LLM provider calls through explicit, typed contracts. It supports OpenRouter, OpenAI, Gemini, and Anthropic while keeping call identity, provider translation, transport policy, and outcomes separate.
Package map
| Package | Responsibility |
|---|---|
dr_providers.modeling |
Identity-bearing definitions, configs, requests, routes, controls, and transcripts |
dr_providers.translation |
Pure provider request-body construction and parsed-response translation |
dr_providers.transport |
Credentials, endpoints, timeout policy, and one-invocation HTTP execution over the dr-wire bounded client |
dr_providers.outcomes |
Typed responses, expected failures, invocation evidence, and conformance warnings |
dr_providers.lifecycle |
Invocation classification, serializable retry state, deterministic transitions, and terminal call results |
dr_providers.core |
Shared provider protocol and failure vocabulary |
dr_providers.surfaces.testing |
Deterministic ScriptedProvider for network-free tests |
dr_providers.surfaces.cli |
Optional dr-providers one-shot CLI |
The top-level dr_providers exports are the stable general import surface.
Functional-area module paths primarily make ownership discoverable; they are
not a second compatibility surface.
.defs/terms.toml and its
rendered defs site are the
authoritative reference for that public surface: every export is mapped to a
term there and checked by scripts/check_defs.py. This README illustrates
common paths rather than enumerating them, so an export it does not mention is
supported, not unsupported.
Potential future features records directions this package deliberately does not build today.
Install
dr-providers requires Python 3.12 or newer.
uv add dr-providers
Unless an API key is injected directly, real HTTP calls read the credential selected by their transport policy:
| Provider | Environment variable |
|---|---|
| OpenRouter | OPENROUTER_API_KEY |
| OpenAI | OPENAI_API_KEY |
| Gemini | GEMINI_API_KEY |
| Anthropic | ANTHROPIC_API_KEY |
Python quickstart
This OpenAI example uses the stable package import surface:
from threading import Event
from dr_providers import (
AcceptAllSemanticResponseClassifier,
GenerationControls,
HttpProvider,
MessageRole,
PromptMessage,
ProviderCallOutcomeKind,
ProviderCallRequest,
ProviderCallState,
ProviderKind,
StandardProviderCallRetryPolicy,
Transcript,
openai_responses_config,
policy_for,
run_local_provider_call,
)
config = openai_responses_config(
model="gpt-5-mini",
controls=GenerationControls(token_limit=256),
)
request = ProviderCallRequest(
config=config,
transcript=Transcript(
messages=(
PromptMessage(
role=MessageRole.USER,
content="Say hello in one word.",
),
)
),
)
classifier = AcceptAllSemanticResponseClassifier()
state = ProviderCallState.initial(
request=request,
retry_policy=StandardProviderCallRetryPolicy(),
classifier_identifier=classifier.identifier,
)
with HttpProvider(
policy=policy_for(
ProviderKind.OPENAI,
timeout_seconds=120.0,
connect_timeout_seconds=30.0,
idle_timeout_seconds=90.0,
max_connections=1,
max_keepalive_connections=1,
max_request_bytes=1024 * 1024,
max_response_bytes=8 * 1024 * 1024,
)
) as provider:
result = run_local_provider_call(
provider=provider,
state=state,
classifier=classifier,
cancellation=Event(),
)
evidence = result.completed_invocations[-1].observation.evidence
if result.outcome.kind is ProviderCallOutcomeKind.ACCEPTED:
assert evidence.response is not None
print(evidence.response.text)
else:
print(result.outcome)
Expected transport failures are retained in invocation evidence and classified
into the terminal ProviderCallResult. Unexpected programming or infrastructure
errors can still raise.
CLI
Install and run the one-shot CLI:
uv add 'dr-providers[cli]'
uv run dr-providers --provider openai-responses \
--model gpt-5-mini \
--token-limit 256 \
-m 'Say hello in one word.'
# Anthropic requires --token-limit:
uv run dr-providers --provider anthropic \
--model claude-sonnet-4-6 \
--token-limit 256 \
-m 'Say hello in one word.'
Outcome and evidence boundaries
HttpProvider.invoke() makes at most one provider wire request and returns
versioned serializable ProviderInvocationEvidence. The evidence binds the
request identity hash and transport-policy identity to structured HTTP request
metadata and exactly one response or expected failure. The HTTP request
evidence is the sole owner of the constructed request-body mapping.
run_local_provider_call() classifies each invocation, applies the selected
serializable retry policy through the deterministic lifecycle transition, and
returns the complete ordered ProviderCallResult. The standard policy permits exactly one invocation with no auto-retry.
Transient network/provider failures and contained transport timeouts are
terminal unless the caller selects an explicit custom retry policy. The
standard HTTP provider uses direct synchronous native phase timeouts, so it
observes a timeout only after the local HTTP operation has ended. It owns and
reuses one bounded client; a clean close stops offload admission, drains
offloaded work, stops invocation admission, drains active invocations, and
closes that client once. Invocation admission stays open to every caller, on any
thread, until the offload drain finishes. An exception escaping a drain wait,
such as a keyboard interrupt, aborts the close: the provider still becomes
terminal and releases the executor and client without joining workers, so no
later caller blocks, but the drain does not complete. Connect, write, and pool
phase timeouts and the
response-read idle timeout are each declared explicitly on transport policy and
do not bound the total wall-clock duration of a slow response that keeps
producing bytes.
Every ProviderTransportPolicy and policy_for() call must declare native
connect, write/pool, and response-read idle timeouts, connection-pool limits,
and request/response byte caps.
There are no library-wide implicit sizing defaults. One-shot examples in this
repository configure one open and one keep-alive connection because each run
admits a single invocation. A caller that shares one HttpProvider across
concurrent work must size both connection limits to its own maximum concurrent
invoke() calls.
run_local_provider_call_async() is the asynchronous entry point. It submits
the same synchronous driver to the provider's own executor through
HttpProvider.offload() and awaits the result, so the transport stays one
bounded synchronous client. That executor is created on first offload and sized
from max_connections, which also bounds the client connection pool, so thread
count and pool size cannot disagree. Cancelling the awaiting asyncio task does
not interrupt the offloaded call: the offloaded future is shielded, so
cancellation flows through the cancellation event, and a clean close() drains
admitted offloaded work. Offloaded work must not call close() or offload()
on the provider running it: closing from inside offloaded work waits on that
same work, and offloaded work blocking on a nested offload starves once every
worker is held that way.
ProviderCallState, ProviderRetryInstruction, and ProviderCallResult are
JSON-serializable handoff values. A durable consumer can persist the declared
next state and schedule the instruction's delay before invoking again; restoring
at that boundary produces the same terminal result as the uninterrupted local
driver. The local driver follows transition outputs and performs only its
declared cancellation-aware wait; the deterministic transition owns retry and
terminal decisions.
Cancellation is draining: it starts no successor and retains an active invocation observation if that invocation completes. It does not promise remote provider cancellation or prompt release of provider capacity. Lifecycle values are neutral to storage and workflow runtimes. This package does not provide durable persistence, workflow scheduling, global admission, or exactly-once provider effects.
The exact encoded request body and decompressed response body are bounded by
identity-bearing transport policy limits. Complete in-limit response bodies are
retained as JSON when possible or as text otherwise; over-limit responses retain
no partial body. Failure summary messages are unbudgeted. Wire-path failures
retain the underlying exception traceback in invocation evidence; other
transport failures leave traceback unset. Original HTTP wire bytes are
not retained. The standard HTTP path redacts known credential header names.
Direct ProviderHttpRequestEvidence construction and deserialization remain
trusted-data paths.
Repository validation
The default suite is offline: pytest excludes tests marked live.
uv sync --locked --all-extras
uv run pre-commit install
scripts/pre-check.sh
uv build
Run the complete live matrix without changing the committed wire corpus:
uv run python scripts/run_live_matrix.py
Capturing and promoting replacement corpus data is a separate, deliberate
operation. It stages outside the repository, validates and redacts the
complete five-case capture, then updates data/wire-corpus/:
uv run python scripts/capture_live_corpus.py capture --promote
Metadata
Release files for dr-providers 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dr_providers-0.3.3.tar.gz | 43.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dr_providers-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 107.5 kB
Release files / dr_providers-0.3.3.tar.gz
| Download URL | dr_providers-0.3.3.tar.gz |
|---|---|
| Size | 43.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
56eb1a765b18f25eace892b5fca29fbe71261b01f8fac9bd9628d51b7ee923e6
|
|
BLAKE2b-256 checksum How to use checksums |
a066eed62e11f48862d0b0799ca22c7804dd485855d4847c4f0a731e3b9a2568
|
| 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 Oct 6, 2026.
Transparency logRelease files / dr_providers-0.3.3-py3-none-any.whl
| Download URL | dr_providers-0.3.3-py3-none-any.whl |
|---|---|
| Size | 64.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c1cad6c895ac757cadf262ddf1d4ea3491b03f78519fb2d037f8847aab863e12
|
|
BLAKE2b-256 checksum How to use checksums |
b3852172dac690594e4c8ec96e7e14a16b10a72bcbbe094b9dad3dfa9a1a7d17
|
| 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 Oct 6, 2026.
Transparency log