A clean, fast chat dashboard with expert personas, authenticated personal workspaces, streaming replies, attachments, and readable code blocks.
Built with Next.js (App Router) + Vercel AI SDK + Supabase.
- Expert personas: switch between different “experts” with their own system prompts.
- Supabase Auth: sign up, sign in, sign out, and change password flows are built in.
- Multi-session chat: sessions list + titles + last-message preview.
- User-isolated chat history: sessions and messages are scoped to the authenticated user.
- Streaming responses: responsive UI while the model streams tokens.
- Attachments: upload images/files to Supabase Storage and send them with messages.
- Markdown + code blocks: readable rendering with syntax highlighting.
Generated with Archify from the repository source. The diagram shows the server bootstrap, client chat flow, persistence, and external integrations.
Interactive architecture diagram · Diagram source
Download the HTML and open it in a browser to explore component details, pinned source links, themes, and image exports.
- Server bootstrap:
app/page.tsxverifies the Supabase user and redirects guests to/auth.lib/botchat/bootstrap.tsloads experts, sessions, and the active conversation before rendering the dashboard. - Client chat flow:
components/botchat/dashboard.tsxcoordinatesSessionsPanel,ChatPanel,useChat, session switching, optimistic updates, uploads, and debounced message persistence. Chat rendering reusesai-elementsand shareduicomponents. - API and model context:
/api/chatresolves the expert prompt, model, and reasoning effort, loads referenced local skills, selects relevant history through optional TypeSafe/Jev evaluation, and streams OpenAI replies. Missing or failed Jev evaluation falls back to the previous turn and current message. Separate routes handle sessions, experts, prompt generation, and configurable ASR/TTS services. - Persistence and attachments: Supabase Postgres stores shared experts and user-owned sessions/messages protected by RLS.
/api/messages/syncsaves messages, titles, previews, token totals, and complete summary batches; saved summaries support session search and are excluded from chat context./api/attachments/uploadstores files in the publicchat-attachmentsbucket and returns URLs before the client sends the message.
Create .env.local:
cp .env.local.example .env.localRequired:
OPENAI_API_KEY– your OpenAI API keyOPENAI_MODEL– e.g.gpt-5-mini(model is not hardcoded in code)
Optional saved conversation summaries:
OPENAI_CONVERSATION_SUMMARY_MODEL– model used for session title and context summary generation; falls back toOPENAI_MODELBOTCHAT_COMPACT_AFTER_USER_MESSAGE_COUNT– despite the retained variable name, counts all user and assistant messages. It controls both the number of messages per saved summary batch and the maximum number of historical messages considered for relevance selection, excluding the current turn; defaults to6(normally three user/assistant turns). Set it to8for four turns.
After messages are saved, each full batch is summarized independently and marked as summarized. With a batch size of 6, messages 1–6 form one summary and messages 7–12 form the next; incomplete batches wait for more messages. Each summary replaces the session's context_summary field without combining it with the previous summary. Summary generation uses only message count; the former BOTCHAT_COMPACT_AFTER_TOTAL_TOKENS setting is ignored.
Optional history relevance routing:
TYPESAFE_API_KEY– server-only TypeSafe key for Jev to evaluate each historical message within the configured conversation window against the current messageTYPESAFE_MODEL– Jev model for history relevance evaluation; defaults tojev-latest
One Jev request batches an individual question for each historical message. Only messages with a Noul true probability of at least 0.9 are sent to the chat model, in chronological order; selecting one message does not include its entire turn or the other turns. Lower probabilities are treated as unrelated and discarded, without triggering fallback. Saved summaries are not sent to Jev or the chat model. If Jev fails, times out after 5 seconds, or its key is missing, the request includes only the previous conversation turn and the current message. Summary generation and message persistence continue regardless of this selection.
Supabase (recommended for sessions/experts/attachments):
PUBLIC_SUPABASE_URLPUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY
Supabase admin (required for expert deletion to remove all linked sessions/messages):
SUPABASE_SERVICE_ROLE_KEY(server-only secret; never expose to browser/client code)
npm install
npm run devOpen http://localhost:3000.
Authentication UI is available at http://localhost:3000/auth.
- Create a Supabase project.
- Run
supabase/schema.sqlin the Supabase SQL Editor. - Enable Supabase Auth email/password sign-in for the project.
- If email confirmation is enabled, new users must activate their account from email before signing in.
- Create a public Storage bucket named
chat-attachments.
The database schema now assumes authenticated access:
chat_sessions.user_idowns each chat session.chat_messagesinherit ownership throughsession_id.- Row Level Security allows users to read/write only their own sessions and messages.
expertsremain global/shared and are not user-owned.
This repo includes a production Docker build for the Next.js app:
docker compose up -d --buildApp is exposed at http://localhost:3202 (mapped from container 3000).
Tip: for Docker envs, set OPENAI_API_KEY, OPENAI_MODEL, OPENAI_CONVERSATION_SUMMARY_MODEL, BOTCHAT_COMPACT_AFTER_USER_MESSAGE_COUNT, TYPESAFE_API_KEY, TYPESAFE_MODEL, PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY, and SUPABASE_SERVICE_ROLE_KEY in your deployment platform or docker-compose.yml.
app/– Next.js routes (UI + API routes)components/botchat/– chat UI (sessions, experts, input, panels)components/ai-elements/– AI-friendly UI primitives (rendering, message building blocks)lib/– shared utilities + Supabase clientssupabase/schema.sql– database schema + RLS policies
npm run dev # local dev
npm run build # production build
npm run start # run production server on :3000