292 lines
12 KiB
Python
292 lines
12 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
CommCoach Feature - Data Models.
|
|
Pydantic models for coaching contexts, sessions, messages, tasks, scores, and user profiles.
|
|
"""
|
|
from typing import Optional, List, Dict, Any
|
|
from pydantic import BaseModel, Field
|
|
from enum import Enum
|
|
|
|
from modules.datamodels.datamodelBase import PowerOnModel
|
|
import uuid
|
|
|
|
|
|
# ============================================================================
|
|
# Enums
|
|
# ============================================================================
|
|
|
|
class CoachingContextStatus(str, Enum):
|
|
ACTIVE = "active"
|
|
PAUSED = "paused"
|
|
ARCHIVED = "archived"
|
|
COMPLETED = "completed"
|
|
|
|
|
|
class CoachingContextCategory(str, Enum):
|
|
LEADERSHIP = "leadership"
|
|
CONFLICT = "conflict"
|
|
NEGOTIATION = "negotiation"
|
|
PRESENTATION = "presentation"
|
|
FEEDBACK = "feedback"
|
|
DELEGATION = "delegation"
|
|
CHANGE_MANAGEMENT = "changeManagement"
|
|
CUSTOM = "custom"
|
|
|
|
|
|
class CoachingSessionStatus(str, Enum):
|
|
ACTIVE = "active"
|
|
COMPLETED = "completed"
|
|
CANCELLED = "cancelled"
|
|
|
|
|
|
class CoachingMessageRole(str, Enum):
|
|
USER = "user"
|
|
ASSISTANT = "assistant"
|
|
SYSTEM = "system"
|
|
|
|
|
|
class CoachingMessageContentType(str, Enum):
|
|
TEXT = "text"
|
|
AUDIO_TRANSCRIPT = "audioTranscript"
|
|
SYSTEM_NOTE = "systemNote"
|
|
|
|
|
|
class CoachingTaskStatus(str, Enum):
|
|
OPEN = "open"
|
|
IN_PROGRESS = "inProgress"
|
|
DONE = "done"
|
|
SKIPPED = "skipped"
|
|
|
|
|
|
class CoachingTaskPriority(str, Enum):
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
|
|
|
|
class CoachingScoreTrend(str, Enum):
|
|
IMPROVING = "improving"
|
|
STABLE = "stable"
|
|
DECLINING = "declining"
|
|
|
|
|
|
# ============================================================================
|
|
# Database Models
|
|
# ============================================================================
|
|
|
|
class CoachingContext(PowerOnModel):
|
|
"""A coaching context/dossier representing a topic the user is working on."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
userId: str = Field(description="Owner user ID (strict ownership)")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
instanceId: str = Field(description="Feature instance ID")
|
|
title: str = Field(description="Context title, e.g. 'Conflict with team lead'")
|
|
description: Optional[str] = Field(default=None, description="Short description")
|
|
category: CoachingContextCategory = Field(default=CoachingContextCategory.CUSTOM)
|
|
status: CoachingContextStatus = Field(default=CoachingContextStatus.ACTIVE)
|
|
goals: Optional[str] = Field(default=None, description="JSON array of goals [{id, text, status, createdAt}]")
|
|
insights: Optional[str] = Field(default=None, description="JSON array of AI insights [{id, text, sessionId, createdAt}]")
|
|
metadata: Optional[str] = Field(default=None, description="JSON object with flexible metadata")
|
|
sessionCount: int = Field(default=0)
|
|
taskCount: int = Field(default=0)
|
|
lastSessionAt: Optional[str] = Field(default=None)
|
|
rollingOverview: Optional[str] = Field(default=None, description="AI summary of older sessions for long context history")
|
|
rollingOverviewUpToSessionCount: Optional[int] = Field(default=None, description="Session count covered by rollingOverview")
|
|
|
|
|
|
class CoachingSession(PowerOnModel):
|
|
"""A single coaching conversation session within a context."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
contextId: str = Field(description="FK to CoachingContext")
|
|
userId: str = Field(description="Owner user ID")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
instanceId: str = Field(description="Feature instance ID")
|
|
status: CoachingSessionStatus = Field(default=CoachingSessionStatus.ACTIVE)
|
|
personaId: Optional[str] = Field(default=None, description="FK to CoachingPersona (Iteration 2)")
|
|
summary: Optional[str] = Field(default=None, description="AI-generated session summary")
|
|
coachNotes: Optional[str] = Field(default=None, description="JSON: AI internal notes for continuity")
|
|
compressedHistorySummary: Optional[str] = Field(default=None, description="AI summary of older messages for long sessions")
|
|
compressedHistoryUpToMessageCount: Optional[int] = Field(default=None, description="Message count covered by compressedHistorySummary")
|
|
keyTopics: Optional[str] = Field(default=None, description="JSON array of key topics extracted at session complete")
|
|
durationSeconds: int = Field(default=0)
|
|
messageCount: int = Field(default=0)
|
|
competenceScore: Optional[float] = Field(default=None, ge=0.0, le=100.0)
|
|
emailSent: bool = Field(default=False)
|
|
startedAt: Optional[str] = Field(default=None)
|
|
endedAt: Optional[str] = Field(default=None)
|
|
|
|
|
|
class CoachingMessage(PowerOnModel):
|
|
"""A single message in a coaching session."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
sessionId: str = Field(description="FK to CoachingSession")
|
|
contextId: str = Field(description="FK to CoachingContext")
|
|
userId: str = Field(description="Owner user ID")
|
|
role: CoachingMessageRole = Field(description="Message author role")
|
|
content: str = Field(description="Message content (Markdown)")
|
|
contentType: CoachingMessageContentType = Field(default=CoachingMessageContentType.TEXT)
|
|
audioRef: Optional[str] = Field(default=None, description="Reference to audio file")
|
|
metadata: Optional[str] = Field(default=None, description="JSON: token count, voice info, etc.")
|
|
|
|
|
|
class CoachingTask(PowerOnModel):
|
|
"""A task/checklist item assigned within a coaching context."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
contextId: str = Field(description="FK to CoachingContext")
|
|
sessionId: Optional[str] = Field(default=None, description="FK to originating session")
|
|
userId: str = Field(description="Owner user ID")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
title: str = Field(description="Task title")
|
|
description: Optional[str] = Field(default=None)
|
|
status: CoachingTaskStatus = Field(default=CoachingTaskStatus.OPEN)
|
|
priority: CoachingTaskPriority = Field(default=CoachingTaskPriority.MEDIUM)
|
|
dueDate: Optional[str] = Field(default=None)
|
|
completedAt: Optional[str] = Field(default=None)
|
|
|
|
|
|
class CoachingScore(PowerOnModel):
|
|
"""A competence score for a dimension, recorded after a session."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
contextId: str = Field(description="FK to CoachingContext")
|
|
sessionId: str = Field(description="FK to CoachingSession")
|
|
userId: str = Field(description="Owner user ID")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
dimension: str = Field(description="e.g. empathy, clarity, assertiveness, listening")
|
|
score: float = Field(ge=0.0, le=100.0)
|
|
trend: CoachingScoreTrend = Field(default=CoachingScoreTrend.STABLE)
|
|
evidence: Optional[str] = Field(default=None, description="AI reasoning for the score")
|
|
|
|
|
|
class CoachingUserProfile(PowerOnModel):
|
|
"""Per-user coaching profile and preferences."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
userId: str = Field(description="Owner user ID")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
instanceId: str = Field(description="Feature instance ID")
|
|
dailyReminderTime: Optional[str] = Field(default=None, description="HH:MM format")
|
|
dailyReminderEnabled: bool = Field(default=False)
|
|
emailSummaryEnabled: bool = Field(default=True)
|
|
streakDays: int = Field(default=0)
|
|
longestStreak: int = Field(default=0)
|
|
totalSessions: int = Field(default=0)
|
|
totalMinutes: int = Field(default=0)
|
|
lastSessionAt: Optional[str] = Field(default=None)
|
|
|
|
|
|
# ============================================================================
|
|
# Iteration 2: Personas
|
|
# ============================================================================
|
|
|
|
class CoachingPersona(PowerOnModel):
|
|
"""A roleplay persona for coaching sessions."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
userId: str = Field(description="Owner user ID ('system' for builtins)")
|
|
mandateId: Optional[str] = Field(default=None)
|
|
instanceId: Optional[str] = Field(default=None)
|
|
key: str = Field(description="Unique key, e.g. 'critical_cfo_f'")
|
|
label: str = Field(description="Display label, e.g. 'Kritische CFO'")
|
|
description: str = Field(description="Detailed role description for the AI")
|
|
systemPromptOverride: Optional[str] = Field(default=None, description="Full system prompt override for this persona")
|
|
gender: Optional[str] = Field(default=None, description="m or f")
|
|
category: str = Field(default="builtin", description="'builtin' or 'custom'")
|
|
isActive: bool = Field(default=True)
|
|
|
|
|
|
# ============================================================================
|
|
# Iteration 2: Badges / Gamification
|
|
# ============================================================================
|
|
|
|
class CoachingBadge(PowerOnModel):
|
|
"""An achievement badge awarded to a user."""
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
userId: str = Field(description="Owner user ID")
|
|
mandateId: str = Field(description="Mandate ID")
|
|
instanceId: str = Field(description="Feature instance ID")
|
|
badgeKey: str = Field(description="Badge identifier, e.g. 'streak_7'")
|
|
awardedAt: Optional[str] = Field(default=None)
|
|
|
|
|
|
# ============================================================================
|
|
# API Request/Response Models
|
|
# ============================================================================
|
|
|
|
class CreateContextRequest(BaseModel):
|
|
title: str = Field(description="Context title")
|
|
description: Optional[str] = None
|
|
category: Optional[CoachingContextCategory] = CoachingContextCategory.CUSTOM
|
|
goals: Optional[List[str]] = None
|
|
|
|
|
|
class UpdateContextRequest(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
category: Optional[CoachingContextCategory] = None
|
|
goals: Optional[str] = None
|
|
|
|
|
|
class SendMessageRequest(BaseModel):
|
|
content: str = Field(description="User message text")
|
|
contentType: Optional[CoachingMessageContentType] = CoachingMessageContentType.TEXT
|
|
fileIds: Optional[List[str]] = Field(default=None, description="Attached file IDs for agent context")
|
|
dataSourceIds: Optional[List[str]] = Field(default=None, description="Personal data source IDs")
|
|
featureDataSourceIds: Optional[List[str]] = Field(default=None, description="Feature data source IDs")
|
|
allowedProviders: Optional[List[str]] = Field(default=None, description="Allowed AI providers")
|
|
|
|
|
|
class CreateTaskRequest(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
priority: Optional[CoachingTaskPriority] = CoachingTaskPriority.MEDIUM
|
|
dueDate: Optional[str] = None
|
|
|
|
|
|
class UpdateTaskRequest(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
priority: Optional[CoachingTaskPriority] = None
|
|
dueDate: Optional[str] = None
|
|
|
|
|
|
class UpdateTaskStatusRequest(BaseModel):
|
|
status: CoachingTaskStatus
|
|
|
|
|
|
class UpdateProfileRequest(BaseModel):
|
|
dailyReminderTime: Optional[str] = None
|
|
dailyReminderEnabled: Optional[bool] = None
|
|
emailSummaryEnabled: Optional[bool] = None
|
|
|
|
|
|
class StartSessionRequest(BaseModel):
|
|
personaId: Optional[str] = None
|
|
|
|
|
|
class CreatePersonaRequest(BaseModel):
|
|
label: str
|
|
description: str
|
|
gender: Optional[str] = None
|
|
systemPromptOverride: Optional[str] = None
|
|
|
|
|
|
class UpdatePersonaRequest(BaseModel):
|
|
label: Optional[str] = None
|
|
description: Optional[str] = None
|
|
gender: Optional[str] = None
|
|
systemPromptOverride: Optional[str] = None
|
|
isActive: Optional[bool] = None
|
|
|
|
|
|
class DashboardData(BaseModel):
|
|
"""Aggregated dashboard data for the user."""
|
|
totalContexts: int = 0
|
|
activeContexts: int = 0
|
|
totalSessions: int = 0
|
|
totalMinutes: int = 0
|
|
streakDays: int = 0
|
|
longestStreak: int = 0
|
|
averageScore: Optional[float] = None
|
|
recentScores: List[Dict[str, Any]] = Field(default_factory=list)
|
|
openTasks: int = 0
|
|
completedTasks: int = 0
|
|
contexts: List[Dict[str, Any]] = Field(default_factory=list)
|