qfa.adapters.tracking_llm#

LLM port decorator that records every call attempt for usage tracking.

Classes

TrackingLLMAdapter(inner, usage_repo)

Decorator over an inner LLMPort that records every call attempt.

class qfa.adapters.tracking_llm.TrackingLLMAdapter(inner: LLMPort, usage_repo: UsageRepositoryPort)[source]#

Bases: LLMPort

Decorator over an inner LLMPort that records every call attempt.

Reads tenant + operation from current_call_context. Persists one LLMCallRecord per attempt (success or failure). Recording errors are logged but never raised, so a misbehaving usage repository never breaks an analysis. Connection-class transient errors (OperationalError, InterfaceError) are retried up to 3 times with exponential backoff capped at 0.5s per wait — worst-case added latency under a sustained DB outage is ~0.3s of waits plus 3 fast-failing connection attempts (typically <1s total). Non-transient errors (IntegrityError, ProgrammingError, etc.) skip the retry path and are logged immediately.

Parameters:
async complete(system_message: str, user_message: str, tenant_id: str, response_model: type[T_Response], timeout: float = 20.0) LLMResponse[source]#

Run the inner complete and record the attempt.

If current_call_context is unset the call still goes through — observability never breaks the use case — but the attempt is not persisted and the missing scope is logged at ERROR. In the current wiring this happens only when the orchestrator is invoked outside an HTTP request (e.g. a CLI or test that forgot to set up scopes); HTTP paths set the scope via call_scope_for at the route layer.