feat: add branching columns to ai_sessions, ai_session_steps, file_uploads
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,10 @@ if TYPE_CHECKING:
|
||||
from app.models.account import Account
|
||||
from app.models.tree import Tree
|
||||
from app.models.psa_connection import PsaConnection
|
||||
from app.models.session_branch import SessionBranch
|
||||
from app.models.fork_point import ForkPoint
|
||||
from app.models.session_handoff import SessionHandoff
|
||||
from app.models.session_resolution_output import SessionResolutionOutput
|
||||
|
||||
|
||||
class AISession(Base):
|
||||
@@ -206,6 +210,28 @@ class AISession(Base):
|
||||
comment="Full LLM message history for context continuity",
|
||||
)
|
||||
|
||||
# ── Branching ──
|
||||
is_branching: Mapped[bool] = mapped_column(
|
||||
default=False,
|
||||
comment="Whether conversational branching is active for this session",
|
||||
)
|
||||
active_branch_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
UUID(as_uuid=True), nullable=True,
|
||||
comment="Currently viewed branch. No FK — soft pointer to avoid circular FK with session_branches",
|
||||
)
|
||||
handoff_count: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0,
|
||||
comment="Number of times this session has been handed off",
|
||||
)
|
||||
total_active_seconds: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0,
|
||||
comment="Cumulative active time in seconds",
|
||||
)
|
||||
total_parked_seconds: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0,
|
||||
comment="Cumulative parked time in seconds",
|
||||
)
|
||||
|
||||
# ── Relationships ──
|
||||
user: Mapped["User"] = relationship("User", foreign_keys=[user_id])
|
||||
account: Mapped["Account"] = relationship("Account")
|
||||
@@ -218,3 +244,18 @@ class AISession(Base):
|
||||
cascade="all, delete-orphan",
|
||||
order_by="AISessionStep.step_order",
|
||||
)
|
||||
branches: Mapped[list["SessionBranch"]] = relationship(
|
||||
"SessionBranch",
|
||||
foreign_keys="SessionBranch.session_id",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="SessionBranch.branch_order",
|
||||
)
|
||||
handoffs: Mapped[list["SessionHandoff"]] = relationship(
|
||||
"SessionHandoff",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="SessionHandoff.created_at",
|
||||
)
|
||||
resolution_outputs: Mapped[list["SessionResolutionOutput"]] = relationship(
|
||||
"SessionResolutionOutput",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
@@ -16,6 +16,8 @@ from app.core.database import Base
|
||||
if TYPE_CHECKING:
|
||||
from app.models.ai_session import AISession
|
||||
from app.models.script_template import ScriptGeneration
|
||||
from app.models.session_branch import SessionBranch
|
||||
from app.models.fork_point import ForkPoint
|
||||
|
||||
|
||||
class AISessionStep(Base):
|
||||
@@ -34,7 +36,7 @@ class AISessionStep(Base):
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"step_type IN ('question', 'action', 'script_generation', 'verification', "
|
||||
"'info_request', 'note', 'intake_analysis')",
|
||||
"'info_request', 'note', 'intake_analysis', 'fork')",
|
||||
name="ck_ai_session_steps_step_type",
|
||||
),
|
||||
)
|
||||
@@ -119,6 +121,24 @@ class AISessionStep(Base):
|
||||
Integer, nullable=False, default=0,
|
||||
)
|
||||
|
||||
# ── Branching ──
|
||||
branch_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("session_branches.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="NULL = pre-branching/root messages",
|
||||
)
|
||||
is_fork_point: Mapped[bool] = mapped_column(
|
||||
default=False,
|
||||
comment="Whether this step triggered a fork",
|
||||
)
|
||||
fork_point_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("fork_points.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
# ── Timestamps ──
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
|
||||
|
||||
@@ -3,7 +3,7 @@ import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import String, Integer, DateTime, ForeignKey
|
||||
from sqlalchemy import String, Integer, DateTime, ForeignKey, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
@@ -30,3 +30,27 @@ class FileUpload(Base):
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
|
||||
)
|
||||
|
||||
# ── AI description + branching context ──
|
||||
ai_description: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True,
|
||||
comment="AI-generated one-sentence description of the file",
|
||||
)
|
||||
extracted_content: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True,
|
||||
comment="Extracted text from logs/configs",
|
||||
)
|
||||
content_summary: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True,
|
||||
comment="AI summary for long text files (>2000 tokens)",
|
||||
)
|
||||
uploaded_on_branch_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("session_branches.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
uploaded_at_step_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("ai_session_steps.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user