mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 15:29:36 +00:00
- Align httpx, litellm, langchain, openai, mem0ai, crewai, numpy, requests, pydantic - Rename SequenceEvent ORM attribute to event_metadata (DB column stays metadata) - Use PyJWT instead of python-jose in security and auth service - Mem0: MemoryConfig + graceful fallback when init fails (CI without keys) - Frontend: I18nProvider in root layout, fix dashboard LeadScoreCard props, Section id, kpi-card useRef, en.json nameAr parity, e2e assertion for premium landing - README: troubleshooting for connection refused and local E2E Playwright install Co-authored-by: VoXc2 <VoXc2@users.noreply.github.com>
108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
"""Sequence engine models for multi-channel outreach."""
|
|
|
|
import enum
|
|
from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey, Index, Text
|
|
from sqlalchemy.orm import relationship
|
|
from datetime import datetime, timezone
|
|
|
|
from app.models.base import TenantModel, BaseModel
|
|
from app.models.compat import UUID, JSONB
|
|
|
|
|
|
class SequenceStatus(str, enum.Enum):
|
|
ACTIVE = "active"
|
|
PAUSED = "paused"
|
|
COMPLETED = "completed"
|
|
STOPPED = "stopped"
|
|
|
|
|
|
class SequenceEventStatus(str, enum.Enum):
|
|
SENT = "sent"
|
|
DELIVERED = "delivered"
|
|
OPENED = "opened"
|
|
REPLIED = "replied"
|
|
FAILED = "failed"
|
|
|
|
|
|
class Sequence(TenantModel):
|
|
"""A reusable multi-step outreach sequence."""
|
|
|
|
__tablename__ = "sequences"
|
|
__table_args__ = (
|
|
Index("ix_seq_tenant_active", "tenant_id", "is_active"),
|
|
)
|
|
|
|
name = Column(String(255), nullable=False)
|
|
name_ar = Column(String(255), nullable=True)
|
|
description = Column(Text, nullable=True)
|
|
trigger_event = Column(String(100), nullable=True) # lead_created, stage_changed, etc.
|
|
is_active = Column(Boolean, default=True)
|
|
created_by = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
|
|
|
|
steps = relationship("SequenceStep", back_populates="sequence", order_by="SequenceStep.step_order")
|
|
enrollments = relationship("SequenceEnrollment", back_populates="sequence")
|
|
creator = relationship("User", foreign_keys=[created_by])
|
|
|
|
|
|
class SequenceStep(BaseModel):
|
|
"""A single step within a sequence."""
|
|
|
|
__tablename__ = "sequence_steps"
|
|
__table_args__ = (
|
|
Index("ix_step_sequence_order", "sequence_id", "step_order"),
|
|
)
|
|
|
|
sequence_id = Column(UUID(as_uuid=True), ForeignKey("sequences.id", ondelete="CASCADE"), nullable=False)
|
|
step_order = Column(Integer, nullable=False)
|
|
channel = Column(String(50), nullable=False) # whatsapp, email, sms
|
|
delay_minutes = Column(Integer, nullable=False, default=0)
|
|
template_content = Column(Text, nullable=False)
|
|
template_content_ar = Column(Text, nullable=True)
|
|
variant = Column(String(1), nullable=True) # A or B for A/B testing
|
|
conditions = Column(JSONB, default=dict) # e.g. {"only_if_no_reply": true}
|
|
|
|
sequence = relationship("Sequence", back_populates="steps")
|
|
events = relationship("SequenceEvent", back_populates="step")
|
|
|
|
|
|
class SequenceEnrollment(BaseModel):
|
|
"""Tracks a single lead's progress through a sequence."""
|
|
|
|
__tablename__ = "sequence_enrollments"
|
|
__table_args__ = (
|
|
Index("ix_enroll_sequence_status", "sequence_id", "status"),
|
|
Index("ix_enroll_lead", "lead_id"),
|
|
)
|
|
|
|
sequence_id = Column(UUID(as_uuid=True), ForeignKey("sequences.id", ondelete="CASCADE"), nullable=False)
|
|
lead_id = Column(UUID(as_uuid=True), ForeignKey("leads.id"), nullable=False)
|
|
current_step = Column(Integer, default=0)
|
|
status = Column(String(50), nullable=False, default=SequenceStatus.ACTIVE.value)
|
|
enrolled_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
|
|
completed_at = Column(DateTime(timezone=True), nullable=True)
|
|
next_step_at = Column(DateTime(timezone=True), nullable=True)
|
|
|
|
sequence = relationship("Sequence", back_populates="enrollments")
|
|
lead = relationship("Lead", foreign_keys=[lead_id])
|
|
events = relationship("SequenceEvent", back_populates="enrollment")
|
|
|
|
|
|
class SequenceEvent(BaseModel):
|
|
"""Records every message sent or event within a sequence enrollment."""
|
|
|
|
__tablename__ = "sequence_events"
|
|
__table_args__ = (
|
|
Index("ix_sevent_enrollment", "enrollment_id"),
|
|
Index("ix_sevent_step", "step_id"),
|
|
)
|
|
|
|
enrollment_id = Column(UUID(as_uuid=True), ForeignKey("sequence_enrollments.id", ondelete="CASCADE"), nullable=False)
|
|
step_id = Column(UUID(as_uuid=True), ForeignKey("sequence_steps.id"), nullable=False)
|
|
channel = Column(String(50), nullable=False)
|
|
status = Column(String(50), nullable=False, default=SequenceEventStatus.SENT.value)
|
|
sent_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
|
|
event_metadata = Column("metadata", JSONB, default=dict)
|
|
|
|
enrollment = relationship("SequenceEnrollment", back_populates="events")
|
|
step = relationship("SequenceStep", back_populates="events")
|