"""ORM rows for the generated-file library read index.""" from __future__ import annotations from datetime import UTC, datetime from sqlalchemy import BigInteger, Boolean, Index, String, Text from sqlalchemy.orm import Mapped, mapped_column from deerflow.persistence.base import Base from deerflow.persistence.types import BeijingDateTime class ArtifactLibraryItemRow(Base): """One generated file projected into the fast library listing.""" __tablename__ = "artifact_library_items" __table_args__ = ( Index( "ix_artifact_library_user_modified", "user_id", "modified_at", "artifact_id", ), Index( "ix_artifact_library_user_kind", "user_id", "file_kind", "modified_at", "artifact_id", ), Index( "ix_artifact_library_user_source", "user_id", "source_type", "modified_at", "artifact_id", ), Index("ix_artifact_library_user_generation", "user_id", "sync_generation"), ) # A deterministic SHA-256 of (user_id, thread_id, virtual path) avoids a # long composite primary key that would exceed older MySQL index limits. artifact_id: Mapped[str] = mapped_column(String(64), primary_key=True) user_id: Mapped[str] = mapped_column(String(64), nullable=False) thread_id: Mapped[str] = mapped_column(String(128), nullable=False) thread_title: Mapped[str] = mapped_column(String(512), nullable=False, default="") agent_id: Mapped[str | None] = mapped_column(String(128), nullable=True) agent_name: Mapped[str | None] = mapped_column(String(512), nullable=True) thread_type: Mapped[str | None] = mapped_column(String(64), nullable=True) source_type: Mapped[str] = mapped_column(String(32), nullable=False) strategy_project_id: Mapped[str | None] = mapped_column(String(128), nullable=True) strategy_name: Mapped[str | None] = mapped_column(String(512), nullable=True) strategy_stale: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) strategy_source_kind: Mapped[str | None] = mapped_column(String(32), nullable=True) download_url: Mapped[str | None] = mapped_column(Text, nullable=True) path: Mapped[str] = mapped_column(Text, nullable=False) name: Mapped[str] = mapped_column(String(512), nullable=False) size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0) mime_type: Mapped[str | None] = mapped_column(String(255), nullable=True) modified_at: Mapped[datetime] = mapped_column(BeijingDateTime(), nullable=False) file_kind: Mapped[str] = mapped_column(String(32), nullable=False) sync_generation: Mapped[str] = mapped_column(String(36), nullable=False) indexed_at: Mapped[datetime] = mapped_column(BeijingDateTime(), nullable=False, default=lambda: datetime.now(UTC)) class ArtifactLibrarySyncStateRow(Base): """Per-user lease and last-published generation metadata.""" __tablename__ = "artifact_library_sync_states" user_id: Mapped[str] = mapped_column(String(64), primary_key=True) status: Mapped[str] = mapped_column(String(16), nullable=False, default="idle") generation: Mapped[str | None] = mapped_column(String(36), nullable=True) started_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True) lease_expires_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True) completed_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True) last_error: Mapped[str | None] = mapped_column(String(1024), nullable=True)