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
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 dr_providers-0.3.1.tar.gz.
File metadata
- Download URL: dr_providers-0.3.1.tar.gz
- Upload date:
- Size: 42.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae37f3c45ded1c1f4317107e5b07417c0842a882869d5468f3058e70ed28d62b
|
|
| MD5 |
aee502cd0bda1cc5b9a426c39a86d9ef
|
|
| BLAKE2b-256 |
092179bee03226f0484f3ca7b3f34a5404c4b04dc1a7d513dbf668ec2df989a1
|
Provenance
The following attestation bundles were made for dr_providers-0.3.1.tar.gz:
Publisher:
release.yaml on danielle-rothermel/dr-providers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dr_providers-0.3.1.tar.gz -
Subject digest:
ae37f3c45ded1c1f4317107e5b07417c0842a882869d5468f3058e70ed28d62b - Sigstore transparency entry: 2431129154
- Sigstore integration time:
-
Permalink:
danielle-rothermel/dr-providers@8e3e1faa07f3c8738b843ffcb97196528f70a4aa -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/danielle-rothermel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@8e3e1faa07f3c8738b843ffcb97196528f70a4aa -
Trigger Event:
push
-
Statement type:
File details
Details for the file dr_providers-0.3.1-py3-none-any.whl.
File metadata
- Download URL: dr_providers-0.3.1-py3-none-any.whl
- Upload date:
- Size: 63.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45c1d691a837625f258e8eb846c9f396b6cafc61fe0f5b1b6ae67f8b0f10aa1c
|
|
| MD5 |
844c6f4cc3ae20c0eb3e64ecbfa7387a
|
|
| BLAKE2b-256 |
e76855a20dcf0df00a71e884159a5ea14945ac4c3261c58ffd890604320ffe9d
|
Provenance
The following attestation bundles were made for dr_providers-0.3.1-py3-none-any.whl:
Publisher:
release.yaml on danielle-rothermel/dr-providers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dr_providers-0.3.1-py3-none-any.whl -
Subject digest:
45c1d691a837625f258e8eb846c9f396b6cafc61fe0f5b1b6ae67f8b0f10aa1c - Sigstore transparency entry: 2431129247
- Sigstore integration time:
-
Permalink:
danielle-rothermel/dr-providers@8e3e1faa07f3c8738b843ffcb97196528f70a4aa -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/danielle-rothermel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@8e3e1faa07f3c8738b843ffcb97196528f70a4aa -
Trigger Event:
push
-
Statement type: