feat: conversational branching, AI markers, TaskLane improvements, collapsible sidebar #120

Merged
chihlasm merged 58 commits from feat/conversational-branching into main 2026-03-27 13:16:44 +00:00
2 changed files with 47 additions and 0 deletions
Showing only changes of commit c9798514a9 - Show all commits

View File

@@ -50,6 +50,10 @@ from .psa_activity_log import PsaActivityLog
from .file_upload import FileUpload
from .ai_session_embedding import AISessionEmbedding
from .beta_feedback import BetaFeedback
from .session_branch import SessionBranch
from .fork_point import ForkPoint
from .session_handoff import SessionHandoff
from .session_resolution_output import SessionResolutionOutput
__all__ = [
"User",
@@ -114,4 +118,8 @@ __all__ = [
"FileUpload",
"AISessionEmbedding",
"BetaFeedback",
"SessionBranch",
"ForkPoint",
"SessionHandoff",
"SessionResolutionOutput",
]

View File

@@ -0,0 +1,39 @@
"""Session resolution output model — three deliverables generated on resolve."""
import uuid
from datetime import datetime, timezone
from typing import Optional, Any
from sqlalchemy import String, Text, DateTime, ForeignKey, CheckConstraint, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID, JSONB
from app.core.database import Base
class SessionResolutionOutput(Base):
"""One of three resolution deliverables: PSA ticket notes, KB article, client summary.
UNIQUE(session_id, output_type) + upsert so outputs can be regenerated.
"""
__tablename__ = "session_resolution_outputs"
__table_args__ = (
CheckConstraint("output_type IN ('psa_ticket_notes', 'knowledge_base', 'client_summary')", name="ck_session_resolution_outputs_output_type"),
CheckConstraint("status IN ('draft', 'approved', 'pushed', 'rejected')", name="ck_session_resolution_outputs_status"),
UniqueConstraint("session_id", "output_type", name="uq_session_resolution_session_type"),
)
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
session_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("ai_sessions.id", ondelete="CASCADE"), nullable=False, index=True)
output_type: Mapped[str] = mapped_column(String(30), nullable=False)
generated_content: Mapped[str] = mapped_column(Text, nullable=False)
structured_data: Mapped[Optional[dict[str, Any]]] = mapped_column(JSONB, nullable=True, comment="For KB: {symptoms, root_cause, steps, tags}")
edited_content: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
status: Mapped[str] = mapped_column(String(20), nullable=False, default="draft")
pushed_to: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
pushed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
pushed_reference: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
generated_by_model: Mapped[str] = mapped_column(String(50), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
# Relationships
session = relationship("AISession")