Skip to content

Latest commit

 

History

History
49 lines (34 loc) · 4.27 KB

File metadata and controls

49 lines (34 loc) · 4.27 KB
type App Guide
title Chat Platform
description The apps/chat island — app router, zustand, assistant-ui, route-handler auth guards, and the model registry.
timestamp 2026-07-07
tags
frontend
chat
ai

Chat Platform (apps/chat)

Migration in flight (2026-07): the chat backend is slated to move to a Mastra-based apps/chat-api service (PR #5126, draft; tutor architecture in PR #5129). This page describes the current AI-SDK reality and stays authoritative until those PRs merge — but check their status before investing heavily in the route-handler/AI-SDK layer. Staged doc/skill changes: project/plans_future/2026-07-07-wiki-skills-migration-roadmap.md.

This app is an island — do not apply the pages-router conventions here. It is the only Next.js app-router app (port 3004), talks to the backend's Prisma models directly through its own API route handlers (no GraphQL ops), uses zustand for client state (nowhere else in the repo), and renders chat via assistant-ui (@assistant-ui/react + react-ai-sdk) over the Vercel AI SDK (@ai-sdk/*). Domain models live in packages/prisma chat.prisma (chatbots, threads, messages, credits as Decimal(18,6)).

Structure

  • src/app/api/chatbots/[chatbotId]/… — route handlers (chat streaming, attachments, threads).
  • src/lib/server/ — server-only helpers: apiGuards.ts, chatModelRegistry.ts, openaiResponsesOptions.ts, imagePreview.ts.
  • src/stores/ — zustand: chatStore, composerStore, settingsStore.
  • src/components/thread.tsx and src/hooks/ — assistant-ui composition.
  • Local model proxy: the litellm compose service (port 4000).

Auth guard pattern (route handlers)

Three steps: getParticipantIdgetChatbotOr404requireParticipation. The composed helper withChatbotAuth(req, chatbotId) (src/lib/server/apiGuards.ts) covers the standard { courseId: true } case — use it for new routes; fall back to the individual guards only for a custom chatbot select. Participant identity comes from the same participant JWT cookies as the PWA (Auth Model); local chat dev therefore needs the backend's APP_SECRET and DATABASE_URL visible to the chat app, or cookies won't verify and Prisma can't load chatbots.

Model registry and credits

chatModelRegistry.ts loads CHAT_MODEL_REGISTRY_JSON (deployment override in deploy/env-uzh-*/values.yaml). Registry gotchas that have caused production incidents:

  • Omitted supportsImageAttachments defaults to false — every image-capable model must set it explicitly in deployment values or the attach button disappears.
  • Zero-credit course chatbots need a usable fallback model (CHAT_FALLBACK_MODEL_ID, default gpt-4.1-mini) AND explicit chatbot allowedModelIds must include it. Audit/fix with packages/prisma-data/src/scripts/2026-06-15_ensure_chatbot_fallback_model.ts.
  • OpenAI Responses backends: keep CHAT_OPENAI_STORE_RESPONSES=true in shared/staged deployments — with store: false, LiteLLM/Azure can return "item not found" when a model references prior response items across tool-call steps. Local OpenRouter-style setups can leave it false.

Credit fields are Prisma Decimal — never truthy-check them (Data & Migrations).

Client-state gotchas

  • Zustand async actions must set fallback state in catch, not just log — otherwise the UI hangs in loading state on network errors.
  • Edited-message image hydration needs the persisted source message id (attachmentSourceMessageId) distinct from the fresh local message id (src/hooks/useThreadManagement.ts, src/stores/chatStore.ts).
  • ComposerPrimitive.AttachmentDropzone must wrap both normal and edit composer roots — it owns the drag/drop capture that prevents native browser file navigation (src/components/thread.tsx).
  • Login redirects: src/app/noLogin/page.tsx must pass an absolute chat URL as the PWA login redirect_to; a relative path makes the PWA redirect to its own domain and 404.

Testing

Pure-logic vitest lives in apps/chat/test/ (safe without services); apps/chat/vitest.config.ts mirrors the @/* alias from the app tsconfig — keep them in sync. E2E coverage is Playwright-only (playwright/tests/Y-chat.spec.ts — no Cypress counterpart).