# Copyright (c) 2025 Patrick Motsch # All rights reserved. """AI Audit Log data model for Compliance & AI-Datenfluss tracking. Records metadata (and optionally content) of every AI provider call for compliance, audit, and data-protection reporting. """ import uuid from typing import Optional from pydantic import BaseModel, Field from modules.shared.i18nRegistry import i18nModel from modules.shared.timeUtils import getUtcTimestamp @i18nModel("AI-Audit-Eintrag") class AiAuditLogEntry(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"label": "ID"}, ) timestamp: float = Field( default_factory=getUtcTimestamp, description="Event timestamp (UTC epoch seconds)", json_schema_extra={ "label": "Zeitpunkt", "frontend_type": "timestamp", "frontend_readonly": True, }, ) userId: str = Field( description="ID of the user who triggered the AI call", json_schema_extra={"label": "Benutzer-ID"}, ) username: Optional[str] = Field( default=None, description="Username at time of call (denormalized for display)", json_schema_extra={"label": "Benutzername"}, ) mandateId: str = Field( description="Mandate context of the call", json_schema_extra={"label": "Mandanten-ID"}, ) featureInstanceId: Optional[str] = Field( default=None, description="Feature instance context", json_schema_extra={"label": "Feature-Instanz-ID"}, ) featureCode: Optional[str] = Field( default=None, description="Feature code (e.g. workspace, trustee)", json_schema_extra={"label": "Feature"}, ) instanceLabel: Optional[str] = Field( default=None, description="Human-readable instance label at time of call", json_schema_extra={"label": "Instanz"}, ) aiProvider: str = Field( description="AI provider key (e.g. azure-openai, anthropic)", json_schema_extra={"label": "AI-Provider"}, ) aiModel: str = Field( description="Model name used (e.g. gpt-4o, claude-3.5-sonnet)", json_schema_extra={"label": "AI-Modell"}, ) operationType: Optional[str] = Field( default=None, description="Operation type (chat, embedding, image, tts, …)", json_schema_extra={"label": "Typ"}, ) tokensInput: Optional[int] = Field( default=None, description="Input tokens consumed", json_schema_extra={"label": "Tokens (Input)"}, ) tokensOutput: Optional[int] = Field( default=None, description="Output tokens consumed", json_schema_extra={"label": "Tokens (Output)"}, ) processingTimeMs: Optional[int] = Field( default=None, description="Processing time in milliseconds", json_schema_extra={"label": "Verarbeitungszeit (ms)"}, ) priceCHF: Optional[float] = Field( default=None, description="Cost in CHF (base price, before markup)", json_schema_extra={"label": "Kosten (CHF)"}, ) neutralizationActive: bool = Field( default=False, description="Whether neutralization was active for this call", json_schema_extra={"label": "Neutralisierung"}, ) neutralizationMappingsCount: Optional[int] = Field( default=None, description="Number of neutralization mappings applied", json_schema_extra={"label": "Neutralisierungs-Mappings"}, ) contentStored: bool = Field( default=False, description="Whether full content was persisted (mandate opt-in)", json_schema_extra={"label": "Inhalt gespeichert"}, ) contentInputHash: Optional[str] = Field( default=None, description="SHA-256 hash of the input content", json_schema_extra={"label": "Input-Hash", "frontend_visible": False}, ) contentInputPreview: Optional[str] = Field( default=None, description="First ~200 chars of input (always stored)", json_schema_extra={"label": "Input-Vorschau"}, ) contentOutputPreview: Optional[str] = Field( default=None, description="First ~200 chars of output (always stored)", json_schema_extra={"label": "Output-Vorschau"}, ) contentInputFull: Optional[str] = Field( default=None, description="Full input content (only if mandate opted in)", json_schema_extra={"label": "Vollständiger Input", "frontend_visible": False}, ) contentOutputFull: Optional[str] = Field( default=None, description="Full output content (only if mandate opted in)", json_schema_extra={"label": "Vollständiger Output", "frontend_visible": False}, ) success: bool = Field( default=True, description="Whether the AI call succeeded", json_schema_extra={"label": "Erfolgreich"}, ) errorMessage: Optional[str] = Field( default=None, description="Error message if the call failed", json_schema_extra={"label": "Fehlermeldung"}, )