char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Charlotte Somenc/dec in server context8300f1411

char/aliceai-t5-moe
5 folders22 files

files

.storybook/
scripts/
src/
static/recommended-mcp/
tests/
.env.example100 B
.gitignore492 B
.npmrc57 B
.prettierignore273 B
.prettierrc296 B
CMakeLists.txt2.9 KiB
README.md25.5 KiB
components.json359 B
eslint.config.js10.9 KiB
package-lock.json571.7 KiB
package.json3.9 KiB
playwright.config.ts653 B
pwa-assets-dark.config.ts648 B
pwa-assets.config.ts2.0 KiB
sources.cmake296 B
svelte.config.js858 B
tsconfig.json1.0 KiB
ui.cpp.in744 B
ui.h.in576 B
vite.config.ts2.7 KiB
vitest-setup-client.ts2.3 KiB
vitest.shims.d.ts53 B

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:


Table of Contents


Features

Chat Interface

File Attachments

Conversation Management

Advanced Rendering

Multi-Model Support (ROUTER mode)

Keyboard Shortcuts

ShortcutAction
Shift+Ctrl/Cmd+ONew chat
Shift+Ctrl/Cmd+EEdit conversation
Shift+Ctrl/Cmd+DDelete conversation
Ctrl/Cmd+KSearch conversations
Ctrl/Cmd+BToggle sidebar

Developer Experience


Getting Started

Prerequisites

1. Install Dependencies

cd tools/ui
npm 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:

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

  1. Open http://localhost:5173 in your browser
  2. Make changes to .svelte, .ts, or .css files
  3. Changes hot-reload instantly
  4. Use Storybook at http://localhost:6006 for isolated component development

Tech Stack

LayerTechnologyPurpose
FrameworkSvelteKit + Svelte 5Reactive UI with runes ($state, $derived, $effect)
UI Componentsshadcn-svelte + bits-uiAccessible, customizable component library
StylingTailwindCSS 4Utility-first CSS with design tokens
DatabaseIndexedDB (Dexie)Client-side storage for conversations and messages
BuildViteFast bundling with static adapter
TestingPlaywright + Vitest + StorybookE2E, unit, and visual testing
Markdownremark + rehypeMarkdown 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:

Production Build

npm run build

The build process:

  1. Vite Build - Bundles all TypeScript, Svelte, and CSS
  2. Static Adapter - Outputs to ../../build/tools/ui/dist (llama-server's static file directory)
  3. Post-Build Script - Cleans up intermediate files
  4. Custom Plugin - Creates index.html with:
    • 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:

  1. npm run build outputs index.html to build/tools/ui/dist/
  2. llama-server compiles this into the binary at build time
  3. 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/)

Components (src/lib/components/)

Components are organized in app/ (application-specific) and ui/ (shadcn-svelte primitives).

Chat Components (app/chat/):

ComponentResponsibility
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/):

ComponentResponsibility
DialogChatSettingsFull-screen settings configuration
DialogModelInformationModel details (context size, modalities, parallel slots)
DialogChatAttachmentPreviewFull preview for images, PDFs (text or page view), code
DialogConfirmationGeneric confirmation for destructive actions
DialogConversationTitleUpdateEdit conversation title

Server/Model Components (app/server/, app/models/):

ComponentResponsibility
ServerErrorSplashError display when server is unreachable
ModelsSelectorModel dropdown with Loaded/Available groups (ROUTER mode)

Shared UI Components (app/misc/):

ComponentResponsibility
MarkdownContentMarkdown rendering with KaTeX, syntax highlighting, copy buttons
SyntaxHighlightedCodeCode blocks with language detection and highlighting
ActionButton, ActionDropdownReusable action buttons and menus
BadgeModality, BadgeInfoStatus 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.

HookResponsibility
use-chat-screen-active-modelActive model resolution + modality capability detection
use-processing-stateView over chatStore.processing for streaming progress/tokens
use-context-gaugeView over contextStatsStore for the context usage gauge
use-models-selectorModel selector dropdown state (loaded/available groups)
use-tools-panelTools panel state
use-reasoning-menuReasoning-effort menu state
use-attachment-menuAttachment menu + modality flags
use-draft-messagesPer-chat draft message/files persistence
use-chat-form-pickersChat form pickers (commands, mentions)
use-debounced-searchShared debounced async search for pickers
use-picker-navigationPicker keyboard navigation
use-chat-message-edit-contextMessage edit context (content + extras)
use-chat-screen-drag-and-dropDrag-and-drop state machine
use-chat-screen-file-uploadFile upload queue + capability validation
use-chat-screen-scrollScroll container binding + navigation guard
use-auto-scrollAuto-scroll controller for streaming
use-marquee-selectionShift+click / marquee range selection
use-keyboard-shortcutsGlobal keyboard shortcuts
use-settings-navigationSettings section navigation
use-pwaPWA 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).

StoreResponsibility
chatStoreChat lifecycle, streaming, abort control, error handling; composes processing, activity, streams, flows
conversationsStoreConversation CRUD, message branching, navigation, import/export; composes preferences
modelsStoreModel list, selection, loading/unloading (ROUTER); composes props, status
mcpStoreMCP host role: multi-server lifecycle, tool routing; composes health, resources
agenticStoreMulti-turn agentic loop orchestration, tool execution; composes gates
serverStoreServer connection state, /props, role detection, modalities
settingsStoreUser preferences, theme, parameter sync with server defaults
toolsStoreTool registry: server + MCP tools, enabled set for the LLM
permissionsStorePersisted tool permission grants
contextStatsStoreContext window usage for the active conversation
draftMessagesStorePer-chat draft message/files
deviceStoreBrowser environment signals (mobile, OS, theme)
versionStoreBuild 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.

ServiceResponsibility
ChatService/v1/chat/completions streaming + SSE parsing, message format conversion
ModelsService/models, /models/load, /models/unload
PropsService/props, /props?model=
DatabaseServiceIndexedDB operations via Dexie
MCPServiceMCP protocol: transports, connect, list/execute tools, prompts, resources
ToolsServiceServer tool list/execute/stream (/tools)
SandboxServiceBrowser JS execution in a sandboxed worker
ParameterSyncServiceSyncs settings with server defaults
ConversationTransferServiceConversation import/export JSONL + ZIP format
MigrationServiceNon-destructive localStorage/IndexedDB migrations
RouterServiceDynamic 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
{#if isRouterMode()}
  <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

Testing

Test Types

TypeToolLocationCommand
UnitVitesttests/unit/npm run test:unit
UI/VisualStorybook + Vitesttests/stories/npm run test:ui
E2EPlaywrighttests/e2e/npm run test:e2e
ClientVitesttests/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