qfa.domain.usage_models#
Usage tracking + aggregation domain models.
Split out from qfa.domain.models to keep the broad request/response
models separate from the usage-tracking cluster. The cluster has two halves:
Persistence primitives —
Operation,CallStatus,CallContext,LLMCallRecord. These describe a single LLM-call attempt and the context propagated to the tracking adapter.Aggregations —
DistributionStats,UsageMetrics,OperationStats,TenantUsageStats,TenantStats,OperationUsageStats. These are the per-tenant / per-operation views returned by/v1/usageendpoints.
All models are immutable (frozen) Pydantic models per ADR-001.
Classes
|
Per-call context propagated via ContextVar from orchestrator to tracker. |
|
Outcome of a single LLM call attempt. |
|
Statistical distribution summary over a numeric column. |
|
A single recorded LLM call attempt for usage and cost tracking. |
|
Orchestrator operations that produce LLM calls. |
|
Per-operation usage stats nested inside |
|
Per-operation (or grand-total) usage stats with nested per-tenant breakdown. |
|
Per-tenant usage stats nested inside an operation block. |
|
Per-tenant (or grand-total) usage stats — per-invocation + per-LLM-call. |
|
Aggregated stats over a set of records. |
- class qfa.domain.usage_models.Operation(*values)[source]#
Bases:
StrEnumOrchestrator operations that produce LLM calls.
Stored as plain strings in the database; new members can be added without a DB migration.
UNKNOWNis a sentinel for backfilled rows from before per-operation tracking was introduced and must never be removed (removal would orphan historical rows).- ANALYZE = 'analyze'#
- SUMMARIZE = 'summarize'#
- SUMMARIZE_AGGREGATE = 'summarize_aggregate'#
- ASSIGN_CODES = 'assign_codes'#
- DETECT_SENSITIVE = 'detect_sensitive'#
- UNKNOWN = 'unknown'#
- class qfa.domain.usage_models.CallStatus(*values)[source]#
Bases:
StrEnumOutcome of a single LLM call attempt.
- OK = 'ok'#
- ERROR = 'error'#
- class qfa.domain.usage_models.CallContext(*, tenant_id: str, operation: Operation, call_id: UUID)[source]#
Bases:
BaseModelPer-call context propagated via ContextVar from orchestrator to tracker.
- Variables:
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class qfa.domain.usage_models.LLMCallRecord(*, tenant_id: str, operation: Operation, call_id: UUID, timestamp: datetime, call_duration_ms: int, model: str, input_tokens: int = 0, output_tokens: int = 0, cost_usd: Decimal = Decimal('0'), status: CallStatus, error_class: str | None = None)[source]#
Bases:
BaseModelA single recorded LLM call attempt for usage and cost tracking.
Recorded once per LLM-call attempt — success or failure.
cost_usdand token counts are populated only for successful attempts; failures record zeros pluserror_class.- Variables:
tenant_id (str) – Tenant that made the call.
operation (Operation) – Public orchestrator operation that issued the call.
call_id (UUID) – Correlation ID linking all LLM calls made within a single API invocation. Shared across the fan-out of LLM calls from one
call_scope, enabling per-invocation aggregation in usage reports.timestamp (datetime) – UTC wall-clock when the call started.
call_duration_ms (int) – Wall-clock duration of the call in milliseconds.
model (str) – The LLM model used.
input_tokens (int) – Number of input (prompt) tokens; 0 on failure.
output_tokens (int) – Number of output (completion) tokens; 0 on failure.
cost_usd (Decimal) – Estimated cost in USD; 0 on failure.
status (CallStatus) – Outcome of the attempt.
error_class (str | None) –
type(exc).__name__whenstatus == CallStatus.ERROR;Noneotherwise. Enforced bymodel_validator.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- status: CallStatus#
- class qfa.domain.usage_models.DistributionStats(*, avg: float, min: float, max: float, p5: float, p95: float, total: int)[source]#
Bases:
BaseModelStatistical distribution summary over a numeric column.
Used uniformly for
call_duration(milliseconds),input_tokens, andoutput_tokens.totalis the sum of the underlying values in the window and is identical between the per-invocation and per-LLM-call views — both sum the same raw rows, just regrouped.- Variables:
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class qfa.domain.usage_models.UsageMetrics(*, total_calls: int, failed_calls: int = 0, total_cost_usd: Decimal = Decimal('0'), call_duration: DistributionStats, input_tokens: DistributionStats, output_tokens: DistributionStats)[source]#
Bases:
BaseModelAggregated stats over a set of records.
Whether the records are per-LLM-call rows or per-invocation roll-ups is fixed by the containing field, not by this class.
UsageMetricsis used directly for the per-LLM-callllm_call_statsblock onTenantUsageStatsandOperationStats, and as the base class for the per-invocation totals onTenantUsageStats/OperationStats.Per-field semantics are in the
Field(description=...)below and surface in the OpenAPI schema atGET /docs.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- call_duration: DistributionStats#
- input_tokens: DistributionStats#
- output_tokens: DistributionStats#
- class qfa.domain.usage_models.OperationStats(*, total_calls: int, failed_calls: int = 0, total_cost_usd: Decimal = Decimal('0'), call_duration: DistributionStats, input_tokens: DistributionStats, output_tokens: DistributionStats, operation: Operation, llm_call_stats: UsageMetrics)[source]#
Bases:
UsageMetricsPer-operation usage stats nested inside
TenantUsageStats.Inherits all metric fields from
UsageMetrics(per-invocation semantics) and adds theoperationdiscriminator plus a parallelllm_call_statsblock giving the per-LLM-call view for the same operation.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- llm_call_stats: UsageMetrics#
- class qfa.domain.usage_models.TenantUsageStats(*, total_calls: int, failed_calls: int = 0, total_cost_usd: Decimal = Decimal('0'), call_duration: DistributionStats, input_tokens: DistributionStats, output_tokens: DistributionStats, tenant_id: str | None = None, llm_call_stats: UsageMetrics, operations: tuple[OperationStats, ...] = ())[source]#
Bases:
UsageMetricsPer-tenant (or grand-total) usage stats — per-invocation + per-LLM-call.
Inherits per-invocation metric fields from
UsageMetricsand adds the per-LLM-call view, the per-operation breakdown, and the optionaltenant_id(Noneis the grand-total sentinel used by/v1/usage/all/by-tenant).Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- llm_call_stats: UsageMetrics#
- operations: tuple[OperationStats, ...]#
- class qfa.domain.usage_models.TenantStats(*, total_calls: int, failed_calls: int = 0, total_cost_usd: Decimal = Decimal('0'), call_duration: DistributionStats, input_tokens: DistributionStats, output_tokens: DistributionStats, tenant_id: str, llm_call_stats: UsageMetrics)[source]#
Bases:
UsageMetricsPer-tenant usage stats nested inside an operation block.
Mirrors
OperationStatsbut for the inverse hierarchy used by/v1/usage/all/by-operation: eachOperationUsageStatscarries a list of these blocks, one per tenant that has activity for that operation in the window. Inherits per-invocation metric fields fromUsageMetricsand adds thetenant_iddiscriminator plus the parallelllm_call_statsblock for the per-LLM-call view of the same (operation, tenant) slice.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- llm_call_stats: UsageMetrics#
- class qfa.domain.usage_models.OperationUsageStats(*, total_calls: int, failed_calls: int = 0, total_cost_usd: Decimal = Decimal('0'), call_duration: DistributionStats, input_tokens: DistributionStats, output_tokens: DistributionStats, operation: Operation | None = None, llm_call_stats: UsageMetrics, tenants: tuple[TenantStats, ...] = ())[source]#
Bases:
UsageMetricsPer-operation (or grand-total) usage stats with nested per-tenant breakdown.
Inverse hierarchy of
TenantUsageStats: top-level aggregation is by orchestrator operation, with a list of per-tenant blocks underneath. Used by/v1/usage/all/by-operation.operationisNoneon the grand-total entry (cross-operation, cross-tenant).Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- llm_call_stats: UsageMetrics#
- tenants: tuple[TenantStats, ...]#