""" Pydantic schemas for API request/response models. """ from datetime import datetime from typing import Optional, List, Dict, Any, Union from pydantic import BaseModel, Field # Base schemas class TimestampMixin(BaseModel): """Mixin for timestamp fields.""" created_at: datetime updated_at: datetime # Session schemas class SessionStartRequest(BaseModel): """Request schema for starting a session.""" session_type: str = Field(..., description="Type of session: startup, resume, or clear") working_directory: str = Field(..., description="Current working directory") git_branch: Optional[str] = Field(None, description="Current git branch") git_repo: Optional[str] = Field(None, description="Git repository URL") environment: Optional[Dict[str, Any]] = Field(None, description="Environment variables and context") class SessionEndRequest(BaseModel): """Request schema for ending a session.""" session_id: int = Field(..., description="ID of the session to end") end_reason: str = Field(default="normal", description="Reason for ending: normal, interrupted, or timeout") class SessionResponse(BaseModel): """Response schema for session operations.""" session_id: int project_id: int status: str message: Optional[str] = None class Config: from_attributes = True # Conversation schemas class ConversationRequest(BaseModel): """Request schema for logging conversations.""" session_id: int = Field(..., description="Associated session ID") timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the exchange occurred") user_prompt: Optional[str] = Field(None, description="User's input message") claude_response: Optional[str] = Field(None, description="Claude's response") tools_used: Optional[List[str]] = Field(None, description="Tools used in the response") files_affected: Optional[List[str]] = Field(None, description="Files mentioned or modified") context: Optional[Dict[str, Any]] = Field(None, description="Additional context") tokens_input: Optional[int] = Field(None, description="Estimated input token count") tokens_output: Optional[int] = Field(None, description="Estimated output token count") exchange_type: str = Field(..., description="Type of exchange: user_prompt or claude_response") class ConversationResponse(BaseModel): """Response schema for conversation operations.""" id: int session_id: int timestamp: datetime exchange_type: str content_length: int class Config: from_attributes = True # Activity schemas class ActivityRequest(BaseModel): """Request schema for recording activities.""" session_id: int = Field(..., description="Associated session ID") conversation_id: Optional[int] = Field(None, description="Associated conversation ID") timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the activity occurred") tool_name: str = Field(..., description="Name of the tool used") action: str = Field(..., description="Specific action taken") file_path: Optional[str] = Field(None, description="Target file path if applicable") metadata: Optional[Dict[str, Any]] = Field(None, description="Tool-specific metadata") success: bool = Field(default=True, description="Whether the operation succeeded") error_message: Optional[str] = Field(None, description="Error details if failed") lines_added: Optional[int] = Field(None, description="Lines added for Edit/Write operations") lines_removed: Optional[int] = Field(None, description="Lines removed for Edit operations") class ActivityResponse(BaseModel): """Response schema for activity operations.""" id: int session_id: int tool_name: str action: str timestamp: datetime success: bool class Config: from_attributes = True # Waiting period schemas class WaitingStartRequest(BaseModel): """Request schema for starting a waiting period.""" session_id: int = Field(..., description="Associated session ID") timestamp: datetime = Field(default_factory=datetime.utcnow, description="When waiting started") context_before: Optional[str] = Field(None, description="Context before waiting") class WaitingEndRequest(BaseModel): """Request schema for ending a waiting period.""" session_id: int = Field(..., description="Associated session ID") timestamp: datetime = Field(default_factory=datetime.utcnow, description="When waiting ended") duration_seconds: Optional[int] = Field(None, description="Total waiting duration") context_after: Optional[str] = Field(None, description="Context after waiting") class WaitingPeriodResponse(BaseModel): """Response schema for waiting period operations.""" id: int session_id: int start_time: datetime end_time: Optional[datetime] duration_seconds: Optional[int] engagement_score: float class Config: from_attributes = True # Git operation schemas class GitOperationRequest(BaseModel): """Request schema for git operations.""" session_id: int = Field(..., description="Associated session ID") timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the operation occurred") operation: str = Field(..., description="Type of git operation") command: str = Field(..., description="Full git command executed") result: Optional[str] = Field(None, description="Command output") success: bool = Field(default=True, description="Whether the command succeeded") files_changed: Optional[List[str]] = Field(None, description="Files affected by the operation") lines_added: Optional[int] = Field(None, description="Lines added") lines_removed: Optional[int] = Field(None, description="Lines removed") commit_hash: Optional[str] = Field(None, description="Git commit SHA") branch_from: Optional[str] = Field(None, description="Source branch") branch_to: Optional[str] = Field(None, description="Target branch") class GitOperationResponse(BaseModel): """Response schema for git operations.""" id: int session_id: int operation: str timestamp: datetime success: bool commit_hash: Optional[str] class Config: from_attributes = True # Project schemas class ProjectSummary(BaseModel): """Summary information about a project.""" id: int name: str path: str git_repo: Optional[str] languages: Optional[List[str]] total_sessions: int total_time_minutes: int last_activity: Optional[datetime] files_modified_count: int lines_changed_count: int class Config: from_attributes = True class TimelineEvent(BaseModel): """Individual event in project timeline.""" timestamp: datetime type: str # session_start, session_end, conversation, activity, git_operation data: Dict[str, Any] class ProjectTimeline(BaseModel): """Project timeline with events.""" project: ProjectSummary timeline: List[TimelineEvent] # Analytics schemas class ProductivityMetrics(BaseModel): """Productivity analytics response.""" engagement_score: float = Field(..., description="Overall engagement level (0-100)") average_session_length: float = Field(..., description="Minutes per session") think_time_average: float = Field(..., description="Average waiting time between interactions") files_per_session: float = Field(..., description="Average files touched per session") tools_most_used: List[Dict[str, Union[str, int]]] = Field(..., description="Most frequently used tools") productivity_trends: List[Dict[str, Union[str, float]]] = Field(..., description="Daily productivity scores") class ConversationSearchResult(BaseModel): """Search result for conversation search.""" id: int project_name: str timestamp: datetime user_prompt: Optional[str] claude_response: Optional[str] relevance_score: float context: List[str] class Config: from_attributes = True # Error schemas class ErrorResponse(BaseModel): """Standard error response.""" error: str message: str details: Optional[Dict[str, Any]] = None