| """SQLAlchemy ORM models for NEXUS.""" |
|
|
| from __future__ import annotations |
|
|
| import uuid |
| from datetime import datetime |
| from typing import Optional |
|
|
| from sqlalchemy import ( |
| JSON, |
| DateTime, |
| Float, |
| ForeignKey, |
| Integer, |
| String, |
| Text, |
| UniqueConstraint, |
| func, |
| ) |
| from sqlalchemy.dialects.postgresql import JSONB, UUID |
| from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship |
|
|
|
|
| def _uuid() -> str: |
| return str(uuid.uuid4()) |
|
|
|
|
| class Base(DeclarativeBase): |
| pass |
|
|
|
|
| class User(Base): |
| __tablename__ = "users" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False) |
| name: Mapped[Optional[str]] = mapped_column(String(255)) |
| google_id: Mapped[Optional[str]] = mapped_column(String(255), unique=True, index=True) |
| api_key_hash: Mapped[Optional[str]] = mapped_column(String(255)) |
| plan: Mapped[str] = mapped_column(String(32), default="free", nullable=False) |
| credits_used: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
|
|
| tasks: Mapped[list["Task"]] = relationship(back_populates="user") |
|
|
|
|
| class Task(Base): |
| __tablename__ = "tasks" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| user_id: Mapped[Optional[str]] = mapped_column( |
| String(36), ForeignKey("users.id", ondelete="SET NULL"), index=True |
| ) |
| topic: Mapped[str] = mapped_column(Text, nullable=False) |
| slide_count: Mapped[int] = mapped_column(Integer, default=8, nullable=False) |
| theme: Mapped[str] = mapped_column(String(64), default="Editorial", nullable=False) |
| search_web: Mapped[bool] = mapped_column(default=True, nullable=False) |
| status: Mapped[str] = mapped_column(String(32), default="pending", index=True, nullable=False) |
| progress_pct: Mapped[float] = mapped_column(Float, default=0.0, nullable=False) |
| current_step: Mapped[str] = mapped_column(String(255), default="", nullable=False) |
| error_msg: Mapped[Optional[str]] = mapped_column(Text) |
| tokens_used: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| cost_usd: Mapped[float] = mapped_column(Float, default=0.0, nullable=False) |
| model_used: Mapped[Optional[str]] = mapped_column(String(64)) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
| completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) |
|
|
| user: Mapped[Optional[User]] = relationship(back_populates="tasks") |
| slides: Mapped[Optional["SlideDeck"]] = relationship( |
| back_populates="task", uselist=False, cascade="all, delete-orphan" |
| ) |
| exports: Mapped[list["Export"]] = relationship( |
| back_populates="task", cascade="all, delete-orphan" |
| ) |
| share_tokens: Mapped[list["ShareToken"]] = relationship( |
| back_populates="task", cascade="all, delete-orphan" |
| ) |
|
|
|
|
| class SlideDeck(Base): |
| __tablename__ = "slides" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| task_id: Mapped[str] = mapped_column( |
| String(36), ForeignKey("tasks.id", ondelete="CASCADE"), unique=True, index=True |
| ) |
| slide_data: Mapped[list] = mapped_column(JSON().with_variant(JSONB, "postgresql"), nullable=False) |
| theme: Mapped[str] = mapped_column(String(64), default="Editorial", nullable=False) |
| slide_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
|
|
| task: Mapped[Task] = relationship(back_populates="slides") |
|
|
|
|
| class Export(Base): |
| __tablename__ = "exports" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| task_id: Mapped[str] = mapped_column( |
| String(36), ForeignKey("tasks.id", ondelete="CASCADE"), index=True |
| ) |
| format: Mapped[str] = mapped_column(String(16), nullable=False) |
| file_url: Mapped[str] = mapped_column(Text, nullable=False) |
| file_size: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
|
|
| task: Mapped[Task] = relationship(back_populates="exports") |
|
|
|
|
| class ShareToken(Base): |
| __tablename__ = "share_tokens" |
| __table_args__ = (UniqueConstraint("token", name="uq_share_token"),) |
|
|
| token: Mapped[str] = mapped_column(String(64), primary_key=True) |
| task_id: Mapped[str] = mapped_column( |
| String(36), ForeignKey("tasks.id", ondelete="CASCADE"), index=True |
| ) |
| views: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
|
|
| task: Mapped[Task] = relationship(back_populates="share_tokens") |
|
|
|
|
| |
| |
| |
| |
| |
|
|
| class AgentRun(Base): |
| __tablename__ = "agent_runs" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| task_id: Mapped[Optional[str]] = mapped_column( |
| String(36), ForeignKey("tasks.id", ondelete="CASCADE"), index=True |
| ) |
| user_id: Mapped[Optional[str]] = mapped_column( |
| String(36), ForeignKey("users.id", ondelete="SET NULL"), index=True |
| ) |
| goal: Mapped[str] = mapped_column(Text, nullable=False) |
| status: Mapped[str] = mapped_column( |
| String(32), default="pending", index=True, nullable=False |
| ) |
| max_steps: Mapped[int] = mapped_column(Integer, default=20, nullable=False) |
| step_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) |
| error_msg: Mapped[Optional[str]] = mapped_column(Text) |
| meta: Mapped[dict] = mapped_column( |
| JSON().with_variant(JSONB, "postgresql"), default=dict, nullable=False |
| ) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
| completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) |
|
|
| steps: Mapped[list["AgentStep"]] = relationship( |
| back_populates="run", cascade="all, delete-orphan", order_by="AgentStep.step_index" |
| ) |
| artifacts: Mapped[list["Artifact"]] = relationship( |
| back_populates="run", cascade="all, delete-orphan" |
| ) |
|
|
|
|
| class AgentStep(Base): |
| __tablename__ = "agent_steps" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| run_id: Mapped[str] = mapped_column( |
| String(36), ForeignKey("agent_runs.id", ondelete="CASCADE"), index=True, nullable=False |
| ) |
| step_index: Mapped[int] = mapped_column(Integer, nullable=False) |
| kind: Mapped[str] = mapped_column(String(32), nullable=False) |
| action: Mapped[Optional[str]] = mapped_column(String(64)) |
| status: Mapped[str] = mapped_column(String(32), default="ok", nullable=False) |
| input_json: Mapped[dict] = mapped_column( |
| JSON().with_variant(JSONB, "postgresql"), default=dict, nullable=False |
| ) |
| output_json: Mapped[dict] = mapped_column( |
| JSON().with_variant(JSONB, "postgresql"), default=dict, nullable=False |
| ) |
| error: Mapped[Optional[str]] = mapped_column(Text) |
| started_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
| completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) |
|
|
| run: Mapped[AgentRun] = relationship(back_populates="steps") |
|
|
|
|
| class Artifact(Base): |
| __tablename__ = "artifacts" |
|
|
| id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid) |
| run_id: Mapped[Optional[str]] = mapped_column( |
| String(36), ForeignKey("agent_runs.id", ondelete="CASCADE"), index=True |
| ) |
| task_id: Mapped[Optional[str]] = mapped_column( |
| String(36), ForeignKey("tasks.id", ondelete="CASCADE"), index=True |
| ) |
| artifact_type: Mapped[str] = mapped_column(String(64), index=True, nullable=False) |
| title: Mapped[Optional[str]] = mapped_column(String(255)) |
| meta: Mapped[dict] = mapped_column( |
| JSON().with_variant(JSONB, "postgresql"), default=dict, nullable=False |
| ) |
| file_url: Mapped[Optional[str]] = mapped_column(Text) |
| created_at: Mapped[datetime] = mapped_column( |
| DateTime(timezone=True), server_default=func.now(), nullable=False |
| ) |
|
|
| run: Mapped[Optional[AgentRun]] = relationship(back_populates="artifacts") |
|
|