"""AI usage tracking for quota enforcement and cost visibility. Every AI API call is recorded here. Only rows with counts_toward_quota=True and succeeded=True are counted against the user's monthly quota. """ import uuid from datetime import datetime, timezone from typing import Optional, Any from sqlalchemy import String, DateTime, ForeignKey, Integer, Boolean, Numeric from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.dialects.postgresql import UUID, JSONB from app.core.database import Base class AIUsage(Base): __tablename__ = "ai_usage" 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, ) conversation_id: Mapped[Optional[uuid.UUID]] = mapped_column( UUID(as_uuid=True), ForeignKey("ai_conversations.id", ondelete="SET NULL"), nullable=True, ) generation_type: Mapped[str] = mapped_column( String(20), nullable=False, comment="scaffold | branch_detail | branch_suggest", ) tier_at_time: Mapped[str] = mapped_column( String(20), nullable=False, comment="free | pro | team | enterprise", ) input_tokens: Mapped[int] = mapped_column(Integer, nullable=False, default=0) output_tokens: Mapped[int] = mapped_column(Integer, nullable=False, default=0) estimated_cost_usd: Mapped[float] = mapped_column( Numeric(10, 6), nullable=False, default=0 ) succeeded: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) counts_toward_quota: Mapped[bool] = mapped_column( Boolean, nullable=False, default=False ) error_code: Mapped[Optional[str]] = mapped_column( String(100), nullable=True ) extra_data: Mapped[dict[str, Any]] = mapped_column( "metadata", JSONB, nullable=False, default=dict ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), index=True, )