gateway/modules/datamodels/datamodelAiAudit.py
2026-04-16 23:13:05 +02:00

150 lines
5.3 KiB
Python

# 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", "fk_target": {"db": "poweron_app", "table": "User"}},
)
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", "fk_target": {"db": "poweron_app", "table": "Mandate"}},
)
featureInstanceId: Optional[str] = Field(
default=None,
description="Feature instance context",
json_schema_extra={"label": "Feature-Instanz-ID", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}},
)
featureCode: Optional[str] = Field(
default=None,
description="Feature code (e.g. workspace, trustee)",
json_schema_extra={"label": "Feature", "fk_target": {"db": "poweron_app", "table": "Feature", "column": "code"}},
)
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"},
)