Skip to main content

dr-providers

Typed LLM provider-call kernel for OpenRouter, OpenAI, Gemini, and Anthropic: one Provider Call Config, Request, Transport Policy, and no-throw Transport Outcome vocabulary across providers. Requires Python 3.12+.

Ecosystem

dr-providers is the typed LLM-provider HTTP transport kernel, with an optional [serve] FastAPI facade for localhost HTTP callers. It builds Provider Call Definition, Config, and Request Identity Documents — each carrying its own full 64-char SHA-256 Identity Hash — through dr-serialize. Its neighboring repos are dr-serialize, dr-graph, dr-platform, dr-code, whetstone-ai, and unitbench. Whetstone-ai / dr-platform, dr-graph's graph runner, and unitbench playgrounds are consumers.

The vocabulary sheet (source: .defs/vocab.html) is the authoritative statement of the provider-call transport contract this repo implements: the terms, the guarantees, what is in and out of scope, and the mapping from each term to the exported names.

Install

pip install dr-providers

Or with uv:

uv add dr-providers

Authentication

Set the API key env var for whichever provider(s) you call:

export OPENROUTER_API_KEY="sk-or-..."
export OPENAI_API_KEY="sk-..."
export GEMINI_API_KEY="..."
export ANTHROPIC_API_KEY="sk-ant-..."

Quickstart

from dr_providers import (
    ApiKeyEnv,
    GenerationControls,
    HttpProvider,
    MessageRole,
    ProviderBaseUrl,
    ProviderCallRequest,
    ProviderTransportPolicy,
    ProviderTransportResponse,
    PromptMessage,
    ReasoningEffort,
    Transcript,
    openrouter_chat_config,
)

# A Provider Call Config is a complete validated assignment of one
# Provider Call Definition; it carries a full SHA-256 Identity Hash.
config = openrouter_chat_config(
    model="openai/gpt-4o-mini",
    controls=GenerationControls(reasoning=ReasoningEffort.LOW),
)

# A Provider Call Request is one Config reference + one Transcript.
request = ProviderCallRequest(
    config=config,
    transcript=Transcript(
        messages=(
            PromptMessage(
                role=MessageRole.USER, content="Say hello in one word."
            ),
        )
    ),
)

# Transport policy (credentials, base URL, timeout, native retry) is
# separate and excluded from identity. Native retry defaults to zero.
policy = ProviderTransportPolicy(
    api_key_env=str(ApiKeyEnv.OPENROUTER),
    base_url=str(ProviderBaseUrl.OPENROUTER),
)

with HttpProvider(policy=policy) as provider:
    outcome = provider.complete(request)  # no-throw typed outcome
    if isinstance(outcome, ProviderTransportResponse):
        print(outcome.text)

complete returns a closed no-throw Provider Transport Outcome (ProviderTransportResponse | ProviderTransportFailure); expected outcomes never raise. invoke instead returns a stable ProviderInvocationEvidence artifact binding the request + policy identities to the outcome and the complete least-processed raw request and success/failure bodies (authorization headers and credentials are never persisted).

HttpProvider is a context manager. In the default (owned) mode each wire call runs on its own short-lived httpx.Client and daemon thread under a per-invocation wall-clock deadline, so one call's deadline breach tears down only that call's connection pool and never disturbs another. If you inject your own client it is shared and left open for you to manage; the transport cannot forcibly cancel a wedged call on a caller-owned sync client (see the HttpProvider docstring).

Provider matrix

Presets in dr_providers.config build a Provider Call Definition and materialize its Config, fixing each provider's Model Route (provider, protocol, model), the token-limit parameter, and the reasoning wire shape. Base URL and API key env var live on the separate ProviderTransportPolicy; policy_for(kind, ...) derives them from the DEFAULT_BASE_URLS / DEFAULT_API_KEY_ENVS per-provider maps (both in dr_providers.policy), each overridable:

Preset Provider Protocol Reasoning wire shape
openrouter_chat_config openrouter chat_completions reasoning: {"effort": ...} object
openai_chat_config openai chat_completions reasoning_effort: ... field
openai_responses_config openai responses reasoning: {"effort": ...} object
gemini_chat_config gemini chat_completions reasoning_effort: ... field (OpenAI-compat endpoint)
anthropic_messages_config anthropic anthropic_messages output_config: {"effort": ...} object

Both the OpenAI-compatible / OpenRouter chat_completions path and the Anthropic anthropic_messages path are first-class, each usable with a custom base URL via the transport policy.

