docs: Centralize and enhance service documentation

This commit is contained in:
Aleksander Grygier 2026-02-09 15:01:05 +01:00
parent 530868f5a1
commit c6e5da11e6
7 changed files with 416 additions and 172 deletions

View File

@ -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

View File

@ -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 {
/**
*

View File

@ -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=<id>` parameter
* - Handle autoload control to prevent unintended model loading
*
* **API Behavior:**
* - `GET /props` Global server props (MODEL mode: includes modalities)
* - `GET /props?model=<id>` 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';

View File

@ -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<void> {
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<Tool[]> {
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<Prompt[]> {
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"

View File

@ -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<ApiModelListResponse> {
return apiFetch<ApiModelListResponse>('/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<ApiRouterModelsListResponse> {
return apiFetch<ApiRouterModelsListResponse>('/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<ApiRouterModelsLoadResponse> {
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<ApiRouterModelsUnloadResponse> {
return apiPost<ApiRouterModelsUnloadResponse>('/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;

View File

@ -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,

View File

@ -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<ApiLlamaCppServerProps>} 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<ApiLlamaCppServerProps> {
@ -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<ApiLlamaCppServerProps>} 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<ApiLlamaCppServerProps> {
const params: Record<string, string> = { model: modelId };