From c6e5da11e627afe8b19a177e3ab95d3c63869c00 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Mon, 9 Feb 2026 15:01:05 +0100 Subject: [PATCH] docs: Centralize and enhance service documentation --- .../webui/src/lib/services/chat.service.ts | 32 --- .../src/lib/services/database.service.ts | 47 ---- tools/server/webui/src/lib/services/index.ts | 256 ++++++++++++++++++ .../webui/src/lib/services/mcp.service.ts | 99 +++++-- .../webui/src/lib/services/models.service.ts | 50 ++-- .../lib/services/parameter-sync.service.ts | 77 ++++-- .../webui/src/lib/services/props.service.ts | 27 +- 7 files changed, 416 insertions(+), 172 deletions(-) diff --git a/tools/server/webui/src/lib/services/chat.service.ts b/tools/server/webui/src/lib/services/chat.service.ts index 49c39e4ddd..389939901a 100644 --- a/tools/server/webui/src/lib/services/chat.service.ts +++ b/tools/server/webui/src/lib/services/chat.service.ts @@ -16,38 +16,6 @@ import type { ApiChatMessageContentPart, ApiChatCompletionToolCall } from '$lib/ import type { DatabaseMessageExtraMcpPrompt, DatabaseMessageExtraMcpResource } from '$lib/types'; import { modelsStore } from '$lib/stores/models.svelte'; -/** - * ChatService - Low-level API communication layer for Chat Completions - * - * **Terminology - Chat vs Conversation:** - * - **Chat**: The active interaction space with the Chat Completions API. This service - * handles the real-time communication with the AI backend - sending messages, receiving - * streaming responses, and managing request lifecycles. "Chat" is ephemeral and runtime-focused. - * - **Conversation**: The persistent database entity storing all messages and metadata. - * Managed by ConversationsService/Store, conversations persist across sessions. - * - * This service handles direct communication with the llama-server's Chat Completions API. - * It provides the network layer abstraction for AI model interactions while remaining - * stateless and focused purely on API communication. - * - * **Architecture & Relationships:** - * - **ChatService** (this class): Stateless API communication layer - * - Handles HTTP requests/responses with the llama-server - * - Manages streaming and non-streaming response parsing - * - Provides per-conversation request abortion capabilities - * - Converts database messages to API format - * - Handles error translation for server responses - * - * - **chatStore**: Uses ChatService for all AI model communication - * - **conversationsStore**: Provides message context for API requests - * - * **Key Responsibilities:** - * - Message format conversion (DatabaseMessage → API format) - * - Streaming response handling with real-time callbacks - * - Reasoning content extraction and processing - * - File attachment processing (images, PDFs, audio, text) - * - Request lifecycle management (abort via AbortSignal) - */ export class ChatService { private static stripReasoningContent( content: ApiChatMessageData['content'] | null | undefined diff --git a/tools/server/webui/src/lib/services/database.service.ts b/tools/server/webui/src/lib/services/database.service.ts index 356088a478..2592794c92 100644 --- a/tools/server/webui/src/lib/services/database.service.ts +++ b/tools/server/webui/src/lib/services/database.service.ts @@ -19,53 +19,6 @@ const db = new LlamacppDatabase(); import { v4 as uuid } from 'uuid'; import { MessageRole } from '$lib/enums'; -/** - * DatabaseService - Stateless IndexedDB communication layer - * - * **Terminology - Chat vs Conversation:** - * - **Chat**: The active interaction space with the Chat Completions API (ephemeral, runtime). - * - **Conversation**: The persistent database entity storing all messages and metadata. - * This service handles raw database operations for conversations - the lowest layer - * in the persistence stack. - * - * This service provides a stateless data access layer built on IndexedDB using Dexie ORM. - * It handles all low-level storage operations for conversations and messages with support - * for complex branching and message threading. All methods are static - no instance state. - * - * **Architecture & Relationships (bottom to top):** - * - **DatabaseService** (this class): Stateless IndexedDB operations - * - Lowest layer - direct Dexie/IndexedDB communication - * - Pure CRUD operations without business logic - * - Handles branching tree structure (parent-child relationships) - * - Provides transaction safety for multi-table operations - * - * - **ConversationsService**: Stateless business logic layer - * - Uses DatabaseService for all persistence operations - * - Adds import/export, navigation, and higher-level operations - * - * - **conversationsStore**: Reactive state management for conversations - * - Uses ConversationsService for database operations - * - Manages conversation list, active conversation, and messages in memory - * - * - **chatStore**: Active AI interaction management - * - Uses conversationsStore for conversation context - * - Directly uses DatabaseService for message CRUD during streaming - * - * **Key Features:** - * - **Conversation CRUD**: Create, read, update, delete conversations - * - **Message CRUD**: Add, update, delete messages with branching support - * - **Branch Operations**: Create branches, find descendants, cascade deletions - * - **Transaction Safety**: Atomic operations for data consistency - * - * **Database Schema:** - * - `conversations`: id, lastModified, currNode, name - * - `messages`: id, convId, type, role, timestamp, parent, children - * - * **Branching Model:** - * Messages form a tree structure where each message can have multiple children, - * enabling conversation branching and alternative response paths. The conversation's - * `currNode` tracks the currently active branch endpoint. - */ export class DatabaseService { /** * diff --git a/tools/server/webui/src/lib/services/index.ts b/tools/server/webui/src/lib/services/index.ts index f8b2c385bd..7c98383d1f 100644 --- a/tools/server/webui/src/lib/services/index.ts +++ b/tools/server/webui/src/lib/services/index.ts @@ -1,6 +1,262 @@ +/** + * + * SERVICES + * + * Stateless service layer for API communication and data operations. + * Services handle protocol-level concerns (HTTP, WebSocket, MCP, IndexedDB) + * without managing reactive state — that responsibility belongs to stores. + * + * **Design Principles:** + * - All methods are static — no instance state + * - Pure I/O operations (network requests, database queries) + * - No Svelte runes or reactive primitives + * - Error handling at the protocol level; business-level error handling in stores + * + * **Architecture (bottom to top):** + * - **Services** (this layer): Stateless protocol communication + * - **Stores**: Reactive state management consuming services + * - **Components**: UI consuming stores + * + */ + +/** + * **ChatService** - Chat Completions API communication layer + * + * Handles direct communication with the llama-server's `/v1/chat/completions` endpoint. + * Provides streaming and non-streaming response parsing, message format conversion + * (DatabaseMessage → API format), and request lifecycle management. + * + * **Terminology - Chat vs Conversation:** + * - **Chat**: The active interaction space with the Chat Completions API. Ephemeral and + * runtime-focused — sending messages, receiving streaming responses, managing request lifecycles. + * - **Conversation**: The persistent database entity storing all messages and metadata. + * Managed by conversationsStore, conversations persist across sessions. + * + * **Architecture & Relationships:** + * - **ChatService** (this class): Stateless API communication layer + * - Handles HTTP requests/responses with the llama-server + * - Manages streaming and non-streaming response parsing + * - Converts database messages to API format (multimodal, tool calls) + * - Handles error translation with user-friendly messages + * + * - **chatStore**: Primary consumer — uses ChatService for all AI model communication + * - **agenticStore**: Uses ChatService for multi-turn agentic loop streaming + * - **conversationsStore**: Provides message context for API requests + * + * **Key Responsibilities:** + * - Streaming response handling with real-time content/reasoning/tool-call callbacks + * - Non-streaming response parsing with complete response extraction + * - Database message to API format conversion (attachments, tool calls, multimodal) + * - Tool call delta merging for incremental streaming aggregation + * - Request parameter assembly (sampling, penalties, custom params) + * - File attachment processing (images, PDFs, audio, text, MCP prompts/resources) + * - Reasoning content stripping from prompt history to avoid KV cache pollution + * - Error translation (network, timeout, server errors → user-friendly messages) + * + * @see chatStore in stores/chat.svelte.ts — primary consumer for chat state management + * @see agenticStore in stores/agentic.svelte.ts — uses ChatService for agentic loop streaming + * @see conversationsStore in stores/conversations.svelte.ts — provides message context + */ export { ChatService } from './chat.service'; + +/** + * **DatabaseService** - IndexedDB persistence layer via Dexie ORM + * + * Provides stateless data access for conversations and messages using IndexedDB. + * Handles all low-level storage operations including branching tree structures, + * cascade deletions, and transaction safety for multi-table operations. + * + * **Architecture & Relationships (bottom to top):** + * - **DatabaseService** (this class): Stateless IndexedDB operations + * - Lowest layer — direct Dexie/IndexedDB communication + * - Pure CRUD operations without business logic + * - Handles branching tree structure (parent-child relationships) + * - Provides transaction safety for multi-table operations + * + * - **conversationsStore**: Reactive state management layer + * - Uses DatabaseService for all persistence operations + * - Manages conversation list, active conversation, and messages in memory + * + * - **chatStore**: Active AI interaction management + * - Uses conversationsStore for conversation context + * - Directly uses DatabaseService for message CRUD during streaming + * + * **Key Responsibilities:** + * - Conversation CRUD (create, read, update, delete) + * - Message CRUD with branching support (parent-child relationships) + * - Root message and system prompt creation + * - Cascade deletion of message branches (descendants) + * - Transaction-safe multi-table operations + * - Conversation import with duplicate detection + * + * **Database Schema:** + * - `conversations`: id, lastModified, currNode, name + * - `messages`: id, convId, type, role, timestamp, parent, children + * + * **Branching Model:** + * Messages form a tree structure where each message can have multiple children, + * enabling conversation branching and alternative response paths. The conversation's + * `currNode` tracks the currently active branch endpoint. + * + * @see conversationsStore in stores/conversations.svelte.ts — reactive layer on top of DatabaseService + * @see chatStore in stores/chat.svelte.ts — uses DatabaseService directly for message CRUD during streaming + */ export { DatabaseService } from './database.service'; + +/** + * **ModelsService** - Model management API communication + * + * Handles communication with model-related endpoints for both MODEL (single model) + * and ROUTER (multi-model) server modes. Provides model listing, loading/unloading, + * and status checking without managing any model state. + * + * **Architecture & Relationships:** + * - **ModelsService** (this class): Stateless HTTP communication + * - Sends requests to model endpoints + * - Parses and returns typed API responses + * - Provides model status utility methods + * + * - **modelsStore**: Primary consumer — manages reactive model state + * - Calls ModelsService for all model API operations + * - Handles polling, caching, and state updates + * + * **Key Responsibilities:** + * - List available models via OpenAI-compatible `/v1/models` endpoint + * - Load/unload models via `/models/load` and `/models/unload` (ROUTER mode) + * - Model status queries (loaded, loading) + * + * **Server Mode Behavior:** + * - **MODEL mode**: Only `list()` is relevant — single model always loaded + * - **ROUTER mode**: Full lifecycle — `list()`, `listRouter()`, `load()`, `unload()` + * + * **Endpoints:** + * - `GET /v1/models` — OpenAI-compatible model list (both modes) + * - `POST /models/load` — Load a model (ROUTER mode only) + * - `POST /models/unload` — Unload a model (ROUTER mode only) + * + * @see modelsStore in stores/models.svelte.ts — primary consumer for reactive model state + */ export { ModelsService } from './models.service'; + +/** + * **PropsService** - Server properties and capabilities retrieval + * + * Fetches server configuration, model information, and capabilities from the `/props` + * endpoint. Supports both global server props and per-model props (ROUTER mode). + * + * **Architecture & Relationships:** + * - **PropsService** (this class): Stateless HTTP communication + * - Fetches server properties from `/props` endpoint + * - Handles authentication and request parameters + * - Returns typed `ApiLlamaCppServerProps` responses + * + * - **serverStore**: Consumes global server properties (role detection, connection state) + * - **modelsStore**: Consumes per-model properties (modalities, context size) + * - **settingsStore**: Syncs default generation parameters from props response + * + * **Key Responsibilities:** + * - Fetch global server properties (default generation settings, modalities) + * - Fetch per-model properties in ROUTER mode via `?model=` parameter + * - Handle autoload control to prevent unintended model loading + * + * **API Behavior:** + * - `GET /props` → Global server props (MODEL mode: includes modalities) + * - `GET /props?model=` → Per-model props (ROUTER mode: model-specific modalities) + * - `&autoload=false` → Prevents model auto-loading when querying props + * + * @see serverStore in stores/server.svelte.ts — consumes global server props + * @see modelsStore in stores/models.svelte.ts — consumes per-model props for modalities + * @see settingsStore in stores/settings.svelte.ts — syncs default generation params from props + */ export { PropsService } from './props.service'; + +/** + * **ParameterSyncService** - Server defaults and user settings synchronization + * + * Manages the complex logic of merging server-provided default parameters with + * user-configured overrides. Ensures the UI reflects the actual server state + * while preserving user customizations. Tracks parameter sources (server default + * vs user override) for display in the settings UI. + * + * **Architecture & Relationships:** + * - **ParameterSyncService** (this class): Stateless sync logic + * - Pure functions for parameter extraction, merging, and diffing + * - No side effects — receives data in, returns data out + * - Handles floating-point precision normalization + * + * - **settingsStore**: Primary consumer — calls sync methods during: + * - Initial load (`syncWithServerDefaults`) + * - Settings reset (`forceSyncWithServerDefaults`) + * - Parameter info queries (`getParameterInfo`) + * + * - **PropsService**: Provides raw server props that feed into extraction + * + * **Key Responsibilities:** + * - Extract syncable parameters from server `/props` response + * - Merge server defaults with user overrides (user wins) + * - Track parameter source (Custom vs Default) for UI badges + * - Validate server parameter values by type (number, string, boolean) + * - Create diffs between current settings and server defaults + * - Floating-point precision normalization for consistent comparisons + * + * **Parameter Source Priority:** + * 1. **User Override** (Custom badge) — explicitly set by user in settings + * 2. **Server Default** (Default badge) — from `/props` endpoint + * 3. **App Default** — hardcoded fallback when server props unavailable + * + * **Exports:** + * - `ParameterSyncService` class — static methods for sync logic + * - `SYNCABLE_PARAMETERS` — mapping of webui setting keys to server parameter keys + * + * @see settingsStore in stores/settings.svelte.ts — primary consumer for settings sync + * @see ChatSettingsParameterSourceIndicator — displays parameter source badges in UI + */ export { ParameterSyncService } from './parameter-sync.service'; + +/** + * **MCPService** - Low-level MCP protocol communication layer + * + * Implements the client-side MCP (Model Context Protocol) SDK operations for connecting + * to MCP servers, discovering capabilities, and executing protocol operations. + * Supports multiple transport types: WebSocket, StreamableHTTP, and SSE (legacy fallback). + * + * **Architecture & Relationships:** + * - **MCPService** (this class): Stateless protocol communication + * - Creates and manages transport connections (WebSocket, StreamableHTTP, SSE) + * - Wraps MCP SDK client operations with error handling + * - Formats tool results and extracts server info + * - Provides abort signal support for cancellable operations + * + * - **mcpStore**: Reactive business logic facade + * - Uses MCPService for all protocol-level operations + * - Manages connection lifecycle, health checks, reconnection + * - Handles tool name conflict resolution and server coordination + * + * - **mcpResourceStore**: Reactive resource state + * - Receives resource data fetched via MCPService + * - Manages resource caching, subscriptions, and attachments + * + * - **agenticStore**: Agentic loop orchestration + * - Executes tool calls via mcpStore → MCPService chain + * + * **Key Responsibilities:** + * - Transport creation with automatic fallback (StreamableHTTP → SSE) + * - Server connection with detailed phase tracking and progress callbacks + * - Tool discovery (`listTools`) and execution (`callTool`) with abort support + * - Prompt listing (`listPrompts`) and retrieval (`getPrompt`) with arguments + * - Resource operations: list, read, subscribe/unsubscribe, template support + * - Completion suggestions for prompt arguments and resource URI templates + * - CORS proxy routing via llama-server for cross-origin MCP servers + * - Tool result formatting (text, images, embedded resources) + * + * **Transport Hierarchy:** + * 1. **WebSocket** — bidirectional, no CORS proxy support + * 2. **StreamableHTTP** — modern HTTP-based, supports CORS proxy + * 3. **SSE** — legacy fallback, supports CORS proxy + * + * @see mcpStore in stores/mcp.svelte.ts — reactive business logic facade on top of MCPService + * @see mcpResourceStore in stores/mcp-resources.svelte.ts — reactive resource state management + * @see agenticStore in stores/agentic.svelte.ts — uses MCPService (via mcpStore) for tool execution + * @see MCP Protocol Specification: https://modelcontextprotocol.io/specification/2025-06-18 + */ export { MCPService } from './mcp.service'; diff --git a/tools/server/webui/src/lib/services/mcp.service.ts b/tools/server/webui/src/lib/services/mcp.service.ts index 883953b5fe..14141b20f0 100644 --- a/tools/server/webui/src/lib/services/mcp.service.ts +++ b/tools/server/webui/src/lib/services/mcp.service.ts @@ -1,18 +1,3 @@ -/** - * MCPService - Stateless MCP Protocol Communication Layer - * - * Low-level MCP operations: - * - Transport creation (WebSocket, StreamableHTTP, SSE) - * - Server connection/disconnection - * - Tool discovery (listTools) - * - Tool execution (callTool) - * - * NO business logic, NO state management, NO orchestration. - * This is the protocol layer - pure MCP SDK operations. - * - * @see mcpStore in stores/mcp.svelte.ts for business logic facade - */ - import { Client } from '@modelcontextprotocol/sdk/client'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; @@ -70,7 +55,13 @@ interface ToolCallResult { export class MCPService { /** - * Create a connection log entry + * Create a connection log entry for phase tracking. + * + * @param phase - The connection phase this log belongs to + * @param message - Human-readable log message + * @param level - Log severity level (default: INFO) + * @param details - Optional structured details for debugging + * @returns Formatted connection log entry */ private static createLog( phase: MCPConnectionPhase, @@ -90,8 +81,16 @@ export class MCPService { /** * Create transport based on server configuration. * Supports WebSocket, StreamableHTTP (modern), and SSE (legacy) transports. - * When useProxy is enabled, routes HTTP requests through llama-server's CORS proxy. - * Returns both transport and the type used. + * When `useProxy` is enabled, routes HTTP requests through llama-server's CORS proxy. + * + * **Fallback Order:** + * 1. WebSocket — if explicitly configured (no CORS proxy support) + * 2. StreamableHTTP — default for HTTP connections + * 3. SSE — automatic fallback if StreamableHTTP fails + * + * @param config - Server configuration with url, transport type, proxy, and auth settings + * @returns Object containing the created transport and the transport type used + * @throws {Error} If url is missing, WebSocket + proxy combination, or all transports fail */ static createTransport(config: MCPServerConfig): { transport: Transport; @@ -166,7 +165,11 @@ export class MCPService { } /** - * Extract server info from SDK Implementation type + * Extract server info from SDK Implementation type. + * Normalizes the SDK's server version response into our MCPServerInfo type. + * + * @param impl - Raw Implementation object from MCP SDK + * @returns Normalized server info or undefined if input is empty */ private static extractServerInfo(impl: Implementation | undefined): MCPServerInfo | undefined { if (!impl) { @@ -189,9 +192,23 @@ export class MCPService { /** * Connect to a single MCP server with detailed phase tracking. - * Returns connection object with client, transport, discovered tools, and connection details. - * @param onPhase - Optional callback for connection phase changes - * @param listChangedHandlers - Optional handlers for list changed notifications + * + * Performs the full MCP connection lifecycle: + * 1. Transport creation (with automatic fallback) + * 2. Client initialization and capability exchange + * 3. Tool discovery via `listTools` + * + * Reports progress via `onPhase` callback at each step, enabling + * UI progress indicators during connection. + * + * @param serverName - Display name for the server (used in logging) + * @param serverConfig - Server URL, transport type, proxy, and auth configuration + * @param clientInfo - Optional client identification (defaults to app info) + * @param capabilities - Optional client capability declaration + * @param onPhase - Optional callback for connection phase progress updates + * @param listChangedHandlers - Optional handlers for server-initiated list change notifications + * @returns Full connection object with client, transport, tools, server info, and timing + * @throws {Error} If transport creation or connection fails */ static async connect( serverName: string, @@ -331,6 +348,9 @@ export class MCPService { /** * Disconnect from a server. + * Clears the `onclose` handler to prevent reconnection attempts on voluntary disconnect. + * + * @param connection - The active MCP connection to close */ static async disconnect(connection: MCPConnection): Promise { console.log(`[MCPService][${connection.serverName}] Disconnecting...`); @@ -348,6 +368,10 @@ export class MCPService { /** * List tools from a connection. + * Silently returns empty array on failure (logged as warning). + * + * @param connection - The MCP connection to query + * @returns Array of available tools, or empty array on error */ static async listTools(connection: MCPConnection): Promise { try { @@ -363,6 +387,10 @@ export class MCPService { /** * List prompts from a connection. + * Silently returns empty array on failure (logged as warning). + * + * @param connection - The MCP connection to query + * @returns Array of available prompts, or empty array on error */ static async listPrompts(connection: MCPConnection): Promise { try { @@ -378,6 +406,14 @@ export class MCPService { /** * Get a specific prompt with arguments. + * Unlike list operations, this throws on failure since the caller explicitly + * requested a specific prompt and needs to handle the error. + * + * @param connection - The MCP connection to use + * @param name - The prompt name to retrieve + * @param args - Optional key-value arguments to pass to the prompt + * @returns The prompt result with messages and metadata + * @throws {Error} If the prompt retrieval fails */ static async getPrompt( connection: MCPConnection, @@ -395,6 +431,14 @@ export class MCPService { /** * Execute a tool call on a connection. + * Supports abort signal for cancellable operations (e.g., when user stops generation). + * Formats the raw tool result into a string representation. + * + * @param connection - The MCP connection to execute against + * @param params - Tool name and arguments to execute + * @param signal - Optional AbortSignal for cancellation support + * @returns Formatted tool execution result with content string and error flag + * @throws {Error} If tool execution fails or is aborted */ static async callTool( connection: MCPConnection, @@ -426,7 +470,11 @@ export class MCPService { } /** - * Format tool result content to string. + * Format tool result content items to a single string. + * Handles text, image (base64 data URL), and embedded resource content types. + * + * @param result - Raw tool call result from MCP SDK + * @returns Concatenated string representation of all content items */ private static formatToolResult(result: ToolCallResult): string { const content = result.content; @@ -661,10 +709,11 @@ export class MCPService { /** * Check if a connection supports resources. - * Per MCP spec: presence of the `resources` key (even as empty object {}) indicates support. + * Per MCP spec: presence of the `resources` key (even as empty object `{}`) indicates support. * Empty object means resources are supported but no sub-features (subscribe, listChanged). + * * @param connection - The MCP connection to check - * @returns Whether the server supports resources + * @returns Whether the server declares the resources capability */ static supportsResources(connection: MCPConnection): boolean { // Per MCP spec: "Servers that support resources MUST declare the resources capability" diff --git a/tools/server/webui/src/lib/services/models.service.ts b/tools/server/webui/src/lib/services/models.service.ts index 06d7ee2a37..347f171846 100644 --- a/tools/server/webui/src/lib/services/models.service.ts +++ b/tools/server/webui/src/lib/services/models.service.ts @@ -1,21 +1,6 @@ import { ServerModelStatus } from '$lib/enums'; import { apiFetch, apiPost } from '$lib/utils'; -/** - * ModelsService - Stateless service for model management API communication - * - * This service handles communication with model-related endpoints: - * - `/v1/models` - OpenAI-compatible model list (MODEL + ROUTER mode) - * - `/models/load`, `/models/unload` - Router-specific model management (ROUTER mode only) - * - * **Responsibilities:** - * - List available models - * - Load/unload models (ROUTER mode) - * - Check model status (ROUTER mode) - * - * **Used by:** - * - modelsStore: Primary consumer for model state management - */ export class ModelsService { /** * @@ -26,16 +11,21 @@ export class ModelsService { */ /** - * Fetch list of models from OpenAI-compatible endpoint - * Works in both MODEL and ROUTER modes + * Fetch list of models from OpenAI-compatible endpoint. + * Works in both MODEL and ROUTER modes. + * + * @returns List of available models with basic metadata */ static async list(): Promise { return apiFetch('/v1/models'); } /** - * Fetch list of all models with detailed metadata (ROUTER mode) + * Fetch list of all models with detailed metadata (ROUTER mode). * Returns models with load status, paths, and other metadata + * beyond what the OpenAI-compatible endpoint provides. + * + * @returns List of models with detailed status and configuration info */ static async listRouter(): Promise { return apiFetch('/v1/models'); @@ -50,10 +40,13 @@ export class ModelsService { */ /** - * Load a model (ROUTER mode) - * POST /models/load + * Load a model (ROUTER mode only). + * Sends POST request to `/models/load`. Note: the endpoint returns success + * before loading completes — use polling to await actual load status. + * * @param modelId - Model identifier to load * @param extraArgs - Optional additional arguments to pass to the model instance + * @returns Load response from the server */ static async load(modelId: string, extraArgs?: string[]): Promise { const payload: { model: string; extra_args?: string[] } = { model: modelId }; @@ -65,9 +58,12 @@ export class ModelsService { } /** - * Unload a model (ROUTER mode) - * POST /models/unload + * Unload a model (ROUTER mode only). + * Sends POST request to `/models/unload`. Note: the endpoint returns success + * before unloading completes — use polling to await actual unload status. + * * @param modelId - Model identifier to unload + * @returns Unload response from the server */ static async unload(modelId: string): Promise { return apiPost('/models/unload', { model: modelId }); @@ -82,14 +78,20 @@ export class ModelsService { */ /** - * Check if a model is loaded based on its metadata + * Check if a model is loaded based on its metadata. + * + * @param model - Model data entry from the API response + * @returns True if the model status is LOADED */ static isModelLoaded(model: ApiModelDataEntry): boolean { return model.status.value === ServerModelStatus.LOADED; } /** - * Check if a model is currently loading + * Check if a model is currently loading. + * + * @param model - Model data entry from the API response + * @returns True if the model status is LOADING */ static isModelLoading(model: ApiModelDataEntry): boolean { return model.status.value === ServerModelStatus.LOADING; diff --git a/tools/server/webui/src/lib/services/parameter-sync.service.ts b/tools/server/webui/src/lib/services/parameter-sync.service.ts index fbc676e24c..1d7666e955 100644 --- a/tools/server/webui/src/lib/services/parameter-sync.service.ts +++ b/tools/server/webui/src/lib/services/parameter-sync.service.ts @@ -1,24 +1,12 @@ -/** - * ParameterSyncService - Handles synchronization between server defaults and user settings - * - * This service manages the complex logic of merging server-provided default parameters - * with user-configured overrides, ensuring the UI reflects the actual server state - * while preserving user customizations. - * - * **Key Responsibilities:** - * - Extract syncable parameters from server props - * - Merge server defaults with user overrides - * - Track parameter sources (server, user, default) - * - Provide sync utilities for settings store integration - */ - import { normalizeFloatingPoint } from '$lib/utils'; import type { SyncableParameter, ParameterRecord, ParameterInfo, ParameterValue } from '$lib/types'; import { SyncableParameterType, ParameterSource } from '$lib/enums'; /** - * Mapping of webui setting keys to server parameter keys - * Only parameters that should be synced from server are included + * Mapping of webui setting keys to server parameter keys. + * Only parameters listed here can be synced from the server `/props` endpoint. + * Each entry defines the webui key, corresponding server key, value type, + * and whether sync is enabled. */ export const SYNCABLE_PARAMETERS: SyncableParameter[] = [ { @@ -178,14 +166,24 @@ export class ParameterSyncService { */ /** - * Round floating-point numbers to avoid JavaScript precision issues + * Round floating-point numbers to avoid JavaScript precision issues. + * E.g., 0.1 + 0.2 = 0.30000000000000004 → 0.3 + * + * @param value - Parameter value to normalize + * @returns Precision-normalized value */ private static roundFloatingPoint(value: ParameterValue): ParameterValue { return normalizeFloatingPoint(value) as ParameterValue; } /** - * Extract server default parameters that can be synced + * Extract server default parameters that can be synced from `/props` response. + * Handles both generation settings parameters and webui-specific settings. + * Converts samplers array to semicolon-delimited string for UI display. + * + * @param serverParams - Raw generation settings from server `/props` endpoint + * @param webuiSettings - Optional webui-specific settings from server + * @returns Record of extracted parameter key-value pairs with normalized precision */ static extractServerDefaults( serverParams: ApiLlamaCppServerProps['default_generation_settings']['params'] | null, @@ -235,8 +233,14 @@ export class ParameterSyncService { */ /** - * Merge server defaults with current user settings - * Returns updated settings that respect user overrides while using server defaults + * Merge server defaults with current user settings. + * User overrides always take priority — only parameters not in `userOverrides` + * set will be updated from server defaults. + * + * @param currentSettings - Current parameter values in the settings store + * @param serverDefaults - Default values extracted from server props + * @param userOverrides - Set of parameter keys explicitly overridden by the user + * @returns Merged parameter record with user overrides preserved */ static mergeWithServerDefaults( currentSettings: ParameterRecord, @@ -264,7 +268,15 @@ export class ParameterSyncService { */ /** - * Get parameter information including source and values + * Get parameter information including source and values. + * Used by ChatSettingsParameterSourceIndicator to display the correct badge + * (Custom vs Default) for each parameter in the settings UI. + * + * @param key - The parameter key to get info for + * @param currentValue - The current value of the parameter + * @param propsDefaults - Server default values from `/props` + * @param userOverrides - Set of parameter keys explicitly overridden by the user + * @returns Parameter info with source, server default, and user override values */ static getParameterInfo( key: string, @@ -287,21 +299,30 @@ export class ParameterSyncService { } /** - * Check if a parameter can be synced from server + * Check if a parameter can be synced from server. + * + * @param key - The parameter key to check + * @returns True if the parameter is in the syncable parameters list */ static canSyncParameter(key: string): boolean { return SYNCABLE_PARAMETERS.some((param) => param.key === key && param.canSync); } /** - * Get all syncable parameter keys + * Get all syncable parameter keys. + * + * @returns Array of parameter keys that can be synced from server */ static getSyncableParameterKeys(): string[] { return SYNCABLE_PARAMETERS.filter((param) => param.canSync).map((param) => param.key); } /** - * Validate server parameter value + * Validate a server parameter value against its expected type. + * + * @param key - The parameter key to validate + * @param value - The value to validate + * @returns True if value matches the expected type for this parameter */ static validateServerParameter(key: string, value: ParameterValue): boolean { const param = SYNCABLE_PARAMETERS.find((p) => p.key === key); @@ -328,7 +349,13 @@ export class ParameterSyncService { */ /** - * Create a diff between current settings and server defaults + * Create a diff between current settings and server defaults. + * Shows which parameters differ from server values, useful for debugging + * and for the "Reset to defaults" functionality. + * + * @param currentSettings - Current parameter values in the settings store + * @param serverDefaults - Default values extracted from server props + * @returns Record of parameter diffs with current value, server value, and whether they differ */ static createParameterDiff( currentSettings: ParameterRecord, diff --git a/tools/server/webui/src/lib/services/props.service.ts b/tools/server/webui/src/lib/services/props.service.ts index 534c703f38..45c3e45773 100644 --- a/tools/server/webui/src/lib/services/props.service.ts +++ b/tools/server/webui/src/lib/services/props.service.ts @@ -1,19 +1,5 @@ import { apiFetchWithParams } from '$lib/utils'; -/** - * PropsService - Server properties management - * - * This service handles communication with the /props endpoint to retrieve - * server configuration, model information, and capabilities. - * - * **Responsibilities:** - * - Fetch server properties from /props endpoint - * - Handle API authentication - * - Parse and validate server response - * - * **Used by:** - * - serverStore: Primary consumer for server state management - */ export class PropsService { /** * @@ -24,10 +10,12 @@ export class PropsService { */ /** - * Fetches server properties from the /props endpoint + * Fetches global server properties from the `/props` endpoint. + * In MODEL mode, returns modalities for the single loaded model. + * In ROUTER mode, returns server-wide settings without model-specific modalities. * * @param autoload - If false, prevents automatic model loading (default: false) - * @returns {Promise} Server properties + * @returns Server properties including default generation settings and capabilities * @throws {Error} If the request fails or returns invalid data */ static async fetch(autoload = false): Promise { @@ -40,12 +28,13 @@ export class PropsService { } /** - * Fetches server properties for a specific model (ROUTER mode) + * Fetches server properties for a specific model (ROUTER mode only). + * Required in ROUTER mode because global `/props` does not include per-model modalities. * * @param modelId - The model ID to fetch properties for * @param autoload - If false, prevents automatic model loading (default: false) - * @returns {Promise} Server properties for the model - * @throws {Error} If the request fails or returns invalid data + * @returns Server properties specific to the requested model + * @throws {Error} If the request fails, model not found, or model not loaded */ static async fetchForModel(modelId: string, autoload = false): Promise { const params: Record = { model: modelId };