Spaces:
Running on Zero
Running on Zero
File size: 6,827 Bytes
0d374d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | """
ORM models.
Conversation history and long-term memory are architecturally separate
systems (see README). users/conversations/messages hold conversation
history. Memory (this file's Memory class, added Day 2) is a distinct,
much smaller table: only durable facts the extractor decided to STORE,
never the raw conversation.
"""
import uuid
from datetime import datetime, timezone
from sqlalchemy import Column, String, DateTime, ForeignKey, Text, Float, Boolean
from sqlalchemy.orm import relationship
from app.database import Base
def _uuid() -> str:
return str(uuid.uuid4())
def _now() -> datetime:
return datetime.now(timezone.utc)
class User(Base):
__tablename__ = "users"
id = Column(String, primary_key=True, default=_uuid)
external_id = Column(String, unique=True, nullable=False, index=True)
created_at = Column(DateTime, default=_now)
conversations = relationship("Conversation", back_populates="user", cascade="all, delete-orphan")
class Conversation(Base):
__tablename__ = "conversations"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
title = Column(String, nullable=True)
created_at = Column(DateTime, default=_now)
updated_at = Column(DateTime, default=_now, onupdate=_now)
user = relationship("User", back_populates="conversations")
messages = relationship(
"Message", back_populates="conversation", cascade="all, delete-orphan", order_by="Message.created_at"
)
class Message(Base):
__tablename__ = "messages"
id = Column(String, primary_key=True, default=_uuid)
conversation_id = Column(String, ForeignKey("conversations.id"), nullable=False, index=True)
role = Column(String, nullable=False) # "user" | "assistant"
content = Column(Text, nullable=False)
created_at = Column(DateTime, default=_now)
conversation = relationship("Conversation", back_populates="messages")
class Memory(Base):
"""
A durable fact extracted from conversation, per Day 2's extraction
step. NOT the same as a Message - this table is meant to stay
small and curated, one row per distinct fact worth remembering.
status stays simple for now ("active" only). HISTORICAL/ACTIVE
temporal tracking and conflict resolution (UPDATE/SUPERSEDE) are a
later stage - don't add those columns before that logic exists to
use them.
"""
__tablename__ = "memories"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
memory_type = Column(String, nullable=False) # "semantic" | "episodic" | "temporal"
content = Column(Text, nullable=False)
importance = Column(Float, nullable=False)
confidence = Column(Float, nullable=False)
status = Column(String, nullable=False, default="active")
source_conversation_id = Column(String, ForeignKey("conversations.id"), nullable=True)
created_at = Column(DateTime, default=_now)
updated_at = Column(DateTime, default=_now, onupdate=_now)
user = relationship("User")
class JobApplication(Base):
"""
One row per job applied to. Reminders/reports (later features) read
from this table rather than being separate stores - keep this one
table as the single source of truth for application status.
status is a plain string, not an enum column, to keep this DB
portable and migrations simple - validate allowed values in the
Pydantic schema layer instead (app/schemas.py), not here.
"""
__tablename__ = "job_applications"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
company = Column(String, nullable=False)
role_title = Column(String, nullable=False)
status = Column(String, nullable=False, default="applied")
# "applied" | "interview" | "offer" | "rejected" | "no_response"
job_url = Column(String, nullable=True)
notes = Column(Text, nullable=True)
applied_date = Column(DateTime, nullable=False)
follow_up_date = Column(DateTime, nullable=True)
last_contact_date = Column(DateTime, nullable=True)
match_score = Column(Float, nullable=True)
tailored_pitch = Column(Text, nullable=True)
created_at = Column(DateTime, default=_now)
updated_at = Column(DateTime, default=_now, onupdate=_now)
user = relationship("User")
class UserResume(Base):
"""
Persists user resumes. Supports multiple named resumes per user
(e.g., Full Stack, AI/ML, DevOps) with a primary active flag.
"""
__tablename__ = "user_resumes"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
title = Column(String, nullable=False, default="Master Resume")
file_name = Column(String, nullable=True)
resume_text = Column(Text, nullable=False)
is_primary = Column(Boolean, default=False)
created_at = Column(DateTime, default=_now)
updated_at = Column(DateTime, default=_now, onupdate=_now)
user = relationship("User")
class JobMatch(Base):
"""
Stores comparison and match analysis between a user's resume and a job description.
"""
__tablename__ = "job_matches"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
job_url = Column(String, nullable=True)
company = Column(String, nullable=True)
role_title = Column(String, nullable=True)
job_description = Column(Text, nullable=False)
match_score = Column(Float, nullable=False) # 0 to 100
verdict = Column(String, nullable=False) # "Strong Match", etc.
analysis_json = Column(Text, nullable=False) # Full structured JSON string
created_at = Column(DateTime, default=_now)
user = relationship("User")
class UserGmailToken(Base):
"""
Stores OAuth2 credentials for Gmail API access.
Scope is strictly restricted to 'https://www.googleapis.com/auth/gmail.send'
(send-only access, no read or mailbox modification permissions).
"""
__tablename__ = "user_gmail_tokens"
id = Column(String, primary_key=True, default=_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
email = Column(String, nullable=True)
access_token = Column(Text, nullable=False)
refresh_token = Column(Text, nullable=True)
expires_at = Column(DateTime, nullable=True)
scopes = Column(String, nullable=False, default="https://www.googleapis.com/auth/gmail.send")
created_at = Column(DateTime, default=_now)
updated_at = Column(DateTime, default=_now, onupdate=_now)
user = relationship("User")
|