Skip to content

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).

png instance-attribute

png: bytes

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 survey or probe.image-limits — a pipeline stage name or capability-probe id, never free prose. It names fixture files, attributes usage, and participates in the fingerprint (it is part of request identity).

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 data must satisfy.

tag instance-attribute

tag: str

system instance-attribute

system: str

parts instance-attribute

parts: tuple[TextPart | ImagePart, ...]

schema instance-attribute

schema: dict[str, object]

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)

One completion's parsed, schema-valid result.

Attributes:

Name Type Description
data object

The parsed JSON, already validated against the request's schema.

usage TokenUsage

Token consumption as the provider reported it.

model_id str

The model identifier the service returned.

data instance-attribute

data: object

usage instance-attribute

usage: TokenUsage

model_id instance-attribute

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.

text instance-attribute

text: str

ensure_schema

ensure_schema(data: object, schema: dict[str, object], context: str) -> None

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

pixel_digest(png: bytes) -> str

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 f"{width}x{height}:" followed by

str

the RGBA pixel bytes, row by row.

Raises:

Type Description
ValueError

If the bytes aren't an image Pillow can decode.