osrforge.providers.base
The extraction stages' provider protocol and its request/response types.
Exactly one seam wide: a single generate method taking a structured-output
request. Schema enforcement is the provider's contract —
generate either returns data that validates against request.schema or
raises SchemaValidationError after
its retry budget, so callers trust response.data.
The tracing stage uses two other protocols, each in its own module:
ReadingProvider for Claude's
whole-map readings and judgments, and
DecisionProvider for
Clef's answers about one cell, edge, or key. The content part types here are
shared by all three.
ImagePart
dataclass
ImagePart(png: bytes)
One ordered image content part of a request: a page render, a map image, or a crop.
The bytes are always PNG. Preprocessing and the tracing stage emit PNG,
and each adapter does its own packaging: the Foundry adapter sends a
base64 data URL, the Claude adapter a base64 image block, and the Clef
adapter re-encodes a large crop as JPEG (see
osrforge.providers.clef).
ModelProvider
Bases: Protocol
The one seam between the pipeline and any model vendor.
Any object with a conforming generate method is a provider — no
subclassing, no registration. The provider owns schema enforcement:
validate with ensure_schema
(and retry as needed) before returning, so callers can trust
response.data.
Examples:
from osrforge.contracts.run import TokenUsage
from osrforge.providers.base import ModelRequest, ModelResponse, ensure_schema
class MyVendorProvider:
def generate(self, request: ModelRequest) -> ModelResponse:
data = my_vendor_call(request.system, request.parts, request.schema)
ensure_schema(data, request.schema, request.tag)
return ModelResponse(data=data, usage=TokenUsage(), model_id="my-model")
generate
generate(request: ModelRequest) -> ModelResponse
Run one structured-output completion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
ModelRequest
|
System text, ordered content parts, and the JSON Schema the response must satisfy. |
required |
Returns:
| Type | Description |
|---|---|
ModelResponse
|
The parsed, schema-validated response with token usage. |
Raises:
| Type | Description |
|---|---|
ProviderError
|
On transport, auth, or rate-limit exhaustion. |
SchemaValidationError
|
If no schema-valid response was obtained within the provider's retry budget. |
ModelRequest
dataclass
ModelRequest(tag: str, system: str, parts: tuple[TextPart | ImagePart, ...], schema: dict[str, object])
One structured-output completion request.
Attributes:
| Name | Type | Description |
|---|---|---|
tag |
str
|
A short stable label like |
system |
str
|
The system text. |
parts |
tuple[TextPart | ImagePart, ...]
|
The ordered text and image content parts. |
schema |
dict[str, object]
|
The JSON Schema the response |
fingerprint
fingerprint() -> str
Return the request's identity hash, which names the extraction stages' fixtures.
The fingerprint is the sha256 hex of the canonical JSON (sorted keys,
compact separators, UTF-8) of the request with each image part replaced
by {"sha256": ..., "bytes": ...}, so it hashes each image's PNG
bytes. A re-encoded page render changes it. The tracing providers'
recording keys hash decoded pixels instead
(pixel_digest), because the
tracing stage draws its crops at run time and a different PNG encoder
must still find the recording.
Returns:
| Type | Description |
|---|---|
str
|
A 64-character sha256 hex digest. |
ModelResponse
dataclass
ModelResponse(data: object, usage: TokenUsage, model_id: str)
TextPart
dataclass
TextPart(text: str)
One ordered text content part of a request.
A page's extracted text layer, or prompt text like a batch header.
ensure_schema
Validate response data against a request's JSON Schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
object
|
The parsed response JSON. |
required |
schema
|
dict[str, object]
|
The JSON Schema from the request. |
required |
context
|
str
|
Where the data came from, for the error message (e.g. a tag or a fixture path). |
required |
Raises:
| Type | Description |
|---|---|
SchemaValidationError
|
If the data doesn't satisfy the schema. |
pixel_digest
Return the sha256 hex digest of an image's decoded pixels.
The digest covers the image's size and its pixels converted to RGBA, so
two PNG files that encode the same picture differently have the same
digest, an image without an alpha channel and the same image with every
pixel opaque have the same digest, and a change of alpha changes it. A
JPEG is lossy, so it never shares a digest with the PNG it was made from:
the Clef key hashes the source PNG's pixels, whatever the adapter sends.
The tracing providers' recording keys use it (see
ReadingRequest.key and
DecisionRequest.key),
where ModelRequest.fingerprint
hashes the PNG bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
png
|
bytes
|
The PNG bytes. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A 64-character sha256 hex digest of |
str
|
the RGBA pixel bytes, row by row. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the bytes aren't an image Pillow can decode. |