refactor: voice-service entfernt (verschoben nach breakpilot-core)
This commit is contained in:
@@ -1,149 +0,0 @@
|
||||
"""
|
||||
Audit Models - DSGVO-compliant logging
|
||||
NO PII in audit logs - only references and metadata
|
||||
|
||||
Erlaubt: ref_id (truncated), content_type, size_bytes, ttl_hours
|
||||
Verboten: user_name, content, transcript, email
|
||||
"""
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Optional, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
import uuid
|
||||
|
||||
|
||||
class AuditAction(str, Enum):
|
||||
"""Audit action types."""
|
||||
# Session actions
|
||||
SESSION_CREATED = "session_created"
|
||||
SESSION_CONNECTED = "session_connected"
|
||||
SESSION_CLOSED = "session_closed"
|
||||
SESSION_EXPIRED = "session_expired"
|
||||
|
||||
# Audio actions (no content logged)
|
||||
AUDIO_RECEIVED = "audio_received"
|
||||
AUDIO_PROCESSED = "audio_processed"
|
||||
|
||||
# Task actions
|
||||
TASK_CREATED = "task_created"
|
||||
TASK_QUEUED = "task_queued"
|
||||
TASK_STARTED = "task_started"
|
||||
TASK_COMPLETED = "task_completed"
|
||||
TASK_FAILED = "task_failed"
|
||||
TASK_EXPIRED = "task_expired"
|
||||
|
||||
# Encryption actions
|
||||
ENCRYPTION_KEY_VERIFIED = "encryption_key_verified"
|
||||
ENCRYPTION_KEY_INVALID = "encryption_key_invalid"
|
||||
|
||||
# Integration actions
|
||||
BREAKPILOT_CALLED = "breakpilot_called"
|
||||
PERSONAPLEX_CALLED = "personaplex_called"
|
||||
OLLAMA_CALLED = "ollama_called"
|
||||
|
||||
# Security actions
|
||||
RATE_LIMIT_EXCEEDED = "rate_limit_exceeded"
|
||||
UNAUTHORIZED_ACCESS = "unauthorized_access"
|
||||
|
||||
|
||||
class AuditEntry(BaseModel):
|
||||
"""
|
||||
Audit log entry - DSGVO compliant.
|
||||
NO PII is stored - only truncated references and metadata.
|
||||
"""
|
||||
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
||||
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
# Action identification
|
||||
action: AuditAction
|
||||
namespace_id_truncated: str = Field(
|
||||
...,
|
||||
description="First 8 chars of namespace ID",
|
||||
max_length=8,
|
||||
)
|
||||
|
||||
# Reference IDs (truncated for privacy)
|
||||
session_id_truncated: Optional[str] = Field(
|
||||
default=None,
|
||||
description="First 8 chars of session ID",
|
||||
max_length=8,
|
||||
)
|
||||
task_id_truncated: Optional[str] = Field(
|
||||
default=None,
|
||||
description="First 8 chars of task ID",
|
||||
max_length=8,
|
||||
)
|
||||
|
||||
# Metadata (no PII)
|
||||
content_type: Optional[str] = Field(default=None, description="Type of content processed")
|
||||
size_bytes: Optional[int] = Field(default=None, description="Size in bytes")
|
||||
duration_ms: Optional[int] = Field(default=None, description="Duration in milliseconds")
|
||||
ttl_hours: Optional[int] = Field(default=None, description="TTL in hours")
|
||||
|
||||
# Technical metadata
|
||||
success: bool = Field(default=True)
|
||||
error_code: Optional[str] = Field(default=None)
|
||||
latency_ms: Optional[int] = Field(default=None)
|
||||
|
||||
# Context (no PII)
|
||||
device_type: Optional[str] = Field(default=None)
|
||||
client_version: Optional[str] = Field(default=None)
|
||||
backend_used: Optional[str] = Field(default=None, description="personaplex, ollama, etc.")
|
||||
|
||||
@staticmethod
|
||||
def truncate_id(full_id: str, length: int = 8) -> str:
|
||||
"""Truncate ID for privacy."""
|
||||
if not full_id:
|
||||
return ""
|
||||
return full_id[:length]
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"id": "audit-123",
|
||||
"timestamp": "2026-01-26T10:30:00Z",
|
||||
"action": "task_completed",
|
||||
"namespace_id_truncated": "teacher-",
|
||||
"session_id_truncated": "session-",
|
||||
"task_id_truncated": "task-xyz",
|
||||
"content_type": "student_observation",
|
||||
"size_bytes": 256,
|
||||
"ttl_hours": 168,
|
||||
"success": True,
|
||||
"latency_ms": 1250,
|
||||
"backend_used": "ollama",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AuditCreate(BaseModel):
|
||||
"""Request to create an audit entry."""
|
||||
action: AuditAction
|
||||
namespace_id: str = Field(..., description="Will be truncated before storage")
|
||||
session_id: Optional[str] = Field(default=None, description="Will be truncated")
|
||||
task_id: Optional[str] = Field(default=None, description="Will be truncated")
|
||||
content_type: Optional[str] = Field(default=None)
|
||||
size_bytes: Optional[int] = Field(default=None)
|
||||
duration_ms: Optional[int] = Field(default=None)
|
||||
success: bool = Field(default=True)
|
||||
error_code: Optional[str] = Field(default=None)
|
||||
latency_ms: Optional[int] = Field(default=None)
|
||||
device_type: Optional[str] = Field(default=None)
|
||||
backend_used: Optional[str] = Field(default=None)
|
||||
|
||||
def to_audit_entry(self) -> AuditEntry:
|
||||
"""Convert to AuditEntry with truncated IDs."""
|
||||
return AuditEntry(
|
||||
action=self.action,
|
||||
namespace_id_truncated=AuditEntry.truncate_id(self.namespace_id),
|
||||
session_id_truncated=AuditEntry.truncate_id(self.session_id) if self.session_id else None,
|
||||
task_id_truncated=AuditEntry.truncate_id(self.task_id) if self.task_id else None,
|
||||
content_type=self.content_type,
|
||||
size_bytes=self.size_bytes,
|
||||
duration_ms=self.duration_ms,
|
||||
success=self.success,
|
||||
error_code=self.error_code,
|
||||
latency_ms=self.latency_ms,
|
||||
device_type=self.device_type,
|
||||
backend_used=self.backend_used,
|
||||
)
|
||||
Reference in New Issue
Block a user