Provider Contracts¶
Routing depends on the provider-neutral StructuredProvider port. Provider SDKs
and Instructor remain inside adapters. Capability inspection must pass before
source content is sent.
Start with AI Providers for provider selection, credentials, runtime setup, retries, and production checks.
base ¶
Provider-neutral structured generation port and safe normalized results.
StructuredProvider ¶
Bases: Protocol
Generic async port consumed directly by application and routing code.
inspect_schema ¶
inspect_schema(
response_model: type[T],
) -> SchemaDescriptor
Transform and validate a response schema without sending request data.
estimate_tokens ¶
estimate_tokens(messages: Sequence[ProviderMessage]) -> int
Return a deterministic request-token estimate.
generate
async
¶
generate(
*,
messages: Sequence[ProviderMessage],
response_model: type[T],
generation: GenerationConfig,
retry: RetryConfig,
limiter: ConcurrencyLimiter,
) -> ProviderResponse[T]
Generate one validated response through the shared limiter.
ProviderResponse
dataclass
¶
ProviderResponse(
output: T,
usage: NormalizedUsage | None,
finish_reason: str | None,
provider: str,
model: str,
response_id: str | None,
transport_attempts: int,
validation_attempts: int,
)
Bases: Generic[T]
Validated model output with normalized non-sensitive provider metadata.
truncated
property
¶
truncated: bool
Return whether the normalized finish reason means incomplete output.
require_complete ¶
require_complete() -> ProviderResponse[T]
Fail closed when a provider reports token or length truncation.
ProviderMessage
dataclass
¶
ProviderMessage(
role: Literal["system", "user", "assistant"],
content: str,
)
NormalizedUsage
dataclass
¶
NormalizedUsage(
input_tokens: int, output_tokens: int, total_tokens: int
)
ConcurrencyLimiter ¶
ConcurrencyLimiter(limit: int)
SchemaDescriptor
dataclass
¶
SchemaDescriptor(
canonical_schema_sha256: str,
request_schema_sha256: str,
canonical_schema_json: str,
request_schema_json: str,
)
capabilities ¶
Named provider/model capabilities and strict request-schema transformation.
ModelCapabilities
dataclass
¶
ModelCapabilities(
provider: str,
model: str,
structured_output: bool,
strict_schema: bool,
max_input_tokens: int,
max_output_tokens: int,
supported_generation_settings: frozenset[str],
evidence: CapabilityEvidence,
tested_sdk_version: str,
token_estimator: TokenEstimator = ConservativeTokenEstimator(),
)
One explicit tested or configuration-only provider/model capability row.
token_estimator
class-attribute
instance-attribute
¶
token_estimator: TokenEstimator = field(
default_factory=ConservativeTokenEstimator
)
inspect_schema ¶
inspect_schema(
response_model: type[BaseModel],
) -> SchemaDescriptor
Fail closed, then return canonical and transformed schema hashes.
validate_generation ¶
validate_generation(generation: GenerationConfig) -> None
Reject active unsupported settings and output limits before transport.
estimate_messages ¶
estimate_messages(
messages: tuple[ProviderMessage, ...],
) -> int
Estimate complete role and content transport deterministically.
CapabilityEvidence ¶
Bases: str, Enum
Evidence state for one named provider and model capability row.
ConservativeTokenEstimator
dataclass
¶
ConservativeTokenEstimator()
Dependency-free conservative estimate of one token per UTF-8 byte.
TokenEstimator ¶
Bases: Protocol
Provider-neutral deterministic token estimation contract.
schema_descriptor ¶
schema_descriptor(
response_model: type[BaseModel],
) -> SchemaDescriptor
Build deterministic canonical and strict OpenAI-compatible request schemas.
Supported adapters and evidence¶
All shipped adapter paths have deterministic contract tests. No named live
model/version has completed the protected verification process, so every model
row created by the public facade is configuration-only. This label means the
declared limits are used for validation; it is not a claim that the endpoint,
model behavior, context window, or extraction quality was verified.
| Provider value | Extra | Endpoint configuration | Credential | Evidence |
|---|---|---|---|---|
openai |
openai |
Reviewed OpenAI preset, or OPENAI_BASE_URL |
OPENAI_API_KEY |
configuration-only |
openrouter |
openai |
Reviewed default preset; explicit base_url overrides it |
OPENROUTER_API_KEY |
configuration-only |
vercel |
openai |
Reviewed default preset; explicit base_url overrides it |
AI_GATEWAY_API_KEY |
configuration-only |
custom |
openai |
Explicit base_url required; use HTTPS in production |
SURVEY_SCRIBE_API_KEY |
configuration-only |
azure, azure_openai |
openai |
Endpoint, API version, deployment | API key or token callback | configuration-only |
anthropic |
anthropic |
Dedicated Anthropic adapter | ANTHROPIC_API_KEY |
configuration-only |
Use survey-scribe providers to inspect this same evidence boundary. A
verified row would require a protected contract run against the exact named
provider, model/version, and SDK version. unknown evidence fails closed before
transport.
Configuration examples¶
# OpenAI
SURVEY_SCRIBE_PROVIDER=openai SURVEY_SCRIBE_MODEL=model-id survey-scribe config check
# OpenRouter
SURVEY_SCRIBE_PROVIDER=openrouter SURVEY_SCRIBE_MODEL=model-id survey-scribe config check
# Vercel AI Gateway
SURVEY_SCRIBE_PROVIDER=vercel SURVEY_SCRIBE_MODEL=model-id survey-scribe config check
# Custom OpenAI-compatible HTTPS gateway
survey-scribe config check --provider custom --model model-id --base-url https://gateway.example/v1
# Azure OpenAI-compatible chat completions
survey-scribe config check --provider azure --model deployment-name \
--base-url https://resource.example/ --api-version 2025-04-01-preview
# Anthropic
SURVEY_SCRIBE_PROVIDER=anthropic SURVEY_SCRIBE_MODEL=model-id survey-scribe config check
These commands require the matching credential environment variable. They validate adapter construction but do not make a model request.
Adapter construction¶
InstructorOpenAIProvider is the packaged OpenAI-compatible adapter. Applications
must supply an administrator-owned ModelCapabilities row. A
configuration-only row allows deterministic schema and request checks; it does
not prove that a live provider or model was tested.
from survey_scribe import QuestionnaireRouter
from survey_scribe.providers import CapabilityEvidence, ModelCapabilities
from survey_scribe.providers.openai_compatible import InstructorOpenAIProvider
async def synthetic_completion(**request: object) -> object:
response_model = request["response_model"]
payload = build_synthetic_response_for(response_model)
return payload
capabilities = ModelCapabilities(
provider="test-gateway",
model="configured-model",
structured_output=True,
strict_schema=True,
max_input_tokens=32_000,
max_output_tokens=4_096,
supported_generation_settings=frozenset(
{"temperature", "max_output_tokens", "seed"}
),
evidence=CapabilityEvidence.configuration_only,
tested_sdk_version="synthetic-no-sdk",
)
provider = InstructorOpenAIProvider(
model=capabilities.model,
capabilities=capabilities,
completion=synthetic_completion,
)
router = QuestionnaireRouter(provider)
The injected completion keeps this construction credential-free and avoids an
SDK import. Production applications normally omit it, install the openai extra,
and supply credentials from protected runtime configuration. Direct OpenAI or
LangChain clients are not routing inputs. Adapt them to StructuredProvider.
Named presets are available for OpenAI, OpenRouter, and Vercel AI Gateway.
OpenRouter accepts only the non-secret HTTP-Referer and X-Title headers.
An explicit facade base_url overrides any named preset. Review that destination
as a custom egress route. Custom gateways require an explicit base URL and use
the same header allowlist.
Azure OpenAI uses AzureOpenAIProvider. Configure exactly one API key or
refreshable token callback. Survey Scribe retains and passes the callback to the
Azure SDK without calling it during construction or persisting returned token
values. InstructorAnthropicProvider is available
through the optional anthropic extra. All SDK imports remain lazy.
Generic Azure-compatible gateway headers¶
Applications that need caller-defined gateway headers construct
AzureOpenAIProvider directly and inject it through the existing
StructuredProvider boundary. The facade, CLI, TOML, and environment schema do
not define gateway-specific fields.
import os
from survey_scribe import SurveyScribe
from survey_scribe.providers import CapabilityEvidence, ModelCapabilities
from survey_scribe.providers.azure import AzureOpenAIProvider
def acquire_token() -> str:
return os.environ["SYNTHETIC_AZURE_RUNTIME_TOKEN"]
def sensitive_headers() -> dict[str, str]:
return {
"X-Synthetic-Aux-Key": os.environ["SYNTHETIC_GATEWAY_AUX_KEY"],
}
capabilities = ModelCapabilities(
provider="azure_openai",
model="deployment-name",
structured_output=True,
strict_schema=True,
max_input_tokens=32_000,
max_output_tokens=4_096,
supported_generation_settings=frozenset(
{"temperature", "max_output_tokens", "seed"}
),
evidence=CapabilityEvidence.configuration_only,
tested_sdk_version="application-managed",
)
provider = AzureOpenAIProvider(
deployment=capabilities.model,
azure_endpoint="https://gateway.example/azure",
api_version="api-version",
token_callback=acquire_token,
metadata_headers={"X-Synthetic-Route": "questionnaire-extraction"},
sensitive_headers_callback=sensitive_headers,
required_headers={"X-Synthetic-Route", "X-Synthetic-Aux-Key"},
capabilities=capabilities,
)
client = SurveyScribe(provider)
metadata_headers is only for non-secret, static request metadata. The adapter
copies it during construction. sensitive_headers_callback is a bounded,
synchronous function invoked for each package-owned request attempt. The adapter
copies its returned values into an attempt-local mapping; it does not store or
serialize those values. required_headers
fails safely before transport when a required name is absent.
The application owns token and auxiliary-secret acquisition. Keep these callbacks non-blocking, and do not make a network request from them. A gateway route or header confirms only the configured route; it does not prove the exact backend model or service. The adapter reports configured route identity, not a dynamic backend model identity. Only Microsoft Foundry endpoints that expose the Azure OpenAI-compatible chat-completions API are supported. See Security and API Keys for credential handling rules.
Survey Scribe disables each SDK's internal retry loop and applies only the
configured package retry policy. Transport and structured-validation attempts
are therefore bounded and reported consistently. Anthropic capability rows must
not advertise seed; explicit unsupported settings fail before transport.
No live model row is advertised as verified by this table. A row becomes
verified only after a protected, named model/version contract run records that
evidence. Unknown rows fail closed during schema inspection.
The placeholder build_synthetic_response_for() represents an application test
fixture and is not a package function. No quality claim follows from this example.
openai_compatible ¶
Lazy Instructor-backed adapter for OpenAI-compatible structured endpoints.
InstructorOpenAIProvider ¶
InstructorOpenAIProvider(
*,
model: str,
api_key: str | None = None,
base_url: str | None = None,
capabilities: ModelCapabilities,
completion: Completion | None = None,
default_headers: Mapping[str, str] | None = None,
)
Structured OpenAI-compatible provider with Instructor kept internal.
from_preset
classmethod
¶
from_preset(
preset: OpenAICompatiblePreset,
*,
model: str,
capabilities: ModelCapabilities,
api_key: str | None = None,
base_url: str | None = None,
default_headers: Mapping[str, str] | None = None,
completion: Completion | None = None,
) -> InstructorOpenAIProvider
Construct a reviewed named endpoint or an explicit custom gateway.
generate
async
¶
generate(
*,
messages: Sequence[ProviderMessage],
response_model: type[T],
generation: GenerationConfig,
retry: RetryConfig,
limiter: ConcurrencyLimiter,
) -> ProviderResponse[T]
OpenAICompatiblePreset ¶
Bases: StrEnum
Reviewed endpoint presets plus an explicit custom gateway mode.
azure ¶
Lazy Azure OpenAI adapter with key or refreshable token credentials.
AzureOpenAIProvider ¶
AzureOpenAIProvider(
*,
deployment: str,
azure_endpoint: str,
api_version: str,
capabilities: ModelCapabilities,
api_key: str | None = None,
token_callback: Callable[[], str] | None = None,
metadata_headers: Mapping[str, str] | None = None,
sensitive_headers_callback: Callable[
[], Mapping[str, str]
]
| None = None,
required_headers: Collection[str] = (),
completion: Completion | None = None,
)
Bases: InstructorOpenAIProvider
Instructor-backed Azure adapter with per-attempt gateway headers.
metadata_headers is copied at construction and must contain only
non-secret values. sensitive_headers_callback is invoked immediately
before each package-owned request attempt. required_headers is copied and
checked case-insensitively after the two channels are merged. Caller-defined
headers cannot replace authentication or HTTP transport headers.
Callback or required-header failures become detached, non-retryable provider authentication errors. The callback must be synchronous and non-blocking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metadata_headers
|
Mapping[str, str] | None
|
Non-secret static headers copied during construction. |
None
|
sensitive_headers_callback
|
Callable[[], Mapping[str, str]] | None
|
Synchronous provider of attempt-local secret headers. The returned mapping is validated and not retained. |
None
|
required_headers
|
Collection[str]
|
Header names that must exist after the two channels merge. Names are copied and compared case-insensitively. |
()
|
Raises:
| Type | Description |
|---|---|
TypeError
|
A callback or required-header collection has the wrong shape. |
ValueError
|
Credentials, endpoint settings, or header rules are invalid. |
anthropic ¶
Lazy Instructor-backed Anthropic structured-output adapter.
InstructorAnthropicProvider ¶
InstructorAnthropicProvider(
*,
model: str,
capabilities: ModelCapabilities,
api_key: str | None = None,
completion: Completion | None = None,
)
Bases: InstructorOpenAIProvider
Anthropic adapter with the same normalized provider response contract.