Provider setup
The extraction stages' model access goes through the
ModelProvider protocol — one
generate method taking a structured-output request. The shipped adapter is
FoundryProvider (Azure AI
Foundry, OpenAI-compatible chat surface); other vendors are drop-ins behind
the same seam. The tracing stage takes two providers of its own, described
under the tracing stage's providers.
Environment variables
The CLI builds the Foundry adapter from OSRFORGE_FOUNDRY_* environment
variables (an osr-forge-specific prefix, deliberately not AZURE_OPENAI_*, to
avoid colliding with other tools' conventions):
| Variable | Azure meaning | Required |
|---|---|---|
OSRFORGE_FOUNDRY_ENDPOINT |
The Azure OpenAI resource endpoint (https://<resource>.openai.azure.com) |
yes |
OSRFORGE_FOUNDRY_DEPLOYMENT |
The model deployment name | yes |
OSRFORGE_FOUNDRY_API_KEY |
The API key. Omit to use Entra ID via DefaultAzureCredential, which needs the osr-forge[entra] extra |
no |
Key vs. Entra auth
With OSRFORGE_FOUNDRY_API_KEY set, the adapter authenticates with the key.
Without it, the adapter uses Entra ID through DefaultAzureCredential — sign
in with az login (or any credential source DefaultAzureCredential
supports) and install the extra:
As a library
from osrforge.providers.foundry import FoundryProvider, FoundrySettings
provider = FoundryProvider(FoundrySettings.from_env())
FoundrySettings can also be
constructed explicitly when a host app manages configuration itself. The
provider owns retries, rate-limit backoff, and schema enforcement: generate
either returns data that validates against the request's JSON Schema or
raises after its retry budget.
What the provider sees
Preprocessing yields each page's extracted text plus a rendered PNG, and every
extraction request interleaves them per page. The pipeline never depends on
any provider's native PDF ingestion — that is what makes adapters cheap — and
no vendor SDK is imported outside the adapters in providers/.
The tracing stage's providers
The tracing stage reads printed maps with two model services, each behind
its own protocol, and passes them to the stage as
TracingProviders. Every other
stage keeps the ModelProvider above.
Claude, the reading provider
ClaudeProvider fills the
ReadingProvider role through
Anthropic's Messages API. It reads its settings from the environment:
| Variable | Meaning | Required |
|---|---|---|
ANTHROPIC_API_KEY |
The API key | yes |
OSRFORGE_CLAUDE_LEDGER |
The path of the Claude cost ledger, a JSON file the provider creates entries in | yes |
OSRFORGE_CLAUDE_MODEL |
The model id. One model serves every kind of request. Default claude-sonnet-5-5 |
no |
OSRFORGE_CLAUDE_LEDGER_STOP |
The spend in dollars past which the provider refuses to send a request. Default: the ledger file's own stop_usd |
no |
from osrforge.providers.claude import ClaudeProvider, ClaudeSettings
provider = ClaudeProvider(ClaudeSettings.from_env())
Before each request the provider reserves the request's worst-case cost on the ledger and refuses the request when that would pass the stop. After the answer it settles the reservation at what the answer's usage cost. The ledger file is locked while it's read and written, so several conversions can share one ledger.
Clef, the decision provider
ClefProvider fills the
DecisionProvider role
through Cloudflare's Workers AI REST endpoint. It reads its settings from
the environment and your wrangler login:
| Variable | Meaning | Required |
|---|---|---|
OSRFORGE_CLEF_ACCOUNT_ID |
The Cloudflare account id | yes |
OSRFORGE_CLEF_LEDGER |
The path of the Clef cost ledger | yes |
OSRFORGE_CLEF_TOKEN |
A Cloudflare API token with Workers AI access. Omit to use the token of your wrangler login |
no |
OSRFORGE_CLEF_LEDGER_STOP |
The spend in dollars past which the provider refuses to send a request. Default: the ledger file's own stop_usd |
no |
from osrforge.providers.clef import ClefProvider, ClefSettings
provider = ClefProvider(ClefSettings.from_env())
The provider never writes the token anywhere: not to a log, a recording, or an error message. It sends each crop as PNG and rebuilds a request body over 100 KB with the crop as JPEG at quality 90, because Workers AI rejects bodies far smaller than its documented limit. Each request's recording records which format it used. The ledger works as Claude's does, with one entry per run that counts the requests settled against it.
Clef has no version pin, so the model behind a request can change between
runs. Before a sweep, the drift check
(drift_check) sends a sample
of recorded requests again with their exact stored bodies and stops the
sweep if a top answer changes or any probability moves by more than 0.03.