"""Data models and schemas for the application.""" from pydantic import BaseModel, Field, EmailStr, validator from typing import Optional from datetime import datetime # ===== USER SCHEMAS ===== class UserCreate(BaseModel): """What we need to create a user.""" email: EmailStr name: str = Field(..., min_length=1, max_length=100) password: str = Field(..., min_length=8) class UserLogin(BaseModel): """What we need to login.""" email: EmailStr password: str class UserResponse(BaseModel): """What we return about a user.""" id: int email: str name: str created_at: datetime class Config: from_attributes = True class UserUpdate(BaseModel): """What can be updated.""" name: Optional[str] = Field(None, min_length=1, max_length=100) # ===== AUTH SCHEMAS ===== class Token(BaseModel): """JWT token response.""" access_token: str token_type: str = "bearer" class TokenData(BaseModel): """Data encoded in the token.""" user_id: int email: str # ===== CHAT SCHEMAS ===== class Message(BaseModel): """A chat message.""" id: Optional[int] = None conversation_id: int sender_id: int content: str created_at: Optional[datetime] = None class Conversation(BaseModel): """A conversation thread.""" id: Optional[int] = None user_id: int title: str created_at: Optional[datetime] = None # ===== AI QUERY SCHEMAS ===== class QueryRequest(BaseModel): """Request for AI query.""" conversation_id: Optional[str] = Field( default=None, max_length=128, description="Optional conversation/session id used for chat memory and per-thread RAG" ) query: str = Field( ..., min_length=1, max_length=1000, description="The question or prompt to send to the AI" ) @validator('query') def validate_query(cls, v): if not v.strip(): raise ValueError("Query cannot be empty") # Basic sanitization - remove potential script tags v = v.replace("", "") return v.strip() @validator('conversation_id') def validate_conversation_id(cls, v): if v is None: return None v = str(v).strip() return v or None class QueryResponse(BaseModel): """Response from AI query.""" success: bool response: Optional[str] = None error: Optional[str] = None timestamp: datetime = Field(default_factory=datetime.utcnow) requires_auth: bool = False # True if Google authentication is needed auth_url: Optional[str] = None # Google OAuth URL if authentication is required class HealthCheckResponse(BaseModel): """Health check response.""" status: str # "healthy", "degraded", "unhealthy" timestamp: datetime = Field(default_factory=datetime.utcnow) version: str = "1.0.0" components: dict = {} # ===== CONVERSATION MANAGEMENT SCHEMAS ===== class MessageResponse(BaseModel): """Response format for a single message.""" id: int sender_id: int content: str created_at: datetime class Config: from_attributes = True class ConversationListResponse(BaseModel): """Brief conversation info for listing.""" id: int title: Optional[str] = None created_at: datetime last_message_at: datetime message_count: int class Config: from_attributes = True class ConversationDetailResponse(BaseModel): """Full conversation with all messages.""" id: int title: Optional[str] = None created_at: datetime last_message_at: datetime messages: list[MessageResponse] class Config: from_attributes = True class ChatRequest(BaseModel): """Chat message request.""" conversation_id: Optional[int] = Field( None, description="Existing conversation ID, or None to create new conversation" ) query: str = Field( ..., min_length=1, max_length=2000, description="User's message or query" ) @validator('query') def validate_query(cls, v): if not v.strip(): raise ValueError("Query cannot be empty") return v.strip() class ChatResponse(BaseModel): """Chat message response.""" conversation_id: int response: str timestamp: datetime = Field(default_factory=datetime.utcnow) class Config: from_attributes = True class PaginatedConversationsResponse(BaseModel): """Paginated list of conversations.""" conversations: list[ConversationListResponse] total: int skip: int limit: int class Config: from_attributes = True