"""Data models for rules and conversations.""" from pydantic import BaseModel, Field from typing import Optional from datetime import datetime class RuleMetadata(BaseModel): """Frontmatter metadata for a rule document.""" rule_id: str = Field(..., description="Unique rule identifier, e.g. '5.2.1(b)'") title: str = Field(..., description="Rule title") section: str = Field(..., description="Section/category name") parent_rule: Optional[str] = Field( None, description="Parent rule ID for hierarchical rules" ) last_updated: str = Field( default_factory=lambda: datetime.now().strftime("%Y-%m-%d"), description="Last update date", ) page_ref: Optional[str] = Field( None, description="Reference to page number in rulebook" ) class RuleDocument(BaseModel): """Complete rule document with metadata and content.""" metadata: RuleMetadata content: str = Field(..., description="Rule text and examples") source_file: str = Field(..., description="Source file path") embedding: Optional[list[float]] = None def to_chroma_metadata(self) -> dict: """Convert to ChromaDB metadata format.""" return { "rule_id": self.metadata.rule_id, "title": self.metadata.title, "section": self.metadata.section, "parent_rule": self.metadata.parent_rule or "", "page_ref": self.metadata.page_ref or "", "last_updated": self.metadata.last_updated, "source_file": self.source_file, } class Conversation(BaseModel): """Conversation session.""" id: str user_id: str # Discord user ID channel_id: str # Discord channel ID created_at: datetime = Field(default_factory=datetime.now) last_activity: datetime = Field(default_factory=datetime.now) class Message(BaseModel): """Individual message in a conversation.""" id: str conversation_id: str content: str is_user: bool parent_id: Optional[str] = None created_at: datetime = Field(default_factory=datetime.now) class ChatRequest(BaseModel): """Incoming chat request from Discord.""" message: str conversation_id: Optional[str] = None parent_message_id: Optional[str] = None user_id: str channel_id: str class ChatResponse(BaseModel): """Response to chat request.""" response: str conversation_id: str message_id: str parent_message_id: Optional[str] = None cited_rules: list[str] = Field(default_factory=list) confidence: float = Field(..., ge=0.0, le=1.0) needs_human: bool = Field( default=False, description="Whether the question needs human review (unanswered)", ) class RuleSearchResult(BaseModel): """Result from vector search.""" rule_id: str title: str content: str section: str similarity: float = Field(..., ge=0.0, le=1.0)