1from __future__ import annotations
2
3from dataclasses import dataclass, field
4from typing import TYPE_CHECKING
5
6# Re-export shared types from contracts — the canonical definitions.
7from lexigram.contracts.ai.governance import AIAuditEvent, AuditEventType # noqa: F401
8
9if TYPE_CHECKING:
10 from datetime import datetime
11
12# ---------------------------------------------------------------------------
13# Query / aggregation models (governance-specific, not in contracts)
14# ---------------------------------------------------------------------------
15
16
17@dataclass
18class AuditQuery:
19 """Filter criteria for querying audit events.
20
21 All fields are optional — ``None`` means *no constraint* on that axis.
22
23 Attributes:
24 start: Inclusive lower bound on timestamp.
25 end: Inclusive upper bound on timestamp.
26 event_types: Restrict to these event types.
27 user_id: Restrict to a specific user.
28 model: Restrict to a specific model.
29 provider: Restrict to a specific provider.
30 status: Restrict to a specific status string.
31 limit: Maximum number of results to return.
32 offset: Number of results to skip (for pagination).
33 """
34
35 start: datetime | None = None
36 end: datetime | None = None
37 event_types: list[AuditEventType] | None = None
38 user_id: str | None = None
39 model: str | None = None
40 provider: str | None = None
41 status: str | None = None
42 limit: int = 1000
43 offset: int = 0
44
45
46@dataclass
47class AuditSummary:
48 """Aggregated audit statistics for a given query period.
49
50 Attributes:
51 total_events: Total number of events matching the query.
52 total_spend: Sum of ``cost`` across matching events.
53 total_tokens: Sum of ``tokens`` across matching events.
54 denied_count: Events where status is ``"denied"``.
55 by_model: Event count per model.
56 by_user: Event count per user.
57 by_event_type: Event count per event type.
58 """
59
60 total_events: int = 0
61 total_spend: float = 0.0
62 total_tokens: int = 0
63 denied_count: int = 0
64 by_model: dict[str, int] = field(default_factory=dict)
65 by_user: dict[str, int] = field(default_factory=dict)
66 by_event_type: dict[str, int] = field(default_factory=dict)
67
68
69# ---------------------------------------------------------------------------
70# Persistence protocol
71# ---------------------------------------------------------------------------