Chat Workspace¶
One chat box is the creation tool for everything: ask a question, run deep research, generate a presentation, prep for an exam. A mode chip row above the composer switches the AI's behavior, and generated artifacts appear inline as rich cards that get organized in a tabbed sidebar and full-width library pages. It serves trainers, learners, and B2C users.
How it works¶
Two structural rules drive the whole surface:
- Deselected = chat mode. There is no "Chat" chip. Chat is the subtraction default.
- Available modes are server-decided.
/auth/mereturnsavailable_modes, a capability-filtered list of mode IDs. The frontend only maps IDs to chip metadata;getModesByIds(ids)silently drops unknown IDs (deploy-safe). Lock state for B2C free users is layered on top.
Chat (/new-chat: always a fresh composer)
├── Mode chip row (above composer): all available modes inline, flex-wrap, no "More" dropdown
│ └── from /auth/me available_modes → getModesByIds → chip metadata
├── Deselected = chat mode (no "Chat" chip)
├── Generated artifacts appear INLINE as rich cards:
│ ├── PresentationCard → "Open in Editor" → /presentations/:id
│ └── ResearchSessionViewer → Publish as Lesson / Download DOCX / Download MD
├── Right sidebar (tabbed): [Chats] [Research] [Slides], each with "View all →"
└── Library pages (full-width grids): /research, /presentations
History (/history: "Search Chats")
└── Searchable, date-grouped conversation list (groupConversationsByDate)
Key invariant: creation happens in chat; browsing happens in the sidebar (recent items) or library pages (full history). Research opens inline (replaces the chat area, Back returns to chat). Presentations open as a separate route, /presentations/:id, which owns fullscreen Present mode, PPTX/PDF export, and metadata editing that need full-page real estate.
Modes, chips, and freemium locks¶
Role decides which chips exist; plan and quota decide which are locked. A plan can never add a chip; a role can never cause a lock.
- Mode definitions live in
services/ai-tutor/index.ts:KWILO_MODES(trainer/creator) andKWILO_STUDENT_MODES(learner: explain, practice, doubt, revision, exam_prep).ALL_KWILO_MODESis the union. - Chip row renders for all roles whenever
modes.length > 0. All chips are inline withflex-wrap. - B2C lock policy lives in one
lockedModesmemo inb2c/ChatPage/index.tsx: - Hard locks on Pro-tier modes (
question_bank,lesson_plan): not available on free at all. - Soft locks on quota-limited modes (
presentation,research,exam_prep): flip to locked whenquota.remaining <= 0. - Downloads are gated at the handler, not just the button. In-chat artifact downloads go through
renderDownload/DownloadLockButton(formats includepptxandpdf).PresentationViewerPagereads B2C quota and interceptspptx/pdfexport at the handler, opening the paywall instead. Hiding the button alone would let deep-linked or keyboard paths through. - QuotaPill (
b2c/components/QuotaPill.tsx) is a button on free plans: clicking anywhere on the pill opens the paywall. Paid plans render it as a static read-out.
New Chat and Search Chats¶
/new-chatalways starts a fresh composer. Resuming is explicit, via Search Chats or a/ai-tutor/c/:iddeep link. An earlier auto-resume-on-entry mechanism was removed;/new-chatno longer silently reopens the last conversation./history("Search Chats") is a searchable, date-grouped list.groupConversationsByDate(HistoryPage/helpers.ts) buckets by date; client-side search filters titles.+ New chatlives in the chat header (ChatHeader.tsx), wired tostartNewConversation.- Nav labels:
nav.chat→ "New Chat",nav.history→ "Search Chats".
Behavior notes¶
- Assignments stay on a dedicated page, not in chat. They are stateful (target → distribute → collect → grade → analyze); presentations are one-shot artifacts (generate → view → present → done).
- Memory v1 reuses
UserLearningProfile, no new table. v1 prefill readsmost_active_subjectsandteaching_patternsvia a tiny endpoint.
User flows¶
Generate a presentation from chat. Open /new-chat. Click the Presentation chip, type "Newton's laws for class 10, 30 minutes", send. A progress bar streams (Outlining → Writing slides → Rendering), then becomes a PresentationCard with title, slide count, badges, and "Open in Editor". Clicking it routes to /presentations/:id. Back returns to chat; the deck also appears in the Slides sidebar tab.
Browse the research library. Click the Research sidebar tab (recent sessions), then "View all →" to /research. Click a card to open /ai-tutor?research=<id>; the chat area is replaced by ResearchSessionViewer (rendered report, source citations, Publish as Lesson / Download DOCX / Download Markdown). Publish opens PublishModal with class/section targeting.
Edge cases¶
- First-time trainer (no profile): chips fall back to neutral defaults; no crash.
- LLM generation failure: progress bar becomes an error card with the message, the original prompt preserved, and Retry (resubmits the exact prompt).
- Quota exceeded: error card + Upgrade linking to subscription settings.
- Missing research
report_preview: inline bubble shows "Report is ready: download it below" with sources and download buttons. - Invalid mermaid syntax:
MermaidDiagramcallsmermaid.parse()before rendering; bad diagrams show raw code in a warning card. - Reopened conversation with a past presentation: loader rebuilds the card from
extra_data.presentation_id; falls back to plain text if the deck was deleted.
Routes¶
| Route | Component | Purpose |
|---|---|---|
/new-chat |
AITutorPage |
Always a fresh chat composer |
/history |
HistoryPage |
"Search Chats": searchable, date-grouped list |
/ai-tutor/c/:id |
AITutorPage |
Open/continue a specific conversation |
/ai-tutor |
(none) | Legacy redirect → Navigate to={ROUTES.newChat}; not a live page |
/research |
ResearchLibraryPage |
Full-width grid of all research sessions |
/presentations |
PresentationsPage |
Full-width grid/list of presentations |
/presentations/:id |
PresentationViewerPage |
HTML viewer: Present, Export, Quick Edit, Improve |
Where it lives¶
apps/web/src/services/ai-tutor/index.ts:KWILO_MODES,KWILO_STUDENT_MODES,ALL_KWILO_MODES,getModesByIds,MODE_IDSapps/web/src/pages/b2c/ChatPage/index.tsx:lockedModesmemo, download label mapapps/web/src/pages/shared/AITutorPage/components/ChatInput/ModeChipRow.tsx: inline chip rowapps/web/src/pages/shared/PresentationViewerPage.tsx: interceptspptx/pdfexport for free usersapps/backend/src/api/v1/presentations.py+apps/backend/src/api/v1/ai_tutor.py: SSE generation, trainer-context endpoint