"""AI Flow Builder conversation tracking. Stores wizard session state across the 4-stage flow builder process. Conversations expire after 24 hours and are cleaned up by the scheduler. """ import uuid from datetime import datetime, timezone from typing import Optional, Any from sqlalchemy import String, DateTime, ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.dialects.postgresql import UUID, JSONB from app.core.database import Base class AIConversation(Base): __tablename__ = "ai_conversations" id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) user_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, ) account_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False, index=True, ) status: Mapped[str] = mapped_column( String(20), nullable=False, default="foundation", comment="foundation | scaffolding | detailing | reviewing | completed | expired", ) # Conversation history across all wizard stages messages: Mapped[list[dict[str, Any]]] = mapped_column( JSONB, nullable=False, default=list ) # Wizard state: Stage 1 metadata, Stage 2 branches, Stage 3 detail wizard_state: Mapped[dict[str, Any]] = mapped_column( JSONB, nullable=False, default=dict ) # Assembled tree from Stage 4 (null until assembly) generated_tree: Mapped[Optional[dict[str, Any]]] = mapped_column( JSONB, nullable=True ) # Tracks AI call count for per-flow limits question_rounds: Mapped[int] = mapped_column( Integer, nullable=False, default=0 ) expires_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), )