Source code for cadrumo.adapters.outbound.llm.errors
"""Error hierarchy for the LLM subpackage.All public LLM exceptions inherit from:class:`~llm.LLMError`, which extends:class:`~core.errors.hierarchy.CadrumoError`. Provider adapters surface:exc:`~llm.LLMProviderError` and:exc:`~llm.LLMRateLimitError`, cache and usage storagesurface :exc:`~llm.LLMCacheError`, and strict modelvalidators surface :exc:`~llm.LLMValidationError`."""from__future__importannotationsfromcollections.abcimportMappingfromtypingimportTYPE_CHECKINGfrom....core.errors.hierarchyimportCadrumoError,TerminalPreconditionErrorMixinifTYPE_CHECKING:from....application.operator_actions.modelsimportPreconditionVerdict_LLMPreconditionErrorMixin=TerminalPreconditionErrorMixin[PreconditionVerdict]else:_LLMPreconditionErrorMixin=TerminalPreconditionErrorMixin
[docs]classLLMError(CadrumoError):"""Base exception for public LLM package failures."""
[docs]classLLMProviderError(LLMError):"""Raised when a provider adapter cannot return a completion."""
[docs]classLLMTransientTransportError(LLMProviderError):"""Raised when a dispatch failed on the way to the model, not at it. A connection refused or reset while the runtime loads a model, a 5xx from the runtime, a read timeout on a model still coming up: the request never produced an answer, and the same request sent again may well produce one. Split out of :exc:`~llm.LLMProviderError` because that class covers both directions and the retry decision needs them apart. A 4xx and a malformed 2xx body are deterministic -- the identical request fails identically forever -- so retrying them burns the budget and delays the real refusal. Retryability is declared once, on the registered :class:`~core.errors.error_codes.ErrorCode` for each class, and the transport reads it from there rather than keeping a second list of its own. A subclass rather than a sibling, so every existing ``except LLMProviderError`` handler keeps catching it: the split refines the boundary, it does not move it. """
[docs]classLLMPdfRasterisationError(LLMError):"""Raised when :func:`~llm.rasterise_pdf_pages_to_base64_png` fails."""
[docs]classLLMCacheError(LLMError):"""Raised when :class:`~adapters.persistence.llm.cache.LLMCache` storage fails."""
[docs]classLLMRateLimitError(LLMProviderError):"""Raised when a provider rejects a request because of rate limits. Inherits from :exc:`~llm.LLMProviderError` so callers can catch all provider-boundary failures together. Args: message: Human-readable error message. retry_after_seconds: Optional server-provided retry delay in seconds. """def__init__(self,message:str|None=None,retry_after_seconds:float|None=None,*,context:Mapping[str,object]|None=None,translated_message:str|None=None,)->None:"""Initialize this public contract."""super().__init__(message,context=context,translated_message=translated_message)self.retry_after_seconds=retry_after_seconds
[docs]classLLMConfigError(_LLMPreconditionErrorMixin,LLMError):"""Raised when :class:`~llm.LLMClient` configuration is invalid."""
[docs]classLLMContentionError(_LLMPreconditionErrorMixin,LLMError):"""Raised when this machine has no measured headroom to load the model. The other half of admission control, and a different question from :exc:`LLMBusyError`. Occupancy asks how many loads are already running; this asks whether ONE load fits right now, measured against free memory rather than counted. Both can refuse while the other would admit. Fails closed on an unreadable figure, because "could not tell" is not evidence of headroom and is precisely the state that destroys running work on a machine with no spare device memory. Never retryable. The condition does not decay on a timer -- it decays when the load on the machine changes -- so re-sending on a schedule turns one refusal into several while the memory it is waiting for is still held. """
[docs]classLLMBusyError(_LLMPreconditionErrorMixin,LLMError):"""Raised when an on-host inference slot is not free and the request is refused. The admission half of the local-resource boundary, and deliberately NOT a subclass of :exc:`~llm.LLMProviderError`: nothing failed, and no request reached a runtime. The machine is already running as much inference as it was configured to run at once, and a second concurrent load on consumer hardware is an out-of-memory kill that takes the FIRST read down with it. Distinct from a contention refusal, which reports measured headroom against one model's requirement. This one reports occupancy: the arena is full regardless of what the figures say, because two loads that each fit individually still do not fit together. """
[docs]classLLMConsentError(_LLMPreconditionErrorMixin,LLMError):"""Raised when an off-host read of taxpayer evidence is refused. Deliberately NOT a subclass of :exc:`~llm.LLMConfigError`. A configuration fault says "you routed this wrongly"; this says "you may not send this document off this host", and a caller catching configuration faults to retry at another provider must not silently swallow a confidentiality refusal (``sensitive-financial-data-secure-storage-only``). """
[docs]classLLMValidationError(_LLMPreconditionErrorMixin,LLMError):"""Raised when an LLM-related object fails validation. Pydantic field validators translate this registered error to a plain ``ValueError`` at their callback boundary while preserving the registered error as the cause. Provider and parsing code keeps the typed :class:`~llm.LLMError` hierarchy. """