Spaces:
Running
Running
| """SQLAlchemy ORM models — matches actual Neon schema.""" | |
| from datetime import datetime | |
| from typing import Optional | |
| from sqlalchemy import ( | |
| Boolean, | |
| DateTime, | |
| Float, | |
| Index, | |
| Integer, | |
| String, | |
| Text, | |
| ) | |
| from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column | |
| class Base(DeclarativeBase): | |
| pass | |
| class User(Base): | |
| __tablename__ = "users" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| email: Mapped[str] = mapped_column(String(320), nullable=False, unique=True, index=True) | |
| hashed_password: Mapped[str] = mapped_column(String(256), nullable=False) | |
| roles: Mapped[str] = mapped_column(Text, default='["user"]', nullable=False) | |
| mfa_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) | |
| mfa_secret: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| display_name: Mapped[Optional[str]] = mapped_column(String(256), nullable=True) | |
| username: Mapped[Optional[str]] = mapped_column(String(128), nullable=True, unique=True) | |
| class BlacklistedToken(Base): | |
| __tablename__ = "blacklisted_tokens" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| token_jti: Mapped[str] = mapped_column(String(512), nullable=False, index=True) | |
| expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class Document(Base): | |
| __tablename__ = "documents" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| filename: Mapped[str] = mapped_column(String(512), nullable=False) | |
| has_pii: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) | |
| privacy: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) | |
| sha256: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) | |
| cloudinary_url: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| cloudinary_public_id: Mapped[Optional[str]] = mapped_column(String(256), nullable=True) | |
| status: Mapped[str] = mapped_column(String(32), default="success", nullable=False, index=True) | |
| uploaded_by: Mapped[Optional[str]] = mapped_column(String(256), nullable=True, index=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, index=True) | |
| class Session(Base): | |
| __tablename__ = "chat_sessions" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| title: Mapped[Optional[str]] = mapped_column(String(256), nullable=True) | |
| user_id: Mapped[str] = mapped_column(String(256), nullable=False, index=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class Message(Base): | |
| __tablename__ = "chat_messages" | |
| # Neon uses string (uuid hex) for id, not bigint autoincrement | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| session_id: Mapped[str] = mapped_column(String(256), nullable=False, index=True) | |
| role: Mapped[str] = mapped_column(String(16), nullable=False) | |
| content: Mapped[str] = mapped_column(Text, nullable=False) | |
| citations: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| reasoning_path: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| tokens_used: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| cost_usd: Mapped[Optional[float]] = mapped_column(Float, nullable=True) | |
| query_id: Mapped[Optional[str]] = mapped_column(String(256), nullable=True) | |
| feedback: Mapped[Optional[str]] = mapped_column(String(8), nullable=True) | |
| verification: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class Query(Base): | |
| __tablename__ = "queries" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| question: Mapped[str] = mapped_column(Text, nullable=False) | |
| answer: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| citations: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| reasoning_path: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| tokens_used: Mapped[Optional[int]] = mapped_column(Integer, default=0, nullable=True) | |
| cost_usd: Mapped[Optional[float]] = mapped_column(Float, default=0, nullable=True) | |
| user_id: Mapped[Optional[str]] = mapped_column(String(256), nullable=True, index=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class Feedback(Base): | |
| __tablename__ = "feedback" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| query_id: Mapped[str] = mapped_column(String(256), nullable=False, index=True) | |
| thumbs_up: Mapped[bool] = mapped_column(Boolean, nullable=False) | |
| comment: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| user_id: Mapped[Optional[str]] = mapped_column(String(256), nullable=True, index=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class DeadLetter(Base): | |
| __tablename__ = "dead_letter_queue" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| source: Mapped[str] = mapped_column(String(64), nullable=False, index=True) | |
| filename: Mapped[Optional[str]] = mapped_column(String(512), nullable=True) | |
| error: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| payload: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| retry_count: Mapped[Optional[int]] = mapped_column(Integer, default=0, nullable=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| class UploadJob(Base): | |
| """Tracks per-document upload pipeline progress. | |
| Note: in the live Neon schema, this table is named `upload_progress` (not | |
| `upload_jobs`). Field names match: `stage`, `progress`, `document_id`, | |
| `user_id`, plus `error` and timestamps. | |
| """ | |
| __tablename__ = "upload_progress" | |
| id: Mapped[str] = mapped_column(String, primary_key=True) | |
| filename: Mapped[str] = mapped_column(String(512), nullable=False) | |
| stage: Mapped[str] = mapped_column(String(32), nullable=False, index=True) | |
| progress: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| error: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| document_id: Mapped[Optional[str]] = mapped_column(String(256), nullable=True) | |
| user_id: Mapped[str] = mapped_column(String(256), nullable=False, index=True) | |
| created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) | |
| Index("ix_documents_uploaded_by_created", Document.uploaded_by, Document.created_at.desc()) | |