Arag / app /schemas /chatbot.py
AuthorBot
Remove embed debug instrumentation and fix widget intake/overflow.
576296a
Raw
History Blame Contribute Delete
4.52 kB
"""Author RAG Chatbot SaaS β€” Chatbot API Schemas."""
from pydantic import BaseModel, EmailStr, Field
class ChatRequest(BaseModel):
"""Incoming chat message from the widget."""
session_id: str
message: str = Field(..., min_length=1, max_length=2000)
selected_book_id: str | None = None
ask_all_books: bool = False
class LinkItem(BaseModel):
label: str
url: str
type: str
icon: str
class BookSelectorItem(BaseModel):
id: str
title: str
tagline: str | None
cover_url: str | None
class ChatResponse(BaseModel):
"""Response from one chat turn."""
text: str
links: list[LinkItem] = []
has_links: bool = False
book_selector: list[BookSelectorItem] | None = None
session_id: str
intent: str = "question"
follow_ups: list[str] = []
class BookInfo(BaseModel):
id: str
title: str
tagline: str | None
cover_path: str | None
ai_summary: str | None = None
purchase_url: str | None = None
class FarewellRequest(BaseModel):
session_id: str
selected_book_id: str | None = None
class SessionInitRequest(BaseModel):
"""Optional body for session/init β€” carries the visitor_uid from widget localStorage.
The widget sends the visitor_uid it stored on the previous visit.
If absent or null, the server generates a new one (first visit).
HF Spaces note: Using request body instead of cookies because
the widget runs inside an iframe (third-party context) where
cookies are blocked by Safari ITP and Firefox ETP.
"""
visitor_uid: str | None = Field(
default=None,
min_length=36,
max_length=36,
description="UUID previously returned by session/init and stored in widget localStorage.",
)
visitor_name: str | None = Field(
default=None,
min_length=1,
max_length=100,
description="Visitor display name collected before first chat.",
)
visitor_email: EmailStr | None = Field(
default=None,
description="Visitor email collected before first chat.",
)
class WidgetAppearanceResponse(BaseModel):
"""Subscription-authenticated widget chrome (theme/position/auto-open).
Used before chat opens so the bubble matches Widget Config without
creating a session (profile gate may block session/init).
"""
bot_name: str
widget_theme: str
widget_position: str
widget_auto_open_delay: int = 0
require_visitor_profile: bool = True
class SessionInitResponse(BaseModel):
"""Response from session initialization."""
session_id: str
bot_name: str
welcome_message: str
widget_theme: str
widget_position: str = "bottom-right"
widget_auto_open_delay: int = 0
books: list[BookInfo]
show_book_selector: bool
visitor_uid: str
require_visitor_profile: bool = True
profile_complete: bool = True
visitor_name: str | None = None
visitor_email: str | None = None
remove_branding: bool = False
custom_widget_css: str | None = None
# ── Phase 7: Polling Streaming ────────────────────────────────────────────────
class StreamStartResponse(BaseModel):
"""Returned immediately when a streaming job is queued.
Phase 7: Replaces SSE (blocked by nginx). Widget polls /poll/{job_id}
until status='done', then renders the full response.
"""
job_id: str
status: str = "queued"
class PollResponse(BaseModel):
"""Returned on each poll of /poll/{job_id}.
status: 'pending' | 'done' | 'error'
result: only present when status='done'
error: only present when status='error'
"""
job_id: str
status: str
result: ChatResponse | None = None
error: str | None = None
# ── Visitor Interaction Schemas (Phase 6B for chat.py) ───────────────────────
class TrackClickRequest(BaseModel):
"""Track purchase/preview link click."""
session_id: str | None = None
book_id: str | None = None
link_type: str = "unknown"
class FeedbackRequest(BaseModel):
"""Thumbs up/down on a bot message."""
session_id: str | None = None
message_index: int | None = Field(None, ge=0)
reaction: str = Field(..., pattern=r"^(up|down)$")
class RateSessionRequest(BaseModel):
"""Star rating for a chat session."""
session_id: str | None = None
rating: int = Field(..., ge=1, le=5)