ReasoningEffort is a shared enum (NONE, MINIMAL, LOW, MEDIUM, HIGH, XHIGH); each Definition's reasoning_shape constraint determines how build_payload() serializes it on the wire. Anthropic Messages accepts only low/medium/high, so NONE, MINIMAL, and XHIGH are rejected with a ControlValidationError rather than silently coerced. Anthropic also requires max_tokens, so anthropic_messages_config marks TOKEN_LIMIT a required control (the CLI and serve facade default it to 4096 when unset).

OpenAI Responses bodies are normalized from wire output[] parts into text, typed no-text failures, and content-free diagnostics (ResponsesDiagnostics). See the vocabulary sheet for the authoritative per-name mapping of the parse/diagnostic surface.

Testing with ScriptedProvider

ScriptedProvider implements the same Provider interface as HttpProvider but scripts outcomes with no network:

from dr_providers import ScriptedOutcome, ScriptedProvider

provider = ScriptedProvider([ScriptedOutcome(text="scripted reply")])
outcome = provider.complete(request)
assert outcome.text == "scripted reply"

Public API

Import stable symbols from the top-level package:

from dr_providers import (
    ProviderCallConfig,
    ProviderCallRequest,
    ProviderTransportPolicy,
    HttpProvider,
    ReasoningEffort,
)

dr_providers.__all__ is the authoritative export list; the vocabulary sheet maps every one of those names to the contract term it implements, so this README does not repeat the per-name detail. HttpProvider loads lazily so importing the pure modules (route, controls, config, request, response, outcome, policy, evidence) never pulls in httpx.

CLI

The [cli] extra installs a typer CLI, exposed as the dr-providers console script, for one-shot provider calls:

pip install 'dr-providers[cli]'
dr-providers --provider openrouter --model openai/gpt-4o-mini -m "Say hello."

The console script is always installed, but the CLI itself requires the [cli] extra; running it without that extra prints an install hint and exits nonzero.

Serve facade

An optional FastAPI facade (the [serve] extra) exposes the kernel over HTTP for non-Python callers:

uv run python -m dr_providers.serve serve

Release notes live in the changelog; note that 0.2.0 is a complete rewrite with no API compatibility with the 0.1.x query client.

Development

uv sync --frozen
uv run pre-commit install
uv run pre-commit run --all-files

Live verification matrix

The default uv run pytest run is fully offline (addopts = "-m 'not live'"). A live-marked matrix in tests/live/test_live_matrix.py exercises the five presets against real provider endpoints:

uv run pytest -m live

Each case skips (not fails) when its API key env var (OPENROUTER_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY) is unset, so this is safe to run without every provider configured. Successful calls overwrite data/wire-corpus/<provider>_<protocol>.json with the raw response body; tests/test_wire_corpus.py re-parses those bodies offline on every normal run.

Audit corpus ground truth

This repo includes a small audit-output corpus and curated ground-truth normalization artifacts under data/audit-corpus/. Regenerate the parsed audit and analysis files with:

uv run python scripts/generate_audit_ground_truth.py \
  --corpus-dir data/audit-corpus \
  --output-dir data/audit-corpus/ground-truth

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dr_providers-0.2.1.tar.gz (39.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dr_providers-0.2.1-py3-none-any.whl (52.8 kB view details)

Uploaded Python 3

File details

Details for the file dr_providers-0.2.1.tar.gz.

File metadata

  • Download URL: dr_providers-0.2.1.tar.gz
  • Upload date:
  • Size: 39.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dr_providers-0.2.1.tar.gz
Algorithm Hash digest
SHA256 75c467cb2a55c3c08f898210b0e850ab38e8e106ebd2f71def7e837534ac2f78
MD5 087d792a6d08f0458c86923ae829c965
BLAKE2b-256 ccd7135cdac222dbe6832f1c3db0c3bd5223f0505dc90542db3602f672813c32

See more details on using hashes here.

Provenance

The following attestation bundles were made for dr_providers-0.2.1.tar.gz:

Publisher: release.yaml on danielle-rothermel/dr-providers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dr_providers-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: dr_providers-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 52.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dr_providers-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 16cb17eff0aa59583a08e483560b1419626e99b0366c838f01e061b3a474db60
MD5 0945adca2f22d5e6f3eef5505a9e951d
BLAKE2b-256 78b51eced6614663d125d1c6a1854e7b068d1b23136da5647746f12319945171

See more details on using hashes here.

Provenance

The following attestation bundles were made for dr_providers-0.2.1-py3-none-any.whl:

Publisher: release.yaml on danielle-rothermel/dr-providers

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page