cadrumo.adapters.outbound.llm.providers.base module¶
Provider adapter contract for the LLM outbound subpackage.
Defines the normalized request and response shapes that every concrete provider
adapter (Anthropic, OpenAI, Gemini, and local) consumes and produces,
so the higher-level LLMClient can stay
provider-agnostic. Adapters live in sibling modules under
llm.providers.
- class ProviderRequest(**data)[source]¶
Bases:
BaseModelNormalized inbound payload passed to a provider adapter.
- Variables:
request_id – Stable opaque hash identifying the public request, suitable for cross-referencing cache and usage records.
model – Fully resolved provider model identifier.
prompt – Rendered prompt text sent to the provider.
system – Optional system prompt prepended to the conversation.
max_tokens – Maximum number of output tokens to request.
temperature – Sampling temperature in the inclusive range
[0.0, 1.0], orNonemeaning the parameter is OMITTED from the wire rather than sent at some default. The distinction is load-bearing: a vendor’s newer models reject an explicitly-senttemperaturewith a 400, so “no preference” cannot be expressed as a number.timeout_s – Per-request timeout in seconds.
images – On-host-prepared image inputs for a multimodal read (empty for a text-only request), each carrying its base64 payload AND its declared media type – a cloud provider needs the type on the wire, so the adapter cannot receive a bare string. Transient and in-memory only; an adapter forwards them to a vision model and they are never persisted (sensitive-financial-data-secure-storage-only).
- Parameters:
data (Any)
- request_id: str¶
- model: str¶
- prompt: str¶
- system: str | None¶
- max_tokens: int¶
- temperature: float | None¶
- timeout_s: int¶
- images: tuple[MultimodalImageInput, ...]¶
- class ProviderCompletion(**data)[source]¶
Bases:
BaseModelNormalized provider response returned to the public client.
- Variables:
text – Generated text payload.
model – Provider model that actually served the request (may differ from the requested model when a vendor performs upstream routing).
input_tokens – Provider-reported prompt token count.
output_tokens – Provider-reported output token count.
provider_request_id – Provider-native request or message id when available, otherwise
None.
- Parameters:
data (Any)
- text: str¶
- model: str¶
- input_tokens: int¶
- output_tokens: int¶
- provider_request_id: str | None¶
- class ProviderAdapter[source]¶
Bases:
ABCThe interface every concrete provider adapter implements.
Public because it is a shared contract, not an implementation detail: the client type-hints against it, each vendor module subclasses it, and the outbound-adapter tests build their own conforming double. A name that says private while four packages depend on it teaches the wrong thing about where the boundary is.
Concrete subclasses bind
providerto a member ofLLMProviderand implementcomplete()to translate the normalizedProviderRequestinto a vendor-specific call.- Variables:
provider – Identifier of the LLM vendor this adapter speaks to.
supports_images – Whether
complete()actually forwardsProviderRequest.imagesto the vendor. Declared as DATA on the class rather than left to each adapter to remember, and enforced once at the client’s single dispatch point – the same reasoningpost_provider_request()documents for the transport boundary. Defaults toFalseso a newly added adapter is text-only until it has genuinely implemented the image path; the failure of the opposite default is silent and unbounded.
-
provider:
LLMProvider¶
- unsupported_parameters(model)[source]¶
Return the request parameters this adapter must NOT put on the wire for model.
The second half of the capability axis
supports_imagesopened. Both defects are one shape – a request field the transport assumed was universal – and they failed in opposite directions:imageswas silently DROPPED by adapters that could not carry it, andtemperatureis unconditionally SENT to models that reject it. One was dropped without telling anyone; one is sent without asking.Declared per MODEL rather than per adapter because that is where vendors actually differ: the same Anthropic adapter serves models that accept
temperatureand models that refuse it, so an adapter-wide flag cannot express the constraint.Defaults to the empty set – an adapter carries every parameter until it declares otherwise. That is the opposite default from
supports_images, deliberately: silently dropping an image makes a model answer from nothing, which is unbounded harm, while omitting a sampling parameter falls back to the vendor’s own default. The restrictive default belongs where the failure is silent, not where it is merely a different temperature.
- abstractmethod async complete(request)[source]¶
Execute a completion request against the underlying provider.
- Parameters:
request (
ProviderRequest) – Normalized request payload.- Return type:
- Returns:
Normalized
ProviderCompletionresponse.
- raise_rate_limit(*, provider_name, model, retry_after)[source]¶
Raise a normalized rate-limit error with parsed retry hint.
- Parameters:
- Raises:
LLMRateLimitError – Always raised with the parsed retry hint.
- Return type:
- parse_provider_response(response, *, provider_name, response_model)[source]¶
Validate a successful provider response at the outbound boundary.
- Parameters:
- Return type:
TypeVar(ProviderResponse, bound=BaseModel)- Returns:
The validated provider response.
- Raises:
LLMProviderError – When a 2xx response body is malformed for its provider.
- require_provider_response_item(items, *, provider_name, item_name)[source]¶
Return the first required response item or raise a declared provider error.
- Parameters:
- Return type:
TypeVar(ProviderResponseItem)- Returns:
The first response item.
- Raises:
LLMProviderError – When a successful provider response has no primary item.
- async post_provider_request(client, url, *, provider_name, model, logger, headers=None, json)[source]¶
POST to a provider endpoint, mapping transport failure to the typed boundary.
LLMProviderErroris the declared transport/provider boundary of this subpackage, and the Anthropic adapter maps its SDK’s connection and timeout failures onto it. The httpx-based adapters did not agree: Gemini caughthttpx.RequestErrorwhile OpenAI and the local runtime calledclient.postbare, so an unreachable endpoint surfaced as a rawConnectError/ConnectTimeoutfrom two of the four providers. The error TYPE, and therefore every caller’s recovery path, depended on which provider happened to be configured — for one and the same failure.Routing every httpx provider through this one call makes the boundary a property of the transport rather than of each adapter’s memory to catch, and keeps the provider context (name, model) attached to the failure.
- Parameters:
client (
AsyncClient) – The provider adapter’s ownhttpx.AsyncClient.url (
str) – Fully resolved endpoint.provider_name (
str) – Human-readable provider label for log and error text.model (
str) – Model identifier, included in the log context.logger (
Logger) – Adapter logger for transport diagnostics.headers (
Mapping[str,str] |None) – Optional request headers (auth material, API keys).json (
object) – JSON-serialisable request body.
- Return type:
Response- Returns:
The provider’s
httpx.Response, unexamined — status dispatch remainscheck_http_error()’s job.- Raises:
LLMTransientTransportError – When the request never produced a response (connection refused, DNS failure, timeout, protocol error). Typed as the TRANSIENT half of the boundary because that is what the failure is: nothing was decided at the model, and a local runtime that is still loading a multi-gigabyte model refuses connections for exactly as long as the load takes.
httpx.TimeoutExceptionis aRequestErrorsubclass, so a read timeout arrives here too.
- check_http_error(response, *, provider_name, model, logger)[source]¶
Raise a normalized error for a non-2xx LLM HTTP response.
A 429 raises a rate-limit error carrying the parsed
Retry-Afterhint; any other non-2xx status raises a provider error. Shared by the OpenAI, Gemini, and local adapters, whose status-dispatch is otherwise identical.The 5xx and 4xx branches raise DIFFERENT types, and the split is the whole basis of the retry decision. A 5xx is the server saying it failed to answer a request it accepted, which the same request may survive on a second attempt; a 4xx is the server saying the request itself is wrong, which the same request will never survive. Retrying the second kind spends a bounded budget on a certainty and delays the real refusal.
- Parameters:
- Raises:
LLMRateLimitError – On HTTP 429.
LLMTransientTransportError – On any 5xx status.
LLMProviderError – On any other non-2xx status.
- Return type: