4.7 KiB
Chat Client
Package: @nexusai/chat-client
Location: packages/chat-client
Deployed on: Mini PC 2 (192.168.0.205)
URL: https://nexus.jellystorm.com (behind Authelia SSO)
Purpose
Browser-based chat interface for NexusAI. Communicates exclusively with the orchestration service — no direct access to memory, embedding, or inference services. Served as static files by Caddy on Mini PC 2.
Dependencies
react+react-dom— UI frameworkuuid— session ID generationvite+@vitejs/plugin-react— build tooling
Build
cd packages/chat-client
npm run build # outputs to dist/
npm run dev # local dev server on port 5173
Vite bakes environment variables into the bundle at build time. The .env
file is only needed on the machine running the build, not where files are served.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| VITE_ORCHESTRATION_URL | No | '' (empty) |
Orchestration base URL. Empty string uses Vite proxy in dev, Caddy proxy in production. |
Internal Structure
src/
├── api/
│ └── orchestration.js # All fetch calls to the orchestration service
├── hooks/
│ ├── useSession.js # Session list, history loading, active session state
│ └── useChat.js # Message sending, SSE streaming, message state
├── components/
│ ├── App.jsx # Root component — layout and shared state
│ ├── SessionList.jsx # Left sidebar — session list and new chat button
│ ├── ChatWindow.jsx # Centre panel — message thread and input bar
│ ├── MessageBubble.jsx # Individual message bubble (user or assistant)
│ └── InfoPanel.jsx # Right panel — model selector and session metadata
├── index.css # Global reset and CSS variables
└── main.jsx # React entry point
Layout
Three-panel layout with collapsible sidebars: ┌─────────────────┬──────────────────────────┬─────────────┐ │ Session List │ Chat Window │ Info Panel │ │ (collapsible) │ │ (collapsible)│ │ │ [message thread] │ │ │ + New Chat │ │ Model │ │ │ │ Session ID │ │ Session 1 │ │ Token count │ │ Session 2 │ │ │ │ │ [input bar] │ │ └─────────────────┴──────────────────────────┴─────────────┘
On mobile, sidebars collapse to a 56px icon rail. The centre chat window always fills the remaining space.
API Layer
All orchestration calls are centralised in src/api/orchestration.js:
| Function | Method | Path | Description |
|---|---|---|---|
fetchSessions |
GET | /sessions | Load session list for sidebar |
fetchSessionHistory |
GET | /sessions/:id/history | Load episode history on session select |
sendMessage |
POST | /chat | Send message, await full response |
streamMessage |
POST | /chat/stream | Send message, receive SSE token stream |
streamMessage returns an abort function — call it to cancel a stream mid-flight.
It uses a buffer pattern to handle SSE chunks that may span multiple network packets.
Streaming
The chat input sends messages via POST /chat/stream. Tokens arrive as SSE events:
data: {"text":"Hello"}
data: {"text":" Tim"}
data: {"done":true}
An empty assistant bubble is appended immediately when the stream opens, then
updated token by token using updateLastMessage. The blinking cursor in
MessageBubble is shown while message.streaming === true and disappears
when done is received.
Model Selector
Available models are defined in InfoPanel.jsx:
| Label | Value |
|---|---|
| Companion | companion:latest |
| Mistral Nemo | mistral-nemo:latest |
| Coder | coder:latest |
| Qwen 2.5 Coder 14B | qwen2.5-coder:14b |
The selected model is passed with every chat request. To add a new model,
update the MODELS array in InfoPanel.jsx.
Session Management
Sessions are identified by a external_id — a human-readable string or UUID
generated client-side. New sessions are created locally with uuid and auto-registered
in the memory service on the first message. The session list refreshes after each
completed response to surface newly created sessions.