ReplyFlow / app /models.py
nvikou's picture
Initial commit
582520a verified
Raw History Blame Contribute Delete
5.9 kB
"""SQLAlchemy models for conversations and leads."""
from __future__ import annotations
from datetime import datetime
from typing import Optional
from sqlalchemy import (
Boolean,
DateTime,
ForeignKey,
Integer,
String,
Text,
)
from sqlalchemy.orm import (
DeclarativeBase,
Mapped,
mapped_column,
relationship,
)
class Base(DeclarativeBase):
"""Declarative base."""
class Conversation(Base):
"""A chat session on web or WhatsApp."""
__tablename__ = "conversations"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
channel: Mapped[str] = mapped_column(
String(32), default="web"
)
language: Mapped[str] = mapped_column(
String(8), default="en"
)
status: Mapped[str] = mapped_column(
String(32), default="active"
)
external_id: Mapped[Optional[str]] = mapped_column(
String(128), nullable=True
)
failure_streak: Mapped[int] = mapped_column(
Integer, default=0
)
resolved_without_human: Mapped[bool] = mapped_column(
Boolean, default=False
)
handed_off: Mapped[bool] = mapped_column(
Boolean, default=False
)
appointment_booked: Mapped[bool] = mapped_column(
Boolean, default=False
)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
)
messages: Mapped[list["Message"]] = relationship(
back_populates="conversation",
cascade="all, delete-orphan",
order_by="Message.created_at",
)
lead: Mapped[Optional["Lead"]] = relationship(
back_populates="conversation",
uselist=False,
cascade="all, delete-orphan",
)
appointment: Mapped[Optional["Appointment"]] = (
relationship(
back_populates="conversation",
uselist=False,
cascade="all, delete-orphan",
)
)
class Message(Base):
"""Single message in a conversation."""
__tablename__ = "messages"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
conversation_id: Mapped[int] = mapped_column(
ForeignKey("conversations.id"), index=True
)
role: Mapped[str] = mapped_column(String(16))
content: Mapped[str] = mapped_column(Text)
language: Mapped[Optional[str]] = mapped_column(
String(8), nullable=True
)
grounded: Mapped[Optional[bool]] = mapped_column(
Boolean, nullable=True
)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
conversation: Mapped["Conversation"] = relationship(
back_populates="messages"
)
class Lead(Base):
"""Prospect qualification data collected in chat."""
__tablename__ = "leads"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
conversation_id: Mapped[int] = mapped_column(
ForeignKey("conversations.id"), unique=True
)
name: Mapped[Optional[str]] = mapped_column(
String(128), nullable=True
)
need: Mapped[Optional[str]] = mapped_column(
Text, nullable=True
)
budget: Mapped[Optional[str]] = mapped_column(
String(64), nullable=True
)
contact_channel: Mapped[Optional[str]] = mapped_column(
String(64), nullable=True
)
email: Mapped[Optional[str]] = mapped_column(
String(256), nullable=True
)
phone: Mapped[Optional[str]] = mapped_column(
String(64), nullable=True
)
complete: Mapped[bool] = mapped_column(
Boolean, default=False
)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
)
conversation: Mapped["Conversation"] = relationship(
back_populates="lead"
)
class Appointment(Base):
"""Booked meeting slot."""
__tablename__ = "appointments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
conversation_id: Mapped[int] = mapped_column(
ForeignKey("conversations.id"), unique=True
)
slot_start: Mapped[str] = mapped_column(String(64))
slot_end: Mapped[str] = mapped_column(String(64))
attendee_name: Mapped[Optional[str]] = mapped_column(
String(128), nullable=True
)
attendee_email: Mapped[Optional[str]] = mapped_column(
String(256), nullable=True
)
external_id: Mapped[Optional[str]] = mapped_column(
String(128), nullable=True
)
status: Mapped[str] = mapped_column(
String(32), default="confirmed"
)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
conversation: Mapped["Conversation"] = relationship(
back_populates="appointment"
)
class UnansweredQuestion(Base):
"""Question the bot could not answer from KB."""
__tablename__ = "unanswered_questions"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
conversation_id: Mapped[int] = mapped_column(
ForeignKey("conversations.id"), index=True
)
question: Mapped[str] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)
class KnowledgeDoc(Base):
"""Ingested knowledge document metadata."""
__tablename__ = "knowledge_docs"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
filename: Mapped[str] = mapped_column(String(256))
source_type: Mapped[str] = mapped_column(String(32))
chunk_count: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow
)