"""Knowledge extraction models — tracks LLM extractions and review queue.""" from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import DateTime, Float, ForeignKey, Index, String, Text, func from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin class KnowledgeExtraction(Base, TenantMixin): """Tracks a single knowledge extraction run from a source (wiki, dms, mail, comm).""" __tablename__ = "knowledge_extractions" __table_args__ = ( Index("ix_knowledge_ext_tenant_status", "tenant_id", "status"), Index("ix_knowledge_ext_source", "tenant_id", "source_type", "source_id"), ) id: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4) source_type: Mapped[str] = mapped_column(String(50), nullable=False) # wiki_article, dms_file, mail, communication source_id: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False) source_title: Mapped[str | None] = mapped_column(String(500), nullable=True) extracted_entities: Mapped[list] = mapped_column(JSONB, nullable=False, default=list) extracted_relationships: Mapped[list] = mapped_column(JSONB, nullable=False, default=list) confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.0) status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending") # pending, approved, rejected, auto_created review_notes: Mapped[str | None] = mapped_column(Text, nullable=True) llm_model: Mapped[str | None] = mapped_column(String(100), nullable=True) llm_cost_usd: Mapped[float] = mapped_column(Float, nullable=False, default=0.0) created_by: Mapped[uuid.UUID | None] = mapped_column(PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) reviewed_by: Mapped[uuid.UUID | None] = mapped_column(PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())