char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
521a64cd0
char/aliceai-t5-moe
llama-ui
A modern, feature-rich web interface for llama-server built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities.
Llama UI supports two server operation modes:
- MODEL mode - Single model operation (standard llama-server)
- ROUTER mode - Multi-model operation with dynamic model loading/unloading
Table of Contents
- Features
- Getting Started
- Tech Stack
- Build Pipeline
- Architecture
- Data Flows
- Architectural Patterns
- Testing
Features
Chat Interface
- Streaming responses with real-time updates
- Reasoning content - Support for models with thinking/reasoning blocks
- Dark/light theme with system preference detection
- Responsive design for desktop and mobile
File Attachments
- Images - JPEG, PNG, GIF, WebP, SVG (with PNG conversion)
- Documents - PDF (text extraction or image conversion for vision models)
- Audio - MP3, WAV for audio-capable models
- Text files - Source code, markdown, and other text formats
- Drag-and-drop and paste support with rich previews
Conversation Management
- Branching - Branch messages conversations at any point by editing messages or regenerating responses, navigate between branches
- Regeneration - Regenerate responses with optional model switching (ROUTER mode)
- Import/Export - JSON format for backup and sharing
- Search - Find conversations by title or content
Advanced Rendering
- Syntax highlighting - Code blocks with language detection
- Math formulas - KaTeX rendering for LaTeX expressions
- Markdown - Full GFM support with tables, lists, and more
Multi-Model Support (ROUTER mode)
- Model selector with Loaded/Available groups
- Automatic loading - Models load on selection
- Modality validation - Prevents sending images to non-vision models
- LRU unloading - Server auto-manages model cache
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Shift+Ctrl/Cmd+O | New chat |
Shift+Ctrl/Cmd+E | Edit conversation |
Shift+Ctrl/Cmd+D | Delete conversation |
Ctrl/Cmd+K | Search conversations |
Ctrl/Cmd+B | Toggle sidebar |
Developer Experience
- Request tracking - Monitor token generation with
/slotsendpoint - Storybook - Component library with visual testing
- Hot reload - Instant updates during development
Getting Started
Prerequisites
- Node.js 18+ (20+ recommended)
- npm 9+
- llama-server running locally (for API access)
1. Install Dependencies
cd tools/uinpm ci
2. Start llama-server
In a separate terminal, start the backend server:
# Single model (MODEL mode) ./llama-server -m model.gguf# Multi-model (ROUTER mode) ./llama-server --models-dir /path/to/models
3. Start Development Servers
npm run dev
This starts:
- Vite dev server at
http://localhost:5173- The main UI frontend app - Storybook at
http://localhost:6006- Component documentation
The Vite dev server proxies API requests to SERVER_ORIGIN (with fallback to default llama-server 8080 port):
// vite.config.ts proxy configuration proxy :{ '/v1' :SERVER_ORIGIN , '/props' :SERVER_ORIGIN , '/models' :SERVER_ORIGIN , '/tools' :SERVER_ORIGIN , '/slots' :SERVER_ORIGIN , '/cors-proxy' :SERVER_ORIGIN },
Development Workflow
- Open
http://localhost:5173in your browser - Make changes to
.svelte,.ts, or.cssfiles - Changes hot-reload instantly
- Use Storybook at
http://localhost:6006for isolated component development
Tech Stack
| Layer | Technology | Purpose |
|---|---|---|
| Framework | SvelteKit + Svelte 5 | Reactive UI with runes ($state, $derived, $effect) |
| UI Components | shadcn-svelte + bits-ui | Accessible, customizable component library |
| Styling | TailwindCSS 4 | Utility-first CSS with design tokens |
| Database | IndexedDB (Dexie) | Client-side storage for conversations and messages |
| Build | Vite | Fast bundling with static adapter |
| Testing | Playwright + Vitest + Storybook | E2E, unit, and visual testing |
| Markdown | remark + rehype | Markdown processing with KaTeX and syntax highlighting |
Key Dependencies
{
"svelte" : "^5.0.0" ,
"bits-ui" : "^2.8.11" ,
"dexie" : "^4.0.11" ,
"pdfjs-dist" : "^5.4.54" ,
"highlight.js" : "^11.11.1" ,
"rehype-katex" : "^7.0.1"
}
Build Pipeline
Development Build
npm run dev
Runs Vite in development mode with:
- Hot Module Replacement (HMR)
- Source maps
- Proxy to llama-server
Production Build
npm run build
The build process:
- Vite Build - Bundles all TypeScript, Svelte, and CSS
- Static Adapter - Outputs to
../../build/tools/ui/dist(llama-server's static file directory) - Post-Build Script - Cleans up intermediate files
- Custom Plugin - Creates
index.htmlwith:- Inlined favicon as base64
- GZIP compression (level 9)
- Deterministic output (zeroed timestamps)
tools/ui/ → build → build/tools/ui/dist/ ├── src/ ├── index.html (served by llama-server) ├── static/ └── (favicon inlined) └── ...
SvelteKit Configuration
// svelte.config.js adapter:adapter ({ pages :'../../build/tools/ui/dist' , // Output directory assets :'../../build/tools/ui/dist' , // Static assets fallback :'index.html' , // SPA fallback strict :true }), output :{ bundleStrategy :'inline' // Single-file bundle }
Integration with llama-server
llama-ui is embedded directly into the llama-server binary:
npm run buildoutputsindex.htmltobuild/tools/ui/dist/- llama-server compiles this into the binary at build time
- When accessing
/, llama-server serves the bundled HTML
This results in a single portable binary with the full Llama UI included.
Architecture
Llama UI follows a layered architecture with unidirectional data flow:
Routes → Components → Hooks → Stores → Services → Storage/API
High-Level Architecture
flowchart TB
subgraph Routes["📍 Routes"]
R1["/ (Welcome)"]
R2["/chat/[id]"]
R3["/mcp-servers"]
R4["/search"]
R5["/settings"]
RL["+layout.svelte"]
end
subgraph Components["🧩 Components"]
C_Screen["ChatScreen"]
C_Form["ChatForm"]
C_Messages["ChatMessages"]
C_Sidebar["ChatSidebar"]
C_Models["ModelsSelector"]
C_Settings["ChatSettings"]
C_Mcp["McpServers"]
end
subgraph Hooks["🔌 Hooks"]
H1["use-chat-screen-active-model"]
H2["use-processing-state"]
H3["use-context-gauge"]
H4["use-models-selector"]
H5["use-tools-panel"]
end
subgraph Stores["🗄️ Stores"]
S1["chatStore"]
S2["conversationsStore"]
S3["modelsStore"]
S4["mcpStore"]
S5["agenticStore"]
S6["serverStore"]
S7["settingsStore"]
S8["toolsStore"]
end
subgraph Services["⚙️ Services"]
SV1["ChatService"]
SV2["ModelsService"]
SV3["PropsService"]
SV4["DatabaseService"]
SV5["MCPService"]
SV6["ToolsService"]
SV7["SandboxService"]
end
subgraph Storage["💾 Storage"]
ST1["IndexedDB"]
ST2["LocalStorage"]
end
subgraph APIs["🌐 llama-server"]
API1["/v1/chat/completions"]
API2["/props"]
API3["/models/*"]
API4["/tools"]
end
R1 & R2 --> C_Screen
RL --> C_Sidebar
C_Screen --> C_Form & C_Messages & C_Settings
C_Screen --> H1 & H2 & H3
C_Models --> H4
C_Mcp --> S4
C_Screen --> S1 & S2 & S3
C_Models --> S3
H1 --> S3
S1 --> SV1 & SV4
S2 --> SV4
S3 --> SV2 & SV3
S4 --> SV5
S5 --> SV1 & SV5 & SV6 & SV7
SV4 --> ST1
SV1 --> API1
SV2 --> API3
SV3 --> API2
SV6 --> API4
Layer Breakdown
Routes (src/routes/)
/- Welcome screen, creates new conversation/chat/[id]- Active chat interface/mcp-servers- MCP server management/search- Conversation search/settings- Settings (optional[[section]])+layout.svelte- Sidebar, navigation, global initialization
Components (src/lib/components/)
Components are organized in app/ (application-specific) and ui/ (shadcn-svelte primitives).
Chat Components (app/chat/):
| Component | Responsibility |
|---|---|
ChatScreen/ | Main chat container, coordinates message list, input form, and attachments |
ChatForm/ | Message input textarea with file upload, paste handling, keyboard shortcuts |
ChatMessages/ | Message list with branch navigation, regenerate/continue/edit actions |
ChatAttachments/ | File attachment previews, drag-and-drop, PDF/image/audio handling |
ChatSettings/ | Parameter sliders (temperature, top-p, etc.) with server default sync |
ChatSidebar/ | Conversation list, search, import/export, navigation |
Dialog Components (app/dialogs/):
| Component | Responsibility |
|---|---|
DialogChatSettings | Full-screen settings configuration |
DialogModelInformation | Model details (context size, modalities, parallel slots) |
DialogChatAttachmentPreview | Full preview for images, PDFs (text or page view), code |
DialogConfirmation | Generic confirmation for destructive actions |
DialogConversationTitleUpdate | Edit conversation title |
Server/Model Components (app/server/, app/models/):
| Component | Responsibility |
|---|---|
ServerErrorSplash | Error display when server is unreachable |
ModelsSelector | Model dropdown with Loaded/Available groups (ROUTER mode) |
Shared UI Components (app/misc/):
| Component | Responsibility |
|---|---|
MarkdownContent | Markdown rendering with KaTeX, syntax highlighting, copy buttons |
SyntaxHighlightedCode | Code blocks with language detection and highlighting |
ActionButton, ActionDropdown | Reusable action buttons and menus |
BadgeModality, BadgeInfo | Status and capability badges |
Hooks (src/lib/hooks/)
Hooks are the thin view-layer between components and stores: they own UI concerns (scroll, drag-and-drop, keyboard shortcuts, pickers, selection) and translate store state into view state.
| Hook | Responsibility |
|---|---|
use-chat-screen-active-model | Active model resolution + modality capability detection |
use-processing-state | View over chatStore.processing for streaming progress/tokens |
use-context-gauge | View over contextStatsStore for the context usage gauge |
use-models-selector | Model selector dropdown state (loaded/available groups) |
use-tools-panel | Tools panel state |
use-reasoning-menu | Reasoning-effort menu state |
use-attachment-menu | Attachment menu + modality flags |
use-draft-messages | Per-chat draft message/files persistence |
use-chat-form-pickers | Chat form pickers (commands, mentions) |
use-debounced-search | Shared debounced async search for pickers |
use-picker-navigation | Picker keyboard navigation |
use-chat-message-edit-context | Message edit context (content + extras) |
use-chat-screen-drag-and-drop | Drag-and-drop state machine |
use-chat-screen-file-upload | File upload queue + capability validation |
use-chat-screen-scroll | Scroll container binding + navigation guard |
use-auto-scroll | Auto-scroll controller for streaming |
use-marquee-selection | Shift+click / marquee range selection |
use-keyboard-shortcuts | Global keyboard shortcuts |
use-settings-navigation | Settings section navigation |
use-pwa | PWA install/update + version mismatch detection |
Stores (src/lib/stores/)
Stores own reactive application state as Svelte 5 runes. Larger stores are split into directories and compose focused sub-stores behind a narrow host interface (see Architectural Patterns).
| Store | Responsibility |
|---|---|
chatStore | Chat lifecycle, streaming, abort control, error handling; composes processing, activity, streams, flows |
conversationsStore | Conversation CRUD, message branching, navigation, import/export; composes preferences |
modelsStore | Model list, selection, loading/unloading (ROUTER); composes props, status |
mcpStore | MCP host role: multi-server lifecycle, tool routing; composes health, resources |
agenticStore | Multi-turn agentic loop orchestration, tool execution; composes gates |
serverStore | Server connection state, /props, role detection, modalities |
settingsStore | User preferences, theme, parameter sync with server defaults |
toolsStore | Tool registry: server + MCP tools, enabled set for the LLM |
permissionsStore | Persisted tool permission grants |
contextStatsStore | Context window usage for the active conversation |
draftMessagesStore | Per-chat draft message/files |
deviceStore | Browser environment signals (mobile, OS, theme) |
versionStore | Build version information |
Services (src/lib/services/)
Services are a stateless protocol layer: static methods, pure I/O, no reactive state. Stores consume them for all API and storage access.
| Service | Responsibility |
|---|---|
ChatService | /v1/chat/completions streaming + SSE parsing, message format conversion |
ModelsService | /models, /models/load, /models/unload |
PropsService | /props, /props?model= |
DatabaseService | IndexedDB operations via Dexie |
MCPService | MCP protocol: transports, connect, list/execute tools, prompts, resources |
ToolsService | Server tool list/execute/stream (/tools) |
SandboxService | Browser JS execution in a sandboxed worker |
ParameterSyncService | Syncs settings with server defaults |
ConversationTransferService | Conversation import/export JSONL + ZIP format |
MigrationService | Non-destructive localStorage/IndexedDB migrations |
RouterService | Dynamic route URL construction |
Data Flows
MODEL Mode (Single Model)
sequenceDiagram
participant User
participant UI
participant Stores
participant DB as IndexedDB
participant API as llama-server
Note over User,API: Initialization
UI->>Stores: initStores() (awaited by route loads)
Stores->>Stores: run migrations
Stores->>DB: load conversations (background)
Stores->>API: GET /props
API-->>Stores: server config
Stores->>API: GET /v1/models
API-->>Stores: single model (auto-selected)
Note over User,API: Chat Flow
User->>UI: send message
Stores->>DB: save user message
Stores->>API: POST /v1/chat/completions (stream)
loop streaming
API-->>Stores: SSE chunks
Stores-->>UI: reactive update
end
Stores->>DB: save assistant message
ROUTER Mode (Multi-Model)
sequenceDiagram
participant User
participant UI
participant Stores
participant API as llama-server
Note over User,API: Initialization
Stores->>API: GET /props
API-->>Stores: {role: "router"}
Stores->>API: GET /models
API-->>Stores: models[] with status
Note over User,API: Model Selection
User->>UI: select model
alt model not loaded
Stores->>API: POST /models/load
loop poll status
Stores->>API: GET /models
end
Stores->>API: GET /props?model=X
end
Stores->>Stores: validate modalities
Note over User,API: Chat Flow
Stores->>API: POST /v1/chat/completions {model: X}
loop streaming
API-->>Stores: SSE chunks + model info
end
Architectural Patterns
1. Reactive State with Svelte 5 Runes
All stores use Svelte 5's fine-grained reactivity:
// Store with reactive state class ChatStore { #isLoading= $state ( false ); #currentResponse= $state ( '' ); // Derived values auto-update get isStreaming () { return $derived ( this . #isLoading&& this . #currentResponse. length > 0 ); } } // Exported reactive accessors export const isLoading = () => chatStore . isLoading ; export const currentResponse = () => chatStore . currentResponse ;
2. Unidirectional Data Flow
Data flows in one direction, making state predictable:
flowchart LR
subgraph UI["UI Layer"]
A[User Action] --> B[Component]
end
subgraph State["State Layer"]
B --> C[Store Method]
C --> D[State Update]
end
subgraph IO["I/O Layer"]
C --> E[Service]
E --> F[API / IndexedDB]
F -.->|Response| D
end
D -->|Reactive| B
Components dispatch actions to stores, stores coordinate with services for I/O, and state updates reactively propagate back to the UI.
3. Per-Conversation State
Enables concurrent streaming across multiple conversations. Loading is tracked
per conversation by the activity ledger (chatStore.activity), while streaming
state and abort controllers live in per-conversation maps:
class ChatStore { chatStreamingStates = new SvelteMap < string , { response :string ; messageId :string }>(); abortControllers = new SvelteMap < string , AbortController >(); }
4. Message Branching with Tree Structure
Conversations are stored as a tree, not a linear list:
interface DatabaseMessage { id :string ; parent :string | null ; // Points to parent message children :string []; // List of child message IDs // ... } interface DatabaseConversation { currentNode :string ; // Currently viewed branch tip // ... }
Navigation between branches updates currentNode without losing history.
5. Layered Service Architecture
Stores handle state; services handle I/O:
┌─────────────────┐ │ Stores │ Business logic, state management ├─────────────────┤ │ Services │ API calls, database operations ├─────────────────┤ │ Storage/API │ IndexedDB, LocalStorage, HTTP └─────────────────┘
6. Server Role Abstraction
Single codebase handles both MODEL and ROUTER modes:
// serverStore.ts get isRouterMode () { return this . role === ServerRole . ROUTER ; } // Components conditionally render based on mode { #ifisRouterMode ()} < ModelsSelector / > { / if}
7. Modality Validation
Prevents sending attachments to incompatible models. The
use-chat-screen-active-model hook derives the active model's capabilities
from modelsStore.props:
// use-chat-screen-active-model hook const hasVisionModality = $derived . by (() => modelsStore . props . modelSupportsVision ( activeModelId )); const hasAudioModality = $derived . by (() => modelsStore . props . modelSupportsAudio ( activeModelId ));
8. Persistent Storage Strategy
Data is persisted across sessions using two storage mechanisms:
flowchart TB
subgraph Browser["Browser Storage"]
subgraph IDB["IndexedDB (Dexie)"]
C[Conversations]
M[Messages]
end
subgraph LS["LocalStorage"]
S[Settings Config]
O[User Overrides]
T[Theme Preference]
end
end
subgraph Stores["Svelte Stores"]
CS[conversationsStore] --> C
CS --> M
SS[settingsStore] --> S
SS --> O
SS --> T
end
- IndexedDB: Conversations and messages (large, structured data)
- LocalStorage: Settings, user parameter overrides, theme (small key-value data)
- Memory only: Server props, model list (fetched fresh on each session)
Testing
Test Types
| Type | Tool | Location | Command |
|---|---|---|---|
| Unit | Vitest | tests/unit/ | npm run test:unit |
| UI/Visual | Storybook + Vitest | tests/stories/ | npm run test:ui |
| E2E | Playwright | tests/e2e/ | npm run test:e2e |
| Client | Vitest | tests/client/. | npm run test:unit |
Running Tests
# All tests npm run test# Individual test suites npm run test:e2e# End-to-end (requires llama-server) npm run test:client# Client-side unit tests npm run test:server# Server-side unit tests npm run test:ui# Storybook visual tests
Storybook Development
npm run storybook# Start Storybook dev server on :6006 npm run build-storybook# Build static Storybook
Linting and Formatting
npm run lint# Check code style npm run format# Auto-format with Prettier npm run check# TypeScript type checking
Project Structure
tools/ui/ ├── src/ │ ├── lib/ │ │ ├── components/ # UI components (app/, ui/) │ │ ├── hooks/ # Svelte hooks │ │ ├── stores/ # State management │ │ ├── services/ # API and database services │ │ ├── types/ # TypeScript interfaces │ │ └── utils/ # Utility functions │ ├── routes/ # SvelteKit routes │ └── styles/ # Global styles ├── static/ # Static assets ├── tests/ # Test files └── .storybook/ # Storybook configuration
Related Documentation
- llama.cpp Server README - Full server documentation
- Multimodal Documentation - Image and audio support
- Function Calling - Tool use capabilities
1# llama-ui 2 3A modern, feature-rich web interface for llama-server built with SvelteKit. This UI provides an intuitive chat interface with advanced file handling, conversation management, and comprehensive model interaction capabilities. 4 5Llama UI supports two server operation modes: 6 7- **MODEL mode** - Single model operation (standard llama-server) 8- **ROUTER mode** - Multi-model operation with dynamic model loading/unloading 9 10--- 11 12## Table of Contents 13 14- [Features](#features) 15- [Getting Started](#getting-started) 16- [Tech Stack](#tech-stack) 17- [Build Pipeline](#build-pipeline) 18- [Architecture](#architecture) 19- [Data Flows](#data-flows) 20- [Architectural Patterns](#architectural-patterns) 21- [Testing](#testing) 22 23--- 24 25## Features 26 27### Chat Interface 28 29- **Streaming responses** with real-time updates 30- **Reasoning content** - Support for models with thinking/reasoning blocks 31- **Dark/light theme** with system preference detection 32- **Responsive design** for desktop and mobile 33 34### File Attachments 35 36- **Images** - JPEG, PNG, GIF, WebP, SVG (with PNG conversion) 37- **Documents** - PDF (text extraction or image conversion for vision models) 38- **Audio** - MP3, WAV for audio-capable models 39- **Text files** - Source code, markdown, and other text formats 40- **Drag-and-drop** and paste support with rich previews 41 42### Conversation Management 43 44- **Branching** - Branch messages conversations at any point by editing messages or regenerating responses, navigate between branches 45- **Regeneration** - Regenerate responses with optional model switching (ROUTER mode) 46- **Import/Export** - JSON format for backup and sharing 47- **Search** - Find conversations by title or content 48 49### Advanced Rendering 50 51- **Syntax highlighting** - Code blocks with language detection 52- **Math formulas** - KaTeX rendering for LaTeX expressions 53- **Markdown** - Full GFM support with tables, lists, and more 54 55### Multi-Model Support (ROUTER mode) 56 57- **Model selector** with Loaded/Available groups 58- **Automatic loading** - Models load on selection 59- **Modality validation** - Prevents sending images to non-vision models 60- **LRU unloading** - Server auto-manages model cache 61 62### Keyboard Shortcuts 63 64| Shortcut | Action | 65| ------------------ | -------------------- | 66| `Shift+Ctrl/Cmd+O` | New chat | 67| `Shift+Ctrl/Cmd+E` | Edit conversation | 68| `Shift+Ctrl/Cmd+D` | Delete conversation | 69| `Ctrl/Cmd+K` | Search conversations | 70| `Ctrl/Cmd+B` | Toggle sidebar | 71 72### Developer Experience 73 74- **Request tracking** - Monitor token generation with `/slots` endpoint 75- **Storybook** - Component library with visual testing 76- **Hot reload** - Instant updates during development 77 78--- 79 80## Getting Started 81 82### Prerequisites 83 84- **Node.js** 18+ (20+ recommended) 85- **npm** 9+ 86- **llama-server** running locally (for API access) 87 88### 1. Install Dependencies 89 90``` bash 91cd tools/ui 92npm ci 93``` 94 95### 2. Start llama-server 96 97In a separate terminal, start the backend server: 98 99``` bash 100# Single model (MODEL mode) 101./llama-server -m model.gguf 102 103# Multi-model (ROUTER mode) 104./llama-server --models-dir /path/to/models 105``` 106 107### 3. Start Development Servers 108 109``` bash 110npm run dev 111``` 112 113This starts: 114 115- **Vite dev server** at `http://localhost:5173` - The main UI frontend app 116- **Storybook** at `http://localhost:6006` - Component documentation 117 118The Vite dev server proxies API requests to `SERVER_ORIGIN` (with fallback to default llama-server `8080` port): 119 120``` typescript 121// vite.config.ts proxy configuration 122proxy: { 123'/v1' : SERVER_ORIGIN , 124'/props' : SERVER_ORIGIN , 125'/models' : SERVER_ORIGIN , 126'/tools' : SERVER_ORIGIN , 127'/slots' : SERVER_ORIGIN , 128'/cors-proxy' : SERVER_ORIGIN 129}, 130``` 131 132### Development Workflow 133 1341. Open `http://localhost:5173` in your browser 1352. Make changes to `.svelte`, `.ts`, or `.css` files 1363. Changes hot-reload instantly 1374. Use Storybook at `http://localhost:6006` for isolated component development 138 139--- 140 141## Tech Stack 142 143| Layer | Technology | Purpose | 144| ----------------- | ------------------------------- | -------------------------------------------------------- | 145| **Framework** | SvelteKit + Svelte 5 | Reactive UI with runes (`$state`, `$derived`, `$effect`) | 146| **UI Components** | shadcn-svelte + bits-ui | Accessible, customizable component library | 147| **Styling** | TailwindCSS 4 | Utility-first CSS with design tokens | 148| **Database** | IndexedDB (Dexie) | Client-side storage for conversations and messages | 149| **Build** | Vite | Fast bundling with static adapter | 150| **Testing** | Playwright + Vitest + Storybook | E2E, unit, and visual testing | 151| **Markdown** | remark + rehype | Markdown processing with KaTeX and syntax highlighting | 152 153### Key Dependencies 154 155``` json 156{ 157"svelte" : "^5.0.0" , 158"bits-ui" : "^2.8.11" , 159"dexie" : "^4.0.11" , 160"pdfjs-dist" : "^5.4.54" , 161"highlight.js" : "^11.11.1" , 162"rehype-katex" : "^7.0.1" 163} 164``` 165 166--- 167 168## Build Pipeline 169 170### Development Build 171 172``` bash 173npm run dev 174``` 175 176Runs Vite in development mode with: 177 178- Hot Module Replacement (HMR) 179- Source maps 180- Proxy to llama-server 181 182### Production Build 183 184``` bash 185npm run build 186``` 187 188The build process: 189 1901. **Vite Build** - Bundles all TypeScript, Svelte, and CSS 1912. **Static Adapter** - Outputs to `../../build/tools/ui/dist` (llama-server's static file directory) 1923. **Post-Build Script** - Cleans up intermediate files 1934. **Custom Plugin** - Creates `index.html` with: 194- Inlined favicon as base64 195- GZIP compression (level 9) 196- Deterministic output (zeroed timestamps) 197 198``` text 199tools/ui/ → build → build/tools/ui/dist/ 200├── src/ ├── index.html (served by llama-server) 201├── static/ └── (favicon inlined) 202└── ... 203``` 204 205### SvelteKit Configuration 206 207``` javascript 208// svelte.config.js 209adapter: adapter ({ 210pages : '../../build/tools/ui/dist' , // Output directory 211assets : '../../build/tools/ui/dist' , // Static assets 212fallback : 'index.html' , // SPA fallback 213strict : true 214}), 215output: { 216bundleStrategy : 'inline' // Single-file bundle 217} 218``` 219 220### Integration with llama-server 221 222llama-ui is embedded directly into the llama-server binary: 223 2241. `npm run build` outputs `index.html` to `build/tools/ui/dist/` 2252. llama-server compiles this into the binary at build time 2263. When accessing `/`, llama-server serves the bundled HTML 227 228This results in a **single portable binary** with the full Llama UI included. 229 230--- 231 232## Architecture 233 234Llama UI follows a layered architecture with unidirectional data flow: 235 236``` text 237Routes → Components → Hooks → Stores → Services → Storage/API 238``` 239 240### High-Level Architecture 241 242``` mermaid 243flowchart TB 244subgraph Routes["📍 Routes"] 245R1["/ (Welcome)"] 246R2["/chat/[id]"] 247R3["/mcp-servers"] 248R4["/search"] 249R5["/settings"] 250RL["+layout.svelte"] 251end 252 253subgraph Components["🧩 Components"] 254C_Screen["ChatScreen"] 255C_Form["ChatForm"] 256C_Messages["ChatMessages"] 257C_Sidebar["ChatSidebar"] 258C_Models["ModelsSelector"] 259C_Settings["ChatSettings"] 260C_Mcp["McpServers"] 261end 262 263subgraph Hooks["🔌 Hooks"] 264H1["use-chat-screen-active-model"] 265H2["use-processing-state"] 266H3["use-context-gauge"] 267H4["use-models-selector"] 268H5["use-tools-panel"] 269end 270 271subgraph Stores["🗄️ Stores"] 272S1["chatStore"] 273S2["conversationsStore"] 274S3["modelsStore"] 275S4["mcpStore"] 276S5["agenticStore"] 277S6["serverStore"] 278S7["settingsStore"] 279S8["toolsStore"] 280end 281 282subgraph Services["⚙️ Services"] 283SV1["ChatService"] 284SV2["ModelsService"] 285SV3["PropsService"] 286SV4["DatabaseService"] 287SV5["MCPService"] 288SV6["ToolsService"] 289SV7["SandboxService"] 290end 291 292subgraph Storage["💾 Storage"] 293ST1["IndexedDB"] 294ST2["LocalStorage"] 295end 296 297subgraph APIs["🌐 llama-server"] 298API1["/v1/chat/completions"] 299API2["/props"] 300API3["/models/*"] 301API4["/tools"] 302end 303 304R1 & R2 --> C_Screen 305RL --> C_Sidebar 306C_Screen --> C_Form & C_Messages & C_Settings 307C_Screen --> H1 & H2 & H3 308C_Models --> H4 309C_Mcp --> S4 310C_Screen --> S1 & S2 & S3 311C_Models --> S3 312H1 --> S3 313S1 --> SV1 & SV4 314S2 --> SV4 315S3 --> SV2 & SV3 316S4 --> SV5 317S5 --> SV1 & SV5 & SV6 & SV7 318SV4 --> ST1 319SV1 --> API1 320SV2 --> API3 321SV3 --> API2 322SV6 --> API4 323``` 324 325### Layer Breakdown 326 327#### Routes (`src/routes/`) 328 329- **`/`** - Welcome screen, creates new conversation 330- **`/chat/[id]`** - Active chat interface 331- **`/mcp-servers`** - MCP server management 332- **`/search`** - Conversation search 333- **`/settings`** - Settings (optional `[[section]]`) 334- **`+layout.svelte`** - Sidebar, navigation, global initialization 335 336#### Components (`src/lib/components/`) 337 338Components are organized in `app/` (application-specific) and `ui/` (shadcn-svelte primitives). 339 340**Chat Components** (`app/chat/`): 341 342| Component | Responsibility | 343| ------------------ | --------------------------------------------------------------------------- | 344| `ChatScreen/` | Main chat container, coordinates message list, input form, and attachments | 345| `ChatForm/` | Message input textarea with file upload, paste handling, keyboard shortcuts | 346| `ChatMessages/` | Message list with branch navigation, regenerate/continue/edit actions | 347| `ChatAttachments/` | File attachment previews, drag-and-drop, PDF/image/audio handling | 348| `ChatSettings/` | Parameter sliders (temperature, top-p, etc.) with server default sync | 349| `ChatSidebar/` | Conversation list, search, import/export, navigation | 350 351**Dialog Components** (`app/dialogs/`): 352 353| Component | Responsibility | 354| ------------------------------- | -------------------------------------------------------- | 355| `DialogChatSettings` | Full-screen settings configuration | 356| `DialogModelInformation` | Model details (context size, modalities, parallel slots) | 357| `DialogChatAttachmentPreview` | Full preview for images, PDFs (text or page view), code | 358| `DialogConfirmation` | Generic confirmation for destructive actions | 359| `DialogConversationTitleUpdate` | Edit conversation title | 360 361**Server/Model Components** (`app/server/`, `app/models/`): 362 363| Component | Responsibility | 364| ------------------- | --------------------------------------------------------- | 365| `ServerErrorSplash` | Error display when server is unreachable | 366| `ModelsSelector` | Model dropdown with Loaded/Available groups (ROUTER mode) | 367 368**Shared UI Components** (`app/misc/`): 369 370| Component | Responsibility | 371| -------------------------------- | ---------------------------------------------------------------- | 372| `MarkdownContent` | Markdown rendering with KaTeX, syntax highlighting, copy buttons | 373| `SyntaxHighlightedCode` | Code blocks with language detection and highlighting | 374| `ActionButton`, `ActionDropdown` | Reusable action buttons and menus | 375| `BadgeModality`, `BadgeInfo` | Status and capability badges | 376 377#### Hooks (`src/lib/hooks/`) 378 379Hooks are the thin view-layer between components and stores: they own UI concerns (scroll, drag-and-drop, keyboard shortcuts, pickers, selection) and translate store state into view state. 380 381| Hook | Responsibility | 382| ------------------------------- | -------------------------------------------------------------- | 383| `use-chat-screen-active-model` | Active model resolution + modality capability detection | 384| `use-processing-state` | View over `chatStore.processing` for streaming progress/tokens | 385| `use-context-gauge` | View over `contextStatsStore` for the context usage gauge | 386| `use-models-selector` | Model selector dropdown state (loaded/available groups) | 387| `use-tools-panel` | Tools panel state | 388| `use-reasoning-menu` | Reasoning-effort menu state | 389| `use-attachment-menu` | Attachment menu + modality flags | 390| `use-draft-messages` | Per-chat draft message/files persistence | 391| `use-chat-form-pickers` | Chat form pickers (commands, mentions) | 392| `use-debounced-search` | Shared debounced async search for pickers | 393| `use-picker-navigation` | Picker keyboard navigation | 394| `use-chat-message-edit-context` | Message edit context (content + extras) | 395| `use-chat-screen-drag-and-drop` | Drag-and-drop state machine | 396| `use-chat-screen-file-upload` | File upload queue + capability validation | 397| `use-chat-screen-scroll` | Scroll container binding + navigation guard | 398| `use-auto-scroll` | Auto-scroll controller for streaming | 399| `use-marquee-selection` | Shift+click / marquee range selection | 400| `use-keyboard-shortcuts` | Global keyboard shortcuts | 401| `use-settings-navigation` | Settings section navigation | 402| `use-pwa` | PWA install/update + version mismatch detection | 403 404#### Stores (`src/lib/stores/`) 405 406Stores own reactive application state as Svelte 5 runes. Larger stores are split into directories and compose focused sub-stores behind a narrow host interface (see Architectural Patterns). 407 408| Store | Responsibility | 409| -------------------- | --------------------------------------------------------------------------------------------------------------- | 410| `chatStore` | Chat lifecycle, streaming, abort control, error handling; composes `processing`, `activity`, `streams`, `flows` | 411| `conversationsStore` | Conversation CRUD, message branching, navigation, import/export; composes `preferences` | 412| `modelsStore` | Model list, selection, loading/unloading (ROUTER); composes `props`, `status` | 413| `mcpStore` | MCP host role: multi-server lifecycle, tool routing; composes `health`, `resources` | 414| `agenticStore` | Multi-turn agentic loop orchestration, tool execution; composes `gates` | 415| `serverStore` | Server connection state, `/props`, role detection, modalities | 416| `settingsStore` | User preferences, theme, parameter sync with server defaults | 417| `toolsStore` | Tool registry: server + MCP tools, enabled set for the LLM | 418| `permissionsStore` | Persisted tool permission grants | 419| `contextStatsStore` | Context window usage for the active conversation | 420| `draftMessagesStore` | Per-chat draft message/files | 421| `deviceStore` | Browser environment signals (mobile, OS, theme) | 422| `versionStore` | Build version information | 423 424#### Services (`src/lib/services/`) 425 426Services are a stateless protocol layer: static methods, pure I/O, no reactive state. Stores consume them for all API and storage access. 427 428| Service | Responsibility | 429| ----------------------------- | ------------------------------------------------------------------------- | 430| `ChatService` | `/v1/chat/completions` streaming + SSE parsing, message format conversion | 431| `ModelsService` | `/models`, `/models/load`, `/models/unload` | 432| `PropsService` | `/props`, `/props?model=` | 433| `DatabaseService` | IndexedDB operations via Dexie | 434| `MCPService` | MCP protocol: transports, connect, list/execute tools, prompts, resources | 435| `ToolsService` | Server tool list/execute/stream (`/tools`) | 436| `SandboxService` | Browser JS execution in a sandboxed worker | 437| `ParameterSyncService` | Syncs settings with server defaults | 438| `ConversationTransferService` | Conversation import/export JSONL + ZIP format | 439| `MigrationService` | Non-destructive localStorage/IndexedDB migrations | 440| `RouterService` | Dynamic route URL construction | 441 442--- 443 444## Data Flows 445 446### MODEL Mode (Single Model) 447 448``` mermaid 449sequenceDiagram 450participant User 451participant UI 452participant Stores 453participant DB as IndexedDB 454participant API as llama-server 455 456Note over User,API: Initialization 457UI->>Stores: initStores() (awaited by route loads) 458Stores->>Stores: run migrations 459Stores->>DB: load conversations (background) 460Stores->>API: GET /props 461API-->>Stores: server config 462Stores->>API: GET /v1/models 463API-->>Stores: single model (auto-selected) 464 465Note over User,API: Chat Flow 466User->>UI: send message 467Stores->>DB: save user message 468Stores->>API: POST /v1/chat/completions (stream) 469loop streaming 470API-->>Stores: SSE chunks 471Stores-->>UI: reactive update 472end 473Stores->>DB: save assistant message 474``` 475 476### ROUTER Mode (Multi-Model) 477 478``` mermaid 479sequenceDiagram 480participant User 481participant UI 482participant Stores 483participant API as llama-server 484 485Note over User,API: Initialization 486Stores->>API: GET /props 487API-->>Stores: {role: "router"} 488Stores->>API: GET /models 489API-->>Stores: models[] with status 490 491Note over User,API: Model Selection 492User->>UI: select model 493alt model not loaded 494Stores->>API: POST /models/load 495loop poll status 496Stores->>API: GET /models 497end 498Stores->>API: GET /props?model=X 499end 500Stores->>Stores: validate modalities 501 502Note over User,API: Chat Flow 503Stores->>API: POST /v1/chat/completions {model: X} 504loop streaming 505API-->>Stores: SSE chunks + model info 506end 507``` 508 509--- 510 511## Architectural Patterns 512 513### 1. Reactive State with Svelte 5 Runes 514 515All stores use Svelte 5's fine-grained reactivity: 516 517``` typescript 518// Store with reactive state 519class ChatStore { 520#isLoading = $state ( false ); 521#currentResponse = $state ( '' ); 522 523// Derived values auto-update 524get isStreaming () { 525return $derived ( this .#isLoading && this .#currentResponse. length > 0 ); 526} 527} 528 529// Exported reactive accessors 530export const isLoading = () => chatStore . isLoading ; 531export const currentResponse = () => chatStore . currentResponse ; 532``` 533 534### 2. Unidirectional Data Flow 535 536Data flows in one direction, making state predictable: 537 538``` mermaid 539flowchart LR 540subgraph UI["UI Layer"] 541A[User Action] --> B[Component] 542end 543 544subgraph State["State Layer"] 545B --> C[Store Method] 546C --> D[State Update] 547end 548 549subgraph IO["I/O Layer"] 550C --> E[Service] 551E --> F[API / IndexedDB] 552F -.->|Response| D 553end 554 555D -->|Reactive| B 556``` 557 558Components dispatch actions to stores, stores coordinate with services for I/O, and state updates reactively propagate back to the UI. 559 560### 3. Per-Conversation State 561 562Enables concurrent streaming across multiple conversations. Loading is tracked 563per conversation by the activity ledger (`chatStore.activity`), while streaming 564state and abort controllers live in per-conversation maps: 565 566``` typescript 567class ChatStore { 568chatStreamingStates = new SvelteMap < string , { response : string ; messageId : string }>(); 569abortControllers = new SvelteMap < string , AbortController >(); 570} 571``` 572 573### 4. Message Branching with Tree Structure 574 575Conversations are stored as a tree, not a linear list: 576 577``` typescript 578interface DatabaseMessage { 579id : string ; 580parent : string | null ; // Points to parent message 581children : string []; // List of child message IDs 582// ... 583} 584 585interface DatabaseConversation { 586currentNode : string ; // Currently viewed branch tip 587// ... 588} 589``` 590 591Navigation between branches updates `currentNode` without losing history. 592 593### 5. Layered Service Architecture 594 595Stores handle state; services handle I/O: 596 597``` text 598┌─────────────────┐ 599│ Stores │ Business logic, state management 600├─────────────────┤ 601│ Services │ API calls, database operations 602├─────────────────┤ 603│ Storage/API │ IndexedDB, LocalStorage, HTTP 604└─────────────────┘ 605``` 606 607### 6. Server Role Abstraction 608 609Single codebase handles both MODEL and ROUTER modes: 610 611``` typescript 612// serverStore.ts 613get isRouterMode () { 614return this . role === ServerRole . ROUTER ; 615} 616 617// Components conditionally render based on mode 618{#if isRouterMode ()} 619< ModelsSelector / > 620{ / if} 621``` 622 623### 7. Modality Validation 624 625Prevents sending attachments to incompatible models. The 626`use-chat-screen-active-model` hook derives the active model's capabilities 627from `modelsStore.props`: 628 629``` typescript 630// use-chat-screen-active-model hook 631const hasVisionModality = $derived . by (() => modelsStore . props . modelSupportsVision ( activeModelId )); 632const hasAudioModality = $derived . by (() => modelsStore . props . modelSupportsAudio ( activeModelId )); 633``` 634 635### 8. Persistent Storage Strategy 636 637Data is persisted across sessions using two storage mechanisms: 638 639``` mermaid 640flowchart TB 641subgraph Browser["Browser Storage"] 642subgraph IDB["IndexedDB (Dexie)"] 643C[Conversations] 644M[Messages] 645end 646subgraph LS["LocalStorage"] 647S[Settings Config] 648O[User Overrides] 649T[Theme Preference] 650end 651end 652 653subgraph Stores["Svelte Stores"] 654CS[conversationsStore] --> C 655CS --> M 656SS[settingsStore] --> S 657SS --> O 658SS --> T 659end 660``` 661 662- **IndexedDB**: Conversations and messages (large, structured data) 663- **LocalStorage**: Settings, user parameter overrides, theme (small key-value data) 664- **Memory only**: Server props, model list (fetched fresh on each session) 665 666--- 667 668## Testing 669 670### Test Types 671 672| Type | Tool | Location | Command | 673| ------------- | ------------------ | ---------------- | ------------------- | 674| **Unit** | Vitest | `tests/unit/` | `npm run test:unit` | 675| **UI/Visual** | Storybook + Vitest | `tests/stories/` | `npm run test:ui` | 676| **E2E** | Playwright | `tests/e2e/` | `npm run test:e2e` | 677| **Client** | Vitest | `tests/client/`. | `npm run test:unit` | 678 679### Running Tests 680 681``` bash 682# All tests 683npm run test 684 685# Individual test suites 686npm run test:e2e # End-to-end (requires llama-server) 687npm run test:client # Client-side unit tests 688npm run test:server # Server-side unit tests 689npm run test:ui # Storybook visual tests 690``` 691 692### Storybook Development 693 694``` bash 695npm run storybook # Start Storybook dev server on :6006 696npm run build-storybook # Build static Storybook 697``` 698 699### Linting and Formatting 700 701``` bash 702npm run lint # Check code style 703npm run format # Auto-format with Prettier 704npm run check # TypeScript type checking 705``` 706 707--- 708 709## Project Structure 710 711``` text 712tools/ui/ 713├── src/ 714│ ├── lib/ 715│ │ ├── components/ # UI components (app/, ui/) 716│ │ ├── hooks/ # Svelte hooks 717│ │ ├── stores/ # State management 718│ │ ├── services/ # API and database services 719│ │ ├── types/ # TypeScript interfaces 720│ │ └── utils/ # Utility functions 721│ ├── routes/ # SvelteKit routes 722│ └── styles/ # Global styles 723├── static/ # Static assets 724├── tests/ # Test files 725└── .storybook/ # Storybook configuration 726``` 727 728--- 729 730## Related Documentation 731 732- [llama.cpp Server README](../server/README.md) - Full server documentation 733- [Multimodal Documentation](../../docs/multimodal.md) - Image and audio support 734- [Function Calling](../../docs/function-calling.md) - Tool use capabilities