ui: Restructure repo to use tools/ui folder and ui / UI / llama-ui / LLAMA_UI naming (#23064)
* webui: Move static build output from `tools/server/public` to `build/ui` directory * refactor: Move to `tools/ui` * refactor: rename CMake variables and preprocessor defines - Rename LLAMA_BUILD_WEBUI -> LLAMA_BUILD_UI (old kept as deprecated) - Rename LLAMA_USE_PREBUILT_WEBUI -> LLAMA_USE_PREBUILT_UI (old kept as deprecated) - Backward compat: old vars auto-forward to new ones with DEPRECATION warning - Rename internal vars: WEBUI_SOURCE -> UI_SOURCE, WEBUI_SOURCE_DIR -> UI_SOURCE_DIR, etc. - Rename HF bucket: LLAMA_WEBUI_HF_BUCKET -> LLAMA_UI_HF_BUCKET - Emit both LLAMA_BUILD_WEBUI and LLAMA_BUILD_UI preprocessor defines - Emit both LLAMA_WEBUI_DEFAULT_ENABLED and LLAMA_UI_DEFAULT_ENABLED * refactor: rename CLI flags (--webui -> --ui) with backward compat - Add --ui/--no-ui (old --webui/--no-webui kept as deprecated aliases) - Add --ui-config (old --webui-config kept as deprecated alias) - Add --ui-config-file (old --webui-config-file kept as deprecated alias) - Add --ui-mcp-proxy/--no-ui-mcp-proxy (old --webui-mcp-proxy kept as deprecated) - Add new env vars: LLAMA_ARG_UI, LLAMA_ARG_UI_CONFIG, LLAMA_ARG_UI_CONFIG_FILE, LLAMA_ARG_UI_MCP_PROXY - C++ struct fields: params.ui, params.ui_config_json, params.ui_mcp_proxy added alongside old fields - Backward compat: old fields synced to new ones in g_params_to_internals * refactor: update C++ server internals with backward compat - Rename json_webui_settings -> json_ui_settings (both kept in server_context_meta) - Rename params.webui usage -> params.ui (both synced, old still works) - JSON API emits both "ui"/"ui_settings" and "webui"/"webui_settings" keys - Server routes use params.ui_mcp_proxy || params.webui_mcp_proxy - Preprocessor guards use #if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) * refactor: rename CI/CD workflows, artifacts, and build script - Rename webui-build.yml -> ui-build.yml; artifact webui-build -> ui-build - Rename webui-publish.yml -> ui-publish.yml; var HF_BUCKET_WEBUI_STATIC_OUTPUT -> HF_BUCKET_UI_STATIC_OUTPUT - Rename server-webui.yml -> server-ui.yml; job webui-build/checks -> ui-build/checks - Update server.yml: job/artifact refs webui-build -> ui-build - Update release.yml: all webui-build/publish refs -> ui-build/publish; HF_TOKEN_WEBUI_STATIC_OUTPUT -> HF_TOKEN_UI_STATIC_OUTPUT - Update server-self-hosted.yml: webui-build -> ui-build - Update build-self-hosted.yml: HF_WEBUI_VERSION -> HF_UI_VERSION - Rename webui-download.cmake -> ui-download.cmake (internal refs updated) - Update labeler.yml: server/webui -> server/ui path label * docs: update CODEOWNERS and server README docs - Update CODEOWNERS: team ggml-org/llama-webui -> ggml-org/llama-ui, path /tools/server/webui/ -> /tools/ui/ - Update server README.md: CLI tables show --ui flags with deprecated --webui aliases - Update server README-dev.md: "WebUI" -> "UI", paths updated to tools/ui/ * fix: Small fixes for UI build * fix: CMake.txt syntax * chore: Formatting * fix: `.editorconfig` for llama-ui * chore: Formatting * refactor: Use `APP_NAME` in Error route * refactor: Cleanup * refactor: Single migration service * make llama-ui a linkable target * fix: UI Build output * fix: Missing change * fix: separate llama-ui npm build output into build/tools/ui/dist subfolder + use cmake npm build instead of downloading ui-build.yml artifacts in CI * refactor: UI workflows cleanup --------- Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
This commit is contained in:
co-authored by
Xuan Son Nguyen
parent
49d1701bd2
commit
59778f0196
@@ -0,0 +1,52 @@
|
||||
import type { AgenticConfig } from '$lib/types/agentic';
|
||||
|
||||
export const ATTACHMENT_SAVED_REGEX = /\[Attachment saved: ([^\]]+)\]/;
|
||||
|
||||
export const NEWLINE_SEPARATOR = '\n';
|
||||
|
||||
export const DEFAULT_AGENTIC_CONFIG: AgenticConfig = {
|
||||
enabled: true,
|
||||
maxTurns: 100,
|
||||
maxToolPreviewLines: 25
|
||||
} as const;
|
||||
|
||||
export const REASONING_TAGS = {
|
||||
START: '<think>',
|
||||
END: '</think>'
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @deprecated Legacy marker tags - only used for migration of old stored messages.
|
||||
* New messages use structured fields (reasoningContent, toolCalls, toolCallId).
|
||||
*/
|
||||
export const LEGACY_AGENTIC_TAGS = {
|
||||
TOOL_CALL_START: '<<<AGENTIC_TOOL_CALL_START>>>',
|
||||
TOOL_CALL_END: '<<<AGENTIC_TOOL_CALL_END>>>',
|
||||
TOOL_NAME_PREFIX: '<<<TOOL_NAME:',
|
||||
TOOL_ARGS_START: '<<<TOOL_ARGS_START>>>',
|
||||
TOOL_ARGS_END: '<<<TOOL_ARGS_END>>>',
|
||||
TAG_SUFFIX: '>>>'
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @deprecated Legacy reasoning tags - only used for migration of old stored messages.
|
||||
* New messages use the dedicated reasoningContent field.
|
||||
*/
|
||||
export const LEGACY_REASONING_TAGS = {
|
||||
START: '<<<reasoning_content_start>>>',
|
||||
END: '<<<reasoning_content_end>>>'
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @deprecated Legacy regex patterns - only used for migration of old stored messages.
|
||||
*/
|
||||
export const LEGACY_AGENTIC_REGEX = {
|
||||
COMPLETED_TOOL_CALL:
|
||||
/<<<AGENTIC_TOOL_CALL_START>>>\n<<<TOOL_NAME:(.+?)>>>\n<<<TOOL_ARGS_START>>>([\s\S]*?)<<<TOOL_ARGS_END>>>([\s\S]*?)<<<AGENTIC_TOOL_CALL_END>>>/g,
|
||||
REASONING_BLOCK: /<<<reasoning_content_start>>>[\s\S]*?<<<reasoning_content_end>>>/g,
|
||||
REASONING_EXTRACT: /<<<reasoning_content_start>>>([\s\S]*?)<<<reasoning_content_end>>>/,
|
||||
REASONING_OPEN: /<<<reasoning_content_start>>>[\s\S]*$/,
|
||||
AGENTIC_TOOL_CALL_BLOCK: /\n*<<<AGENTIC_TOOL_CALL_START>>>[\s\S]*?<<<AGENTIC_TOOL_CALL_END>>>/g,
|
||||
AGENTIC_TOOL_CALL_OPEN: /\n*<<<AGENTIC_TOOL_CALL_START>>>[\s\S]*$/,
|
||||
HAS_LEGACY_MARKERS: /<<<(?:AGENTIC_TOOL_CALL_START|reasoning_content_start)>>>/
|
||||
} as const;
|
||||
@@ -0,0 +1,13 @@
|
||||
export const API_MODELS = {
|
||||
LIST: '/v1/models',
|
||||
LOAD: '/models/load',
|
||||
UNLOAD: '/models/unload'
|
||||
};
|
||||
|
||||
export const API_TOOLS = {
|
||||
LIST: '/tools',
|
||||
EXECUTE: '/tools'
|
||||
};
|
||||
|
||||
/** CORS proxy endpoint path */
|
||||
export const CORS_PROXY_ENDPOINT = '/cors-proxy';
|
||||
@@ -0,0 +1,4 @@
|
||||
export const ATTACHMENT_LABEL_FILE = 'File';
|
||||
export const ATTACHMENT_LABEL_PDF_FILE = 'PDF File';
|
||||
export const ATTACHMENT_LABEL_MCP_PROMPT = 'MCP Prompt';
|
||||
export const ATTACHMENT_LABEL_MCP_RESOURCE = 'MCP Resource';
|
||||
@@ -0,0 +1,103 @@
|
||||
import type { Component } from 'svelte';
|
||||
import { MessageSquare, Zap, FolderOpen } from '@lucide/svelte';
|
||||
import { FILE_TYPE_ICONS } from '$lib/constants/icons';
|
||||
import {
|
||||
AttachmentAction,
|
||||
AttachmentItemEnabledWhen,
|
||||
AttachmentItemVisibleWhen,
|
||||
AttachmentMenuItemId
|
||||
} from '$lib/enums';
|
||||
|
||||
export interface AttachmentMenuItem {
|
||||
/** Unique identifier for the item */
|
||||
id: AttachmentMenuItemId;
|
||||
/** Display label */
|
||||
label: string;
|
||||
/** Lucide icon component */
|
||||
icon: Component;
|
||||
/** Extra CSS class applied to the item (e.g. for test selectors) */
|
||||
class?: string;
|
||||
/** Whether the item requires a specific modality to be enabled */
|
||||
enabledWhen?: AttachmentItemEnabledWhen;
|
||||
/** Tooltip shown when the item is disabled */
|
||||
disabledTooltip?: string;
|
||||
/** Callback key on the Props interface to invoke when clicked */
|
||||
action: AttachmentAction;
|
||||
/** Whether the item is only shown when a specific capability is present */
|
||||
visibleWhen?: AttachmentItemVisibleWhen;
|
||||
/** Whether this item has a tooltip even when enabled (uses dynamic text) */
|
||||
hasEnabledTooltip?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* File attachment menu items shown in both the desktop dropdown and mobile sheet.
|
||||
* The "Tools" submenu is handled separately by each component.
|
||||
*/
|
||||
export const ATTACHMENT_FILE_ITEMS: AttachmentMenuItem[] = [
|
||||
{
|
||||
id: AttachmentMenuItemId.IMAGES,
|
||||
label: 'Images',
|
||||
icon: FILE_TYPE_ICONS.image,
|
||||
class: 'images-button',
|
||||
enabledWhen: AttachmentItemEnabledWhen.HAS_VISION_MODALITY,
|
||||
disabledTooltip: 'Image processing requires a vision model',
|
||||
action: AttachmentAction.FILE_UPLOAD
|
||||
},
|
||||
{
|
||||
id: AttachmentMenuItemId.AUDIO,
|
||||
label: 'Audio Files',
|
||||
icon: FILE_TYPE_ICONS.audio,
|
||||
class: 'audio-button',
|
||||
enabledWhen: AttachmentItemEnabledWhen.HAS_AUDIO_MODALITY,
|
||||
disabledTooltip: 'Audio files processing requires an audio model',
|
||||
action: AttachmentAction.FILE_UPLOAD
|
||||
},
|
||||
{
|
||||
id: AttachmentMenuItemId.TEXT,
|
||||
label: 'Text Files',
|
||||
icon: FILE_TYPE_ICONS.text,
|
||||
enabledWhen: AttachmentItemEnabledWhen.ALWAYS,
|
||||
action: AttachmentAction.FILE_UPLOAD
|
||||
},
|
||||
{
|
||||
id: AttachmentMenuItemId.PDF,
|
||||
label: 'PDF Files',
|
||||
icon: FILE_TYPE_ICONS.pdf,
|
||||
enabledWhen: AttachmentItemEnabledWhen.ALWAYS,
|
||||
disabledTooltip: 'PDFs will be converted to text. Image-based PDFs may not work properly.',
|
||||
hasEnabledTooltip: true,
|
||||
action: AttachmentAction.FILE_UPLOAD
|
||||
}
|
||||
];
|
||||
|
||||
export const ATTACHMENT_EXTRA_ITEMS: AttachmentMenuItem[] = [
|
||||
{
|
||||
id: AttachmentMenuItemId.SYSTEM_MESSAGE,
|
||||
label: 'System Message',
|
||||
icon: MessageSquare,
|
||||
enabledWhen: AttachmentItemEnabledWhen.ALWAYS,
|
||||
hasEnabledTooltip: true,
|
||||
action: AttachmentAction.SYSTEM_PROMPT_CLICK
|
||||
}
|
||||
];
|
||||
|
||||
export const ATTACHMENT_MCP_ITEMS: AttachmentMenuItem[] = [
|
||||
{
|
||||
id: AttachmentMenuItemId.MCP_PROMPT,
|
||||
label: 'MCP Prompt',
|
||||
icon: Zap,
|
||||
enabledWhen: AttachmentItemEnabledWhen.ALWAYS,
|
||||
action: AttachmentAction.MCP_PROMPT_CLICK,
|
||||
visibleWhen: AttachmentItemVisibleWhen.HAS_MCP_PROMPTS_SUPPORT
|
||||
},
|
||||
{
|
||||
id: AttachmentMenuItemId.MCP_RESOURCES,
|
||||
label: 'MCP Resources',
|
||||
icon: FolderOpen,
|
||||
enabledWhen: AttachmentItemEnabledWhen.ALWAYS,
|
||||
action: AttachmentAction.MCP_RESOURCES_CLICK,
|
||||
visibleWhen: AttachmentItemVisibleWhen.HAS_MCP_RESOURCES_SUPPORT
|
||||
}
|
||||
];
|
||||
|
||||
export const ATTACHMENT_TOOLTIP_TEXT = 'Add files, prompts, tools or MCP Servers';
|
||||
@@ -0,0 +1,2 @@
|
||||
export const AUTO_SCROLL_INTERVAL = 100;
|
||||
export const AUTO_SCROLL_AT_BOTTOM_THRESHOLD = 10;
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { BinaryDetectionOptions } from '$lib/types';
|
||||
|
||||
export const DEFAULT_BINARY_DETECTION_OPTIONS: BinaryDetectionOptions = {
|
||||
prefixLength: 1024 * 10, // Check the first 10KB of the string
|
||||
suspiciousCharThresholdRatio: 0.15, // Allow up to 15% suspicious chars
|
||||
maxAbsoluteNullBytes: 2
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Cache configuration constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default TTL (Time-To-Live) for cache entries in milliseconds
|
||||
* @default 5 minutes
|
||||
*/
|
||||
export const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Default maximum number of entries in a cache
|
||||
* @default 100
|
||||
*/
|
||||
export const DEFAULT_CACHE_MAX_ENTRIES = 100;
|
||||
|
||||
/**
|
||||
* TTL for model props cache in milliseconds
|
||||
* Props don't change frequently, so we can cache them longer
|
||||
* @default 10 minutes
|
||||
*/
|
||||
export const MODEL_PROPS_CACHE_TTL_MS = 10 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Maximum number of model props to cache
|
||||
* @default 50
|
||||
*/
|
||||
export const MODEL_PROPS_CACHE_MAX_ENTRIES = 50;
|
||||
|
||||
/**
|
||||
* Maximum number of MCP resources to cache
|
||||
* @default 50
|
||||
*/
|
||||
export const MCP_RESOURCE_CACHE_MAX_ENTRIES = 50;
|
||||
|
||||
/**
|
||||
* TTL for MCP resource cache entries in milliseconds
|
||||
* @default 5 minutes
|
||||
*/
|
||||
export const MCP_RESOURCE_CACHE_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Maximum number of inactive conversation states to keep in memory
|
||||
* States for conversations beyond this limit will be cleaned up
|
||||
* @default 10
|
||||
*/
|
||||
export const MAX_INACTIVE_CONVERSATION_STATES = 10;
|
||||
|
||||
/**
|
||||
* Maximum age (in ms) for inactive conversation states before cleanup
|
||||
* States older than this will be removed during cleanup
|
||||
* @default 30 minutes
|
||||
*/
|
||||
export const INACTIVE_CONVERSATION_STATE_MAX_AGE_MS = 30 * 60 * 1000;
|
||||
@@ -0,0 +1,6 @@
|
||||
export const INITIAL_FILE_SIZE = 0;
|
||||
export const PROMPT_CONTENT_SEPARATOR = '\n\n';
|
||||
export const CLIPBOARD_CONTENT_QUOTE_PREFIX = '"';
|
||||
export const PROMPT_TRIGGER_PREFIX = '/';
|
||||
export const RESOURCE_TRIGGER_PREFIX = '@';
|
||||
export const NEW_CHAT_DRAFT_KEY = '__new_chat__';
|
||||
@@ -0,0 +1,6 @@
|
||||
export const CLI_FLAGS = {
|
||||
API_KEY: '--api-key',
|
||||
MCP_PROXY: '--ui-mcp-proxy',
|
||||
SLOTS: '--slots',
|
||||
TOOLS: '--tools'
|
||||
} as const;
|
||||
@@ -0,0 +1,8 @@
|
||||
export const CODE_BLOCK_SCROLL_CONTAINER_CLASS = 'code-block-scroll-container';
|
||||
export const CODE_BLOCK_WRAPPER_CLASS = 'code-block-wrapper';
|
||||
export const CODE_BLOCK_HEADER_CLASS = 'code-block-header';
|
||||
export const CODE_BLOCK_ACTIONS_CLASS = 'code-block-actions';
|
||||
export const CODE_LANGUAGE_CLASS = 'code-language';
|
||||
export const COPY_CODE_BTN_CLASS = 'copy-code-btn';
|
||||
export const PREVIEW_CODE_BTN_CLASS = 'preview-code-btn';
|
||||
export const RELATIVE_CLASS = 'relative';
|
||||
@@ -0,0 +1,7 @@
|
||||
export const NEWLINE = '\n';
|
||||
export const DEFAULT_LANGUAGE = 'text';
|
||||
export const LANG_PATTERN = /^(\w*)\n?/;
|
||||
export const AMPERSAND_REGEX = /&/g;
|
||||
export const LT_REGEX = /</g;
|
||||
export const GT_REGEX = />/g;
|
||||
export const FENCE_PATTERN = /^```|\n```/g;
|
||||
@@ -0,0 +1,4 @@
|
||||
export const CONTEXT_KEY_MESSAGE_EDIT = 'chat-message-edit';
|
||||
export const CONTEXT_KEY_CHAT_ACTIONS = 'chat-actions';
|
||||
export const CONTEXT_KEY_CHAT_SETTINGS_CONFIG = 'chat-settings-config';
|
||||
export const CONTEXT_KEY_PROCESSING_INFO = 'processing-info';
|
||||
@@ -0,0 +1,19 @@
|
||||
export const BOX_BORDER =
|
||||
'border border-border/30 focus-within:border-border dark:border-border/20 dark:focus-within:border-border';
|
||||
|
||||
export const INPUT_CLASSES = `
|
||||
bg-muted/60 dark:bg-muted/75
|
||||
${BOX_BORDER}
|
||||
shadow-sm
|
||||
outline-none
|
||||
text-foreground
|
||||
`;
|
||||
|
||||
export const PANEL_CLASSES = `
|
||||
bg-background
|
||||
border border-border/30 dark:border-border/20
|
||||
shadow-sm backdrop-blur-lg!
|
||||
rounded-t-lg!
|
||||
`;
|
||||
|
||||
export const CHAT_FORM_POPOVER_MAX_HEIGHT = 'max-h-80';
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Database-related constants (IndexedDB, Dexie).
|
||||
*
|
||||
* Centralized to ensure consistency across the app and simplify future
|
||||
* naming changes.
|
||||
*/
|
||||
|
||||
import { STORAGE_APP_NAME } from './storage';
|
||||
|
||||
/** IndexedDB database name */
|
||||
export const DB_NAME = STORAGE_APP_NAME;
|
||||
|
||||
/** IndexedDB store / table names */
|
||||
export const IDXDB_TABLES = {
|
||||
conversations: 'conversations',
|
||||
messages: 'messages'
|
||||
} as const;
|
||||
|
||||
/** IndexedDB store schemas */
|
||||
export const IDXDB_STORE_SCHEMAS = {
|
||||
conversations: 'id, lastModified, currNode, name',
|
||||
messages: 'id, convId, type, role, timestamp, parent, children'
|
||||
} as const;
|
||||
|
||||
/** Combined Dexie stores definition — keys are table names, values are schemas */
|
||||
export const IDXDB_STORES = {
|
||||
[IDXDB_TABLES.conversations]: IDXDB_STORE_SCHEMAS.conversations,
|
||||
[IDXDB_TABLES.messages]: IDXDB_STORE_SCHEMAS.messages
|
||||
} as const;
|
||||
@@ -0,0 +1,2 @@
|
||||
export const VIEWPORT_GUTTER = 8;
|
||||
export const MENU_OFFSET = 6;
|
||||
@@ -0,0 +1,8 @@
|
||||
export const MS_PER_SECOND = 1000;
|
||||
export const SECONDS_PER_MINUTE = 60;
|
||||
export const SECONDS_PER_HOUR = 3600;
|
||||
export const SHORT_DURATION_THRESHOLD = 1;
|
||||
export const MEDIUM_DURATION_THRESHOLD = 10;
|
||||
|
||||
/** Default display value when no performance time is available */
|
||||
export const DEFAULT_PERFORMANCE_TIME = '0s';
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Icon mappings for file types and model modalities
|
||||
* Centralized configuration to ensure consistent icon usage across the app
|
||||
*/
|
||||
|
||||
import {
|
||||
File as FileIcon,
|
||||
FileText as FileTextIcon,
|
||||
Image as ImageIcon,
|
||||
Eye as VisionIcon,
|
||||
Mic as AudioIcon
|
||||
} from '@lucide/svelte';
|
||||
import { FileTypeCategory, ModelModality } from '$lib/enums';
|
||||
|
||||
export const FILE_TYPE_ICONS = {
|
||||
[FileTypeCategory.IMAGE]: ImageIcon,
|
||||
[FileTypeCategory.AUDIO]: AudioIcon,
|
||||
[FileTypeCategory.TEXT]: FileTextIcon,
|
||||
[FileTypeCategory.PDF]: FileIcon
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_FILE_ICON = FileIcon;
|
||||
|
||||
export const MODALITY_ICONS = {
|
||||
[ModelModality.VISION]: VisionIcon,
|
||||
[ModelModality.AUDIO]: AudioIcon
|
||||
} as const;
|
||||
|
||||
export const MODALITY_LABELS = {
|
||||
[ModelModality.VISION]: 'Vision',
|
||||
[ModelModality.AUDIO]: 'Audio'
|
||||
} as const;
|
||||
@@ -0,0 +1,45 @@
|
||||
// Central constants export file
|
||||
// All constants should be imported from '$lib/constants'
|
||||
|
||||
export * from './agentic';
|
||||
export * from './api-endpoints';
|
||||
export * from './attachment-labels';
|
||||
export * from './database';
|
||||
export * from './storage';
|
||||
export * from './attachment-menu';
|
||||
export * from './auto-scroll';
|
||||
export * from './binary-detection';
|
||||
export * from './cache';
|
||||
export * from './chat-form';
|
||||
export * from './cli-flags';
|
||||
export * from './code-blocks';
|
||||
export * from './code';
|
||||
export * from './context-keys';
|
||||
export * from './css-classes';
|
||||
export * from './floating-ui-constraints';
|
||||
export * from './formatters';
|
||||
export * from './key-value-pairs';
|
||||
export * from './icons';
|
||||
export * from './latex-protection';
|
||||
export * from './literal-html';
|
||||
export * from './markdown';
|
||||
export * from './max-bundle-size';
|
||||
export * from './mcp';
|
||||
export * from './mcp-form';
|
||||
export * from './mcp-resource';
|
||||
export * from './message-export';
|
||||
export * from './model-id';
|
||||
export * from './precision';
|
||||
export * from './processing-info';
|
||||
export * from './routes';
|
||||
export * from './settings-keys';
|
||||
export * from './settings-registry';
|
||||
export * from './supported-file-types';
|
||||
export * from './table-html-restorer';
|
||||
export * from './title-generation';
|
||||
export * from './tools';
|
||||
export * from './tooltip-config';
|
||||
export * from './ui';
|
||||
export * from './uri-template';
|
||||
export * from './url';
|
||||
export * from './viewport';
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Key-value pair form constraints and sanitization patterns.
|
||||
*
|
||||
* Both regexes target characters dangerous in HTTP-header / env-var contexts:
|
||||
* \x00 – null byte (injection)
|
||||
* \x0A (\n) – LF (HTTP header injection / response splitting)
|
||||
* \x0D (\r) – CR (HTTP header injection / response splitting)
|
||||
* \x01–\x08, \x0B–\x0C, \x0E–\x1F, \x7F – other C0/DEL control chars
|
||||
*
|
||||
* KEY_UNSAFE_RE additionally strips TAB (\x09); values keep TAB because it is
|
||||
* a valid header-value continuation character per RFC 7230.
|
||||
*/
|
||||
|
||||
export const KEY_VALUE_PAIR_KEY_MAX_LENGTH = 256;
|
||||
export const KEY_VALUE_PAIR_VALUE_MAX_LENGTH = 8192;
|
||||
|
||||
// eslint-disable-next-line no-control-regex
|
||||
export const KEY_VALUE_PAIR_UNSAFE_KEY_RE = /[\x00-\x1F\x7F]/g;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
export const KEY_VALUE_PAIR_UNSAFE_VALUE_RE = /[\x00-\x08\x0A-\x0D\x0E-\x1F\x7F]/g;
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Matches common Markdown code blocks to exclude them from further processing (e.g. LaTeX).
|
||||
* - Fenced: ```...```
|
||||
* - Inline: `...` (does NOT support nested backticks or multi-backtick syntax)
|
||||
*
|
||||
* Note: This pattern does not handle advanced cases like:
|
||||
* `` `code with `backticks` `` or \\``...\\``
|
||||
*/
|
||||
export const CODE_BLOCK_REGEXP = /(```[\s\S]*?```|`[^`\n]+`)/g;
|
||||
|
||||
/**
|
||||
* Matches LaTeX math delimiters \(...\) and \[...\] only when not preceded by a backslash (i.e., not escaped),
|
||||
* while also capturing code blocks (```, `...`) so they can be skipped during processing.
|
||||
*
|
||||
* Uses negative lookbehind `(?<!\\)` to avoid matching \\( or \\[.
|
||||
* Using the look‑behind pattern `(?<!\\)` we skip matches
|
||||
* that are preceded by a backslash, e.g.
|
||||
* `Definitions\\(also called macros)` (title of chapter 20 in The TeXbook)
|
||||
* or `\\[4pt]` (LaTeX line-break).
|
||||
*
|
||||
* group 1: code-block
|
||||
* group 2: square-bracket
|
||||
* group 3: round-bracket
|
||||
*/
|
||||
export const LATEX_MATH_AND_CODE_PATTERN =
|
||||
/(```[\S\s]*?```|`.*?`)|(?<!\\)\\\[([\S\s]*?[^\\])\\]|(?<!\\)\\\((.*?)\\\)/g;
|
||||
|
||||
/** Regex to capture the content of a $$...\\\\...$$ block (display-formula with line-break) */
|
||||
export const LATEX_LINEBREAK_REGEXP = /\$\$([\s\S]*?\\\\[\s\S]*?)\$\$/;
|
||||
|
||||
/** map from mchem-regexp to replacement */
|
||||
export const MHCHEM_PATTERN_MAP: readonly [RegExp, string][] = [
|
||||
[/(\s)\$\\ce{/g, '$1$\\\\ce{'],
|
||||
[/(\s)\$\\pu{/g, '$1$\\\\pu{']
|
||||
] as const;
|
||||
@@ -0,0 +1,15 @@
|
||||
export const LINE_BREAK = /\r?\n/;
|
||||
|
||||
export const PHRASE_PARENTS = new Set([
|
||||
'paragraph',
|
||||
'heading',
|
||||
'emphasis',
|
||||
'strong',
|
||||
'delete',
|
||||
'link',
|
||||
'linkReference',
|
||||
'tableCell'
|
||||
]);
|
||||
|
||||
export const NBSP = '\u00a0';
|
||||
export const TAB_AS_SPACES = NBSP.repeat(4);
|
||||
@@ -0,0 +1,4 @@
|
||||
export const IMAGE_NOT_ERROR_BOUND_SELECTOR = 'img:not([data-error-bound])';
|
||||
export const DATA_ERROR_BOUND_ATTR = 'errorBound';
|
||||
export const DATA_ERROR_HANDLED_ATTR = 'errorHandled';
|
||||
export const BOOL_TRUE_STRING = 'true';
|
||||
@@ -0,0 +1 @@
|
||||
export const MAX_BUNDLE_SIZE = 2 * 1024 * 1024;
|
||||
@@ -0,0 +1,2 @@
|
||||
export const MCP_SERVER_URL_PLACEHOLDER = 'https://mcp.example.com/sse';
|
||||
export const MIN_AUTOCOMPLETE_INPUT_LENGTH = 1;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { MimeTypeImage } from '$lib/enums';
|
||||
|
||||
// File extension patterns for resource type detection
|
||||
export const IMAGE_FILE_EXTENSION_REGEX = /\.(png|jpg|jpeg|gif|svg|webp)$/i;
|
||||
export const CODE_FILE_EXTENSION_REGEX =
|
||||
/\.(js|ts|json|yaml|yml|xml|html|css|py|rs|go|java|cpp|c|h|rb|sh|toml)$/i;
|
||||
export const TEXT_FILE_EXTENSION_REGEX = /\.(txt|md|log)$/i;
|
||||
|
||||
// URI protocol prefix pattern
|
||||
export const PROTOCOL_PREFIX_REGEX = /^[a-z]+:\/\//;
|
||||
|
||||
// File extension regex for display name extraction
|
||||
export const FILE_EXTENSION_REGEX = /\.[^.]+$/;
|
||||
|
||||
// Separator regex for splitting display names (kebab-case/snake_case)
|
||||
export const DISPLAY_NAME_SEPARATOR_REGEX = /[-_]/;
|
||||
|
||||
// Regex for matching base64-encoded data URIs
|
||||
export const DATA_URI_BASE64_REGEX = /^data:([^;]+);base64,([A-Za-z0-9+/]+=*)$/;
|
||||
|
||||
// Prefix for MCP attachment filenames
|
||||
export const MCP_ATTACHMENT_NAME_PREFIX = 'mcp-attachment';
|
||||
|
||||
// Prefix for MCP resource attachment IDs
|
||||
export const MCP_RESOURCE_ATTACHMENT_ID_PREFIX = 'res';
|
||||
|
||||
// Default file extension for unknown image types
|
||||
export const DEFAULT_IMAGE_EXTENSION = 'img';
|
||||
|
||||
// Default filename for resource content downloads
|
||||
export const DEFAULT_RESOURCE_FILENAME = 'resource.txt';
|
||||
|
||||
// Path separator for resource URI parsing
|
||||
export const PATH_SEPARATOR = '/';
|
||||
|
||||
// Separator for joining text content from multiple resource parts
|
||||
export const RESOURCE_TEXT_CONTENT_SEPARATOR = '\n\n';
|
||||
|
||||
// Fallback text for unknown content types
|
||||
export const RESOURCE_UNKNOWN_TYPE = 'unknown type';
|
||||
|
||||
// Label prefix for binary blob content
|
||||
export const BINARY_CONTENT_LABEL = 'Binary content';
|
||||
|
||||
/**
|
||||
* Mapping from image MIME types to file extensions.
|
||||
* Used for generating attachment filenames from MIME types.
|
||||
*/
|
||||
export const IMAGE_MIME_TO_EXTENSION: Record<string, string> = {
|
||||
[MimeTypeImage.JPEG]: 'jpg',
|
||||
[MimeTypeImage.JPG]: 'jpg',
|
||||
[MimeTypeImage.PNG]: 'png',
|
||||
[MimeTypeImage.GIF]: 'gif',
|
||||
[MimeTypeImage.WEBP]: 'webp'
|
||||
} as const;
|
||||
@@ -0,0 +1,86 @@
|
||||
import { Zap, Globe, Radio } from '@lucide/svelte';
|
||||
import { MCPTransportType } from '$lib/enums';
|
||||
import type { ClientCapabilities, Implementation } from '$lib/types';
|
||||
import type { Component } from 'svelte';
|
||||
import { MimeTypeImage } from '$lib/enums/files';
|
||||
|
||||
export const DEFAULT_CLIENT_VERSION = '1.0.0';
|
||||
export const MCP_CLIENT_NAME = 'llama-ui-mcp';
|
||||
export const DEFAULT_IMAGE_MIME_TYPE = MimeTypeImage.PNG;
|
||||
|
||||
/** MIME types considered safe for rendering MCP server icons */
|
||||
export const MCP_ALLOWED_ICON_MIME_TYPES = new Set([
|
||||
MimeTypeImage.PNG,
|
||||
MimeTypeImage.JPEG,
|
||||
MimeTypeImage.JPG,
|
||||
MimeTypeImage.SVG,
|
||||
MimeTypeImage.WEBP,
|
||||
MimeTypeImage.ICO,
|
||||
MimeTypeImage.ICO_MICROSOFT
|
||||
]);
|
||||
|
||||
/**
|
||||
* MCP specification version this client targets.
|
||||
* Update when the upstream MCP spec introduces a new stable version:
|
||||
* https://spec.modelcontextprotocol.io/
|
||||
*/
|
||||
export const MCP_PROTOCOL_VERSION = '2025-06-18';
|
||||
|
||||
export const DEFAULT_MCP_CONFIG = {
|
||||
protocolVersion: MCP_PROTOCOL_VERSION,
|
||||
capabilities: { tools: { listChanged: true } } as ClientCapabilities,
|
||||
clientInfo: { name: MCP_CLIENT_NAME, version: DEFAULT_CLIENT_VERSION } as Implementation,
|
||||
requestTimeoutSeconds: 300, // 5 minutes for long-running tools
|
||||
connectionTimeoutMs: 10_000 // 10 seconds for connection establishment
|
||||
} as const;
|
||||
|
||||
export const MCP_SERVER_ID_PREFIX = 'LlamaUI-MCP-Server';
|
||||
|
||||
export const MCP_RECONNECT_INITIAL_DELAY = 1000;
|
||||
export const MCP_RECONNECT_BACKOFF_MULTIPLIER = 2;
|
||||
export const MCP_RECONNECT_MAX_DELAY = 30000;
|
||||
/** Per-attempt timeout for a single reconnection attempt before giving up and backing off. */
|
||||
export const MCP_RECONNECT_ATTEMPT_TIMEOUT_MS = 15_000;
|
||||
|
||||
/** Maximum number of MCP server avatars to display in the chat form */
|
||||
export const MAX_DISPLAYED_MCP_AVATARS = 4;
|
||||
|
||||
/** Expected count when two theme-less icons represent a light/dark pair */
|
||||
export const EXPECTED_THEMED_ICON_PAIR_COUNT = 2;
|
||||
|
||||
/** CORS proxy URL query parameter name */
|
||||
export const CORS_PROXY_URL_PARAM = 'url';
|
||||
|
||||
/** Number of trailing characters to keep visible when partially redacting mcp-session-id */
|
||||
export const MCP_SESSION_ID_VISIBLE_CHARS = 5;
|
||||
|
||||
/** Partial-redaction rules for MCP headers: header name -> visible trailing chars */
|
||||
export const MCP_PARTIAL_REDACT_HEADERS = new Map<string, number>([
|
||||
['mcp-session-id', MCP_SESSION_ID_VISIBLE_CHARS]
|
||||
]);
|
||||
|
||||
/** Header names whose values should be redacted in diagnostic logs */
|
||||
export const REDACTED_HEADERS = new Set([
|
||||
'authorization',
|
||||
'api-key',
|
||||
'cookie',
|
||||
'mcp-session-id',
|
||||
'proxy-authorization',
|
||||
'set-cookie',
|
||||
'x-auth-token',
|
||||
'x-api-key'
|
||||
]);
|
||||
|
||||
/** Human-readable labels for MCP transport types */
|
||||
export const MCP_TRANSPORT_LABELS: Record<MCPTransportType, string> = {
|
||||
[MCPTransportType.WEBSOCKET]: 'WebSocket',
|
||||
[MCPTransportType.STREAMABLE_HTTP]: 'HTTP',
|
||||
[MCPTransportType.SSE]: 'SSE'
|
||||
};
|
||||
|
||||
/** Icon components for MCP transport types */
|
||||
export const MCP_TRANSPORT_ICONS: Record<MCPTransportType, Component> = {
|
||||
[MCPTransportType.WEBSOCKET]: Zap,
|
||||
[MCPTransportType.STREAMABLE_HTTP]: Globe,
|
||||
[MCPTransportType.SSE]: Radio
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// Conversation filename constants
|
||||
|
||||
// Length of the trimmed conversation ID in the filename
|
||||
export const EXPORT_CONV_ID_TRIM_LENGTH = 8;
|
||||
// Maximum length of the sanitized conversation name snippet
|
||||
export const EXPORT_CONV_NAME_SUFFIX_MAX_LENGTH = 20;
|
||||
// Characters to keep in the ISO timestamp. 19 keeps 2026-01-01T00:00:00
|
||||
export const ISO_TIMESTAMP_SLICE_LENGTH = 19;
|
||||
|
||||
// Replacements for making the conversation title filename-friendly
|
||||
export const NON_ALPHANUMERIC_REGEX = /[^a-z0-9]/gi;
|
||||
export const EXPORT_CONV_NONALNUM_REPLACEMENT = '_';
|
||||
export const MULTIPLE_UNDERSCORE_REGEX = /_+/g;
|
||||
|
||||
// Replacements to the ISO date for use in the export filename
|
||||
export const ISO_DATE_TIME_SEPARATOR = 'T';
|
||||
export const ISO_DATE_TIME_SEPARATOR_REPLACEMENT = '_';
|
||||
|
||||
export const ISO_TIME_SEPARATOR = ':';
|
||||
export const ISO_TIME_SEPARATOR_REPLACEMENT = '-';
|
||||
@@ -0,0 +1,39 @@
|
||||
/** Sentinel value returned by `indexOf` when a substring is not found. */
|
||||
export const MODEL_ID_NOT_FOUND = -1;
|
||||
|
||||
/** Separates `<org>` from `<model>` in a model ID, e.g. `org/ModelName`. */
|
||||
export const MODEL_ID_ORG_SEPARATOR = '/';
|
||||
|
||||
/** Separates named segments within the model path, e.g. `ModelName-7B-GGUF`. */
|
||||
export const MODEL_ID_SEGMENT_SEPARATOR = '-';
|
||||
|
||||
/** Separates the model path from the quantization tag, e.g. `model:Q4_K_M`. */
|
||||
export const MODEL_ID_QUANTIZATION_SEPARATOR = ':';
|
||||
|
||||
/**
|
||||
* Matches a quantization/precision segment, e.g. `Q4_K_M`, `IQ4_XS`, `F16`, `BF16`, `MXFP4`.
|
||||
* Case-insensitive to handle both uppercase and lowercase inputs.
|
||||
*/
|
||||
export const MODEL_QUANTIZATION_SEGMENT_RE =
|
||||
/^(I?Q\d+(_[A-Z0-9]+)*|F\d+|BF\d+|MXFP\d+(_[A-Z0-9]+)*)$/i;
|
||||
|
||||
/**
|
||||
* Matches prefix for custom quantization types, e.g. `UD-Q8_K_XL`.
|
||||
*/
|
||||
export const MODEL_CUSTOM_QUANTIZATION_PREFIX_RE = /^UD$/i;
|
||||
|
||||
/**
|
||||
* Matches a parameter-count segment, e.g. `7B`, `1.5b`, `120M`.
|
||||
*/
|
||||
export const MODEL_PARAMS_RE = /^\d+(\.\d+)?[BbMmKkTt]$/;
|
||||
|
||||
/**
|
||||
* Matches an activated-parameter-count segment, e.g. `A10B`, `a2.4b`.
|
||||
* The leading `A`/`a` distinguishes it from a regular params segment.
|
||||
*/
|
||||
export const MODEL_ACTIVATED_PARAMS_RE = /^[Aa]\d+(\.\d+)?[BbMmKkTt]$/;
|
||||
|
||||
/**
|
||||
* Container format segments to exclude from tags (every model uses these).
|
||||
*/
|
||||
export const MODEL_IGNORED_SEGMENTS = new Set(['GGUF', 'GGML']);
|
||||
@@ -0,0 +1,2 @@
|
||||
export const PRECISION_MULTIPLIER = 1000000;
|
||||
export const PRECISION_DECIMAL_PLACES = 6;
|
||||
@@ -0,0 +1,8 @@
|
||||
export const PROCESSING_INFO_TIMEOUT = 2000;
|
||||
|
||||
/**
|
||||
* Statistics units labels
|
||||
*/
|
||||
export const STATS_UNITS = {
|
||||
TOKENS_PER_SECOND: 't/s'
|
||||
} as const;
|
||||
@@ -0,0 +1,26 @@
|
||||
export const NEW_CHAT_PARAM = 'new_chat';
|
||||
|
||||
/** Settings section slugs — used for routes and navigation. */
|
||||
export const SETTINGS_SECTION_SLUGS = {
|
||||
GENERAL: 'general',
|
||||
DISPLAY: 'display',
|
||||
SAMPLING: 'sampling',
|
||||
PENALTIES: 'penalties',
|
||||
AGENTIC: 'agentic',
|
||||
DEVELOPER: 'developer',
|
||||
TOOLS: 'tools',
|
||||
IMPORT_EXPORT: 'import-export'
|
||||
} as const;
|
||||
|
||||
export const ROUTES = {
|
||||
/** Root — start of the app. */
|
||||
START: '#/',
|
||||
/** New chat — root with new chat query param. */
|
||||
NEW_CHAT: `?${NEW_CHAT_PARAM}=true#/`,
|
||||
/** Chat base — for dynamic chat URLs use RouterService. */
|
||||
CHAT: '#/chat',
|
||||
/** MCP servers. */
|
||||
MCP_SERVERS: '#/mcp-servers',
|
||||
/** Settings base — for dynamic settings URLs use RouterService. */
|
||||
SETTINGS: '#/settings'
|
||||
} as const;
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Settings key constants for ChatSettings configuration.
|
||||
*
|
||||
* These keys correspond to properties in SettingsConfigType and are used
|
||||
* in settings field configurations to ensure consistency.
|
||||
*/
|
||||
export const SETTINGS_KEYS = {
|
||||
// General
|
||||
THEME: 'theme',
|
||||
API_KEY: 'apiKey',
|
||||
SYSTEM_MESSAGE: 'systemMessage',
|
||||
PASTE_LONG_TEXT_TO_FILE_LEN: 'pasteLongTextToFileLen',
|
||||
COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT: 'copyTextAttachmentsAsPlainText',
|
||||
SEND_ON_ENTER: 'sendOnEnter',
|
||||
ENABLE_CONTINUE_GENERATION: 'enableContinueGeneration',
|
||||
PDF_AS_IMAGE: 'pdfAsImage',
|
||||
ASK_FOR_TITLE_CONFIRMATION: 'askForTitleConfirmation',
|
||||
TITLE_GENERATION_USE_FIRST_LINE: 'titleGenerationUseFirstLine',
|
||||
TITLE_GENERATION_USE_LLM: 'titleGenerationUseLLM',
|
||||
TITLE_GENERATION_PROMPT: 'titleGenerationPrompt',
|
||||
// Display
|
||||
SHOW_MESSAGE_STATS: 'showMessageStats',
|
||||
SHOW_THOUGHT_IN_PROGRESS: 'showThoughtInProgress',
|
||||
KEEP_STATS_VISIBLE: 'keepStatsVisible',
|
||||
AUTO_MIC_ON_EMPTY: 'autoMicOnEmpty',
|
||||
RENDER_USER_CONTENT_AS_MARKDOWN: 'renderUserContentAsMarkdown',
|
||||
DISABLE_AUTO_SCROLL: 'disableAutoScroll',
|
||||
ALWAYS_SHOW_SIDEBAR_ON_DESKTOP: 'alwaysShowSidebarOnDesktop',
|
||||
FULL_HEIGHT_CODE_BLOCKS: 'fullHeightCodeBlocks',
|
||||
SHOW_RAW_MODEL_NAMES: 'showRawModelNames',
|
||||
SHOW_SYSTEM_MESSAGE: 'showSystemMessage',
|
||||
// Sampling
|
||||
TEMPERATURE: 'temperature',
|
||||
DYNATEMP_RANGE: 'dynatemp_range',
|
||||
DYNATEMP_EXPONENT: 'dynatemp_exponent',
|
||||
TOP_K: 'top_k',
|
||||
TOP_P: 'top_p',
|
||||
MIN_P: 'min_p',
|
||||
XTC_PROBABILITY: 'xtc_probability',
|
||||
XTC_THRESHOLD: 'xtc_threshold',
|
||||
TYP_P: 'typ_p',
|
||||
MAX_TOKENS: 'max_tokens',
|
||||
SAMPLERS: 'samplers',
|
||||
BACKEND_SAMPLING: 'backend_sampling',
|
||||
// Penalties
|
||||
REPEAT_LAST_N: 'repeat_last_n',
|
||||
REPEAT_PENALTY: 'repeat_penalty',
|
||||
PRESENCE_PENALTY: 'presence_penalty',
|
||||
FREQUENCY_PENALTY: 'frequency_penalty',
|
||||
DRY_MULTIPLIER: 'dry_multiplier',
|
||||
DRY_BASE: 'dry_base',
|
||||
DRY_ALLOWED_LENGTH: 'dry_allowed_length',
|
||||
DRY_PENALTY_LAST_N: 'dry_penalty_last_n',
|
||||
// MCP
|
||||
MCP_SERVERS: 'mcpServers',
|
||||
AGENTIC_MAX_TURNS: 'agenticMaxTurns',
|
||||
ALWAYS_SHOW_AGENTIC_TURNS: 'alwaysShowAgenticTurns',
|
||||
AGENTIC_MAX_TOOL_PREVIEW_LINES: 'agenticMaxToolPreviewLines',
|
||||
SHOW_TOOL_CALL_IN_PROGRESS: 'showToolCallInProgress',
|
||||
// Performance
|
||||
PRE_ENCODE_CONVERSATION: 'preEncodeConversation',
|
||||
// Developer
|
||||
DISABLE_REASONING_PARSING: 'disableReasoningParsing',
|
||||
EXCLUDE_REASONING_FROM_CONTEXT: 'excludeReasoningFromContext',
|
||||
SHOW_RAW_OUTPUT_SWITCH: 'showRawOutputSwitch',
|
||||
// PY_INTERPRETER_ENABLED: 'pyInterpreterEnabled',
|
||||
CUSTOM: 'custom'
|
||||
} as const;
|
||||
@@ -0,0 +1,759 @@
|
||||
import { ColorMode } from '$lib/enums/ui';
|
||||
import { SettingsFieldType } from '$lib/enums/settings';
|
||||
import { SyncableParameterType } from '$lib/enums';
|
||||
import {
|
||||
Funnel,
|
||||
AlertTriangle,
|
||||
Code,
|
||||
Monitor,
|
||||
ListRestart,
|
||||
Sliders,
|
||||
PencilRuler,
|
||||
Database,
|
||||
Monitor as MonitorIcon,
|
||||
Sun,
|
||||
Moon
|
||||
} from '@lucide/svelte';
|
||||
import type { Component } from 'svelte';
|
||||
import type {
|
||||
SettingsConfigValue,
|
||||
SyncableParameter,
|
||||
SettingsEntry,
|
||||
SettingsSectionTitle,
|
||||
SettingsSectionEntry,
|
||||
SettingsSection
|
||||
} from '$lib/types';
|
||||
import { CLI_FLAGS } from '$lib/constants';
|
||||
import { SETTINGS_KEYS } from './settings-keys';
|
||||
import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes';
|
||||
import { TITLE_GENERATION } from './title-generation';
|
||||
|
||||
export const SETTINGS_SECTION_TITLES = {
|
||||
GENERAL: 'General',
|
||||
DISPLAY: 'Display',
|
||||
SAMPLING: 'Sampling',
|
||||
PENALTIES: 'Penalties',
|
||||
AGENTIC: 'Agentic',
|
||||
TOOLS: 'Tools',
|
||||
IMPORT_EXPORT: 'Import/Export',
|
||||
DEVELOPER: 'Developer'
|
||||
} as const;
|
||||
|
||||
const STANDALONE_SECTIONS: { title: SettingsSectionTitle; slug: string; icon: Component }[] = [
|
||||
{ title: SETTINGS_SECTION_TITLES.TOOLS, slug: SETTINGS_SECTION_SLUGS.TOOLS, icon: PencilRuler },
|
||||
{
|
||||
title: SETTINGS_SECTION_TITLES.IMPORT_EXPORT,
|
||||
slug: SETTINGS_SECTION_SLUGS.IMPORT_EXPORT,
|
||||
icon: Database
|
||||
}
|
||||
];
|
||||
|
||||
const COLOR_MODE_OPTIONS: Array<{ value: string; label: string; icon: Component }> = [
|
||||
{ value: ColorMode.SYSTEM, label: 'System', icon: MonitorIcon },
|
||||
{ value: ColorMode.LIGHT, label: 'Light', icon: Sun },
|
||||
{ value: ColorMode.DARK, label: 'Dark', icon: Moon }
|
||||
];
|
||||
|
||||
const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
[SETTINGS_SECTION_SLUGS.GENERAL]: {
|
||||
title: SETTINGS_SECTION_TITLES.GENERAL,
|
||||
slug: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
icon: Sliders,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.THEME,
|
||||
label: 'Theme',
|
||||
help: 'Choose the color theme for the interface. You can choose between System (follows your device settings), Light, or Dark.',
|
||||
defaultValue: ColorMode.SYSTEM,
|
||||
type: SettingsFieldType.SELECT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
options: COLOR_MODE_OPTIONS,
|
||||
sync: { serverKey: SETTINGS_KEYS.THEME, paramType: SyncableParameterType.STRING }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.API_KEY,
|
||||
label: 'API Key',
|
||||
help: `Set the API Key if you are using <code> ${CLI_FLAGS.API_KEY} </code> option for the server.`,
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SYSTEM_MESSAGE,
|
||||
label: 'System Message',
|
||||
help: 'The starting message that defines how model should behave.',
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SYSTEM_MESSAGE,
|
||||
paramType: SyncableParameterType.STRING
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,
|
||||
label: 'Paste long text to file length',
|
||||
help: 'On pasting long text, it will be converted to a file. You can control the file length by setting the value of this parameter. Value 0 means disable.',
|
||||
defaultValue: 2500,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SEND_ON_ENTER,
|
||||
label: 'Send message on Enter',
|
||||
help: 'Use Enter to send messages and Shift + Enter for new lines. When disabled, use Ctrl/Cmd + Enter.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SEND_ON_ENTER,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT,
|
||||
label: 'Copy text attachments as plain text',
|
||||
help: 'When copying a message with text attachments, combine them into a single plain text string instead of a special format that can be pasted back as attachments.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,
|
||||
label: 'Enable "Continue" button',
|
||||
help: 'Enable "Continue" button for assistant messages, including reasoning models.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
isExperimental: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.PDF_AS_IMAGE,
|
||||
label: 'Parse PDF as image',
|
||||
help: 'Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PDF_AS_IMAGE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,
|
||||
label: 'Ask for confirmation before changing conversation title',
|
||||
help: 'Ask for confirmation before automatically changing conversation title when editing the first message.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ASK_FOR_TITLE_CONFIRMATION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,
|
||||
label: 'Use first non-empty line for conversation title',
|
||||
help: 'Use only the first non-empty line of the prompt to generate the conversation title.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,
|
||||
label: 'Use LLM to generate conversation title',
|
||||
help: 'Use the LLM to automatically generate conversation titles based on the first message exchange.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
isExperimental: true
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_PROMPT,
|
||||
label: 'LLM title generation prompt',
|
||||
help: 'Optional template for the title generation prompt. Use {{USER}} for the user message and {{ASSISTANT}} for the assistant message.',
|
||||
defaultValue: TITLE_GENERATION.DEFAULT_PROMPT,
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
}
|
||||
]
|
||||
},
|
||||
[SETTINGS_SECTION_SLUGS.DISPLAY]: {
|
||||
title: SETTINGS_SECTION_TITLES.DISPLAY,
|
||||
slug: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
icon: Monitor,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_MESSAGE_STATS,
|
||||
label: 'Show message generation statistics',
|
||||
help: 'Display generation statistics (tokens/second, token count, duration) below each assistant message.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_MESSAGE_STATS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,
|
||||
label: 'Show thought in progress',
|
||||
help: 'Expand thought process by default when generating messages.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,
|
||||
label: 'Show tool call in progress',
|
||||
help: 'Automatically expand tool call details while executing and keep them expanded after completion.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_TOOL_CALL_IN_PROGRESS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.KEEP_STATS_VISIBLE,
|
||||
label: 'Keep stats visible after generation',
|
||||
help: 'Keep processing statistics visible after generation finishes.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.KEEP_STATS_VISIBLE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,
|
||||
label: 'Show microphone on empty input',
|
||||
help: 'Automatically show microphone button instead of send button when textarea is empty for models with audio modality support.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
isExperimental: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,
|
||||
label: 'Render user content as Markdown',
|
||||
help: 'Render user messages using markdown formatting in the chat.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,
|
||||
label: 'Use full height code blocks',
|
||||
help: 'Always display code blocks at their full natural height, overriding any height limits.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DISABLE_AUTO_SCROLL,
|
||||
label: 'Disable automatic scroll',
|
||||
help: 'Disable automatic scrolling while messages stream so you can control the viewport position manually.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DISABLE_AUTO_SCROLL,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP,
|
||||
label: 'Always show sidebar on desktop',
|
||||
help: 'Always keep the sidebar visible on desktop instead of auto-hiding it.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,
|
||||
label: 'Show raw model names',
|
||||
help: 'Display full raw model identifiers (e.g. "ggml-org/GLM-4.7-Flash-GGUF:Q8_0") instead of parsed names with badges.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS,
|
||||
label: 'Always show agentic turns in conversation',
|
||||
help: 'Always expand and display agentic loop turns in conversation messages.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ALWAYS_SHOW_AGENTIC_TURNS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[SETTINGS_SECTION_SLUGS.SAMPLING]: {
|
||||
title: SETTINGS_SECTION_TITLES.SAMPLING,
|
||||
slug: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
icon: Funnel,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.TEMPERATURE,
|
||||
label: 'Temperature',
|
||||
help: 'Controls the randomness of the generated text by affecting the probability distribution of the output tokens. Higher = more random, lower = more focused.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.TEMPERATURE,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DYNATEMP_RANGE,
|
||||
label: 'Dynamic temperature range',
|
||||
help: 'Addon for the temperature sampler. The added value to the range of dynamic temperature, which adjusts probabilities by entropy of tokens.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DYNATEMP_RANGE,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DYNATEMP_EXPONENT,
|
||||
label: 'Dynamic temperature exponent',
|
||||
help: 'Addon for the temperature sampler. Smoothes out the probability redistribution based on the most probable token.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DYNATEMP_EXPONENT,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TOP_K,
|
||||
label: 'Top K',
|
||||
help: 'Keeps only k top tokens.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: { serverKey: SETTINGS_KEYS.TOP_K, paramType: SyncableParameterType.NUMBER }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TOP_P,
|
||||
label: 'Top P',
|
||||
help: 'Limits tokens to those that together have a cumulative probability of at least p',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: { serverKey: SETTINGS_KEYS.TOP_P, paramType: SyncableParameterType.NUMBER }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MIN_P,
|
||||
label: 'Min P',
|
||||
help: 'Limits tokens based on the minimum probability for a token to be considered, relative to the probability of the most likely token.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: { serverKey: SETTINGS_KEYS.MIN_P, paramType: SyncableParameterType.NUMBER }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.XTC_PROBABILITY,
|
||||
label: 'XTC probability',
|
||||
help: 'XTC sampler cuts out top tokens; this parameter controls the chance of cutting tokens at all. 0 disables XTC.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.XTC_PROBABILITY,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.XTC_THRESHOLD,
|
||||
label: 'XTC threshold',
|
||||
help: 'XTC sampler cuts out top tokens; this parameter controls the token probability that is required to cut that token.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.XTC_THRESHOLD,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TYP_P,
|
||||
label: 'Typical P',
|
||||
help: 'Sorts and limits tokens based on the difference between log-probability and entropy.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: { serverKey: SETTINGS_KEYS.TYP_P, paramType: SyncableParameterType.NUMBER }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MAX_TOKENS,
|
||||
label: 'Max tokens',
|
||||
help: 'The maximum number of token per output. Use -1 for infinite (no limit).',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.MAX_TOKENS,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SAMPLERS,
|
||||
label: 'Samplers',
|
||||
help: 'The order at which samplers are applied, in simplified way. Default is "top_k;typ_p;top_p;min_p;temperature": top_k->typ_p->top_p->min_p->temperature',
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: { serverKey: SETTINGS_KEYS.SAMPLERS, paramType: SyncableParameterType.STRING }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.BACKEND_SAMPLING,
|
||||
label: 'Backend sampling',
|
||||
help: 'Enable backend-based samplers. When enabled, supported samplers run on the accelerator backend for faster sampling.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.BACKEND_SAMPLING,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[SETTINGS_SECTION_SLUGS.PENALTIES]: {
|
||||
title: SETTINGS_SECTION_TITLES.PENALTIES,
|
||||
slug: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
icon: AlertTriangle,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.REPEAT_LAST_N,
|
||||
label: 'Repeat last N',
|
||||
help: 'Last n tokens to consider for penalizing repetition',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.REPEAT_LAST_N,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.REPEAT_PENALTY,
|
||||
label: 'Repeat penalty',
|
||||
help: 'Controls the repetition of token sequences in the generated text',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.REPEAT_PENALTY,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.PRESENCE_PENALTY,
|
||||
label: 'Presence penalty',
|
||||
help: 'Limits tokens based on whether they appear in the output or not.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PRESENCE_PENALTY,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.FREQUENCY_PENALTY,
|
||||
label: 'Frequency penalty',
|
||||
help: 'Limits tokens based on how often they appear in the output.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.FREQUENCY_PENALTY,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DRY_MULTIPLIER,
|
||||
label: 'DRY multiplier',
|
||||
help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling multiplier.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DRY_MULTIPLIER,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DRY_BASE,
|
||||
label: 'DRY base',
|
||||
help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the DRY sampling base value.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: { serverKey: SETTINGS_KEYS.DRY_BASE, paramType: SyncableParameterType.NUMBER }
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DRY_ALLOWED_LENGTH,
|
||||
label: 'DRY allowed length',
|
||||
help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets the allowed length for DRY sampling.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DRY_ALLOWED_LENGTH,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DRY_PENALTY_LAST_N,
|
||||
label: 'DRY penalty last N',
|
||||
help: 'DRY sampling reduces repetition in generated text even across long contexts. This parameter sets DRY penalty for the last n tokens.',
|
||||
defaultValue: undefined,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.PENALTIES,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DRY_PENALTY_LAST_N,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[SETTINGS_SECTION_SLUGS.AGENTIC]: {
|
||||
title: SETTINGS_SECTION_TITLES.AGENTIC,
|
||||
slug: SETTINGS_SECTION_SLUGS.AGENTIC,
|
||||
icon: ListRestart,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.AGENTIC_MAX_TURNS,
|
||||
label: 'Agentic turns',
|
||||
help: 'Maximum number of tool execution cycles before stopping (prevents infinite loops).',
|
||||
defaultValue: 10,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.AGENTIC,
|
||||
isPositiveInteger: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.AGENTIC_MAX_TURNS,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.AGENTIC_MAX_TOOL_PREVIEW_LINES,
|
||||
label: 'Max lines per tool preview',
|
||||
help: 'Number of lines shown in tool output previews (last N lines). Only these previews and the final LLM response persist after the agentic loop completes.',
|
||||
defaultValue: 25,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.AGENTIC,
|
||||
isPositiveInteger: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.AGENTIC_MAX_TOOL_PREVIEW_LINES,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[SETTINGS_SECTION_SLUGS.DEVELOPER]: {
|
||||
title: SETTINGS_SECTION_TITLES.DEVELOPER,
|
||||
slug: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
icon: Code,
|
||||
settings: [
|
||||
{
|
||||
key: SETTINGS_KEYS.PRE_ENCODE_CONVERSATION,
|
||||
label: 'Pre-fill KV cache after response',
|
||||
help: 'After each response, re-submit the conversation to pre-fill the server KV cache. Makes the next turn faster since the prompt is already encoded while you read the response.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DISABLE_REASONING_PARSING,
|
||||
label: 'Disable reasoning content parsing',
|
||||
help: 'Send reasoning_format=none so the server returns thinking tokens inline instead of extracting them into a separate field.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT,
|
||||
label: 'Exclude reasoning from context',
|
||||
help: 'Strip thinking from previous messages before sending. When off, thinking is sent back via the reasoning_content field so the model sees its own chain-of-thought across turns.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH,
|
||||
label: 'Enable raw output toggle',
|
||||
help: 'Show toggle button to display messages as plain text instead of Markdown-formatted content',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.CUSTOM,
|
||||
label: 'Custom JSON',
|
||||
help: 'Custom JSON parameters to send to the API. Must be valid JSON format.',
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
}
|
||||
]
|
||||
}
|
||||
} as const;
|
||||
|
||||
const NON_UI_SETTINGS: SettingsEntry[] = [
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE,
|
||||
label: 'Show system message',
|
||||
help: 'Display the system message at the top of each conversation.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MCP_SERVERS,
|
||||
label: 'MCP servers',
|
||||
help: 'Configure MCP servers as a JSON list. Use the form in the MCP Client settings section to edit.',
|
||||
defaultValue: '[]',
|
||||
type: SettingsFieldType.INPUT,
|
||||
sync: { serverKey: SETTINGS_KEYS.MCP_SERVERS, paramType: SyncableParameterType.STRING }
|
||||
}
|
||||
// {
|
||||
// key: SETTINGS_KEYS.PY_INTERPRETER_ENABLED,
|
||||
// label: 'Python interpreter enabled',
|
||||
// help: 'Enable Python interpreter using Pyodide. Allows running Python code in markdown code blocks.',
|
||||
// defaultValue: false,
|
||||
// type: SettingsFieldType.CHECKBOX,
|
||||
// isExperimental: true,
|
||||
// sync: { serverKey: SETTINGS_KEYS.PY_INTERPRETER_ENABLED, paramType: SyncableParameterType.BOOLEAN }
|
||||
// }
|
||||
];
|
||||
|
||||
function getAllSettings(): SettingsEntry[] {
|
||||
const result: SettingsEntry[] = [];
|
||||
for (const section of Object.values(SETTINGS_REGISTRY)) {
|
||||
result.push(...section.settings);
|
||||
}
|
||||
result.push(...NON_UI_SETTINGS);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Flat config object stored in localStorage. */
|
||||
export const SETTING_CONFIG_DEFAULT: Record<string, SettingsConfigValue> = Object.fromEntries(
|
||||
getAllSettings().map((s) => [s.key, s.defaultValue])
|
||||
) as Record<string, SettingsConfigValue>;
|
||||
|
||||
/** Help text for every setting (including non-UI). */
|
||||
export const SETTING_CONFIG_INFO: Record<string, string> = Object.fromEntries(
|
||||
getAllSettings().map((s) => [s.key, s.help])
|
||||
) as Record<string, string>;
|
||||
|
||||
/** Theme select options. */
|
||||
export const SETTINGS_COLOR_MODES_CONFIG = COLOR_MODE_OPTIONS;
|
||||
|
||||
export type { SettingsSectionTitle } from '$lib/types';
|
||||
export type { SettingsSection } from '$lib/types';
|
||||
|
||||
/** Sidebar sections + field configs (as consumed by UI). */
|
||||
export const SETTINGS_CHAT_SECTIONS: SettingsSection[] = [
|
||||
...Object.values(SETTINGS_REGISTRY).map((section) => ({
|
||||
title: section.title,
|
||||
slug: section.slug,
|
||||
icon: section.icon,
|
||||
fields: section.settings.map((s) => ({
|
||||
key: s.key,
|
||||
label: s.label,
|
||||
type: s.type,
|
||||
isExperimental: s.isExperimental,
|
||||
help: s.help,
|
||||
options: s.options
|
||||
}))
|
||||
})),
|
||||
...STANDALONE_SECTIONS
|
||||
];
|
||||
|
||||
/** INPUT-type settings whose value is a number. */
|
||||
export const NUMERIC_FIELDS = getAllSettings()
|
||||
.filter((s) => s.type === SettingsFieldType.INPUT && typeof s.defaultValue !== 'string')
|
||||
.map((s) => s.key) as readonly string[];
|
||||
|
||||
/** Numeric fields clamped to ≥ 1 and rounded. */
|
||||
export const POSITIVE_INTEGER_FIELDS = getAllSettings()
|
||||
.filter((s) => s.isPositiveInteger)
|
||||
.map((s) => s.key) as readonly string[];
|
||||
|
||||
/** Derived for the parameter sync service. */
|
||||
export const SYNCABLE_PARAMETERS: SyncableParameter[] = getAllSettings()
|
||||
.filter((s) => s.sync !== undefined)
|
||||
.map((s) => ({
|
||||
key: s.key,
|
||||
serverKey: s.sync!.serverKey,
|
||||
type: s.sync!.paramType,
|
||||
canSync: true
|
||||
}));
|
||||
|
||||
export const SETTINGS_FALLBACK_EXIT_ROUTE = ROUTES.START;
|
||||
|
||||
export { SETTINGS_KEYS } from './settings-keys';
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Storage-related constants (localStorage, IndexedDB).
|
||||
*
|
||||
* Centralized to ensure consistency across the app and simplify future
|
||||
* name changes.
|
||||
*/
|
||||
|
||||
/** Name prefix for all localStorage keys */
|
||||
export const STORAGE_APP_NAME = 'LlamaUi';
|
||||
|
||||
/** Deprecated localStorage key prefix (old app name) */
|
||||
export const STORAGE_APP_NAME_DEPRECATED = 'LlamaCppWebui';
|
||||
|
||||
/** @deprecated Deprecated IndexedDB name — will be removed after all users have migrated */
|
||||
export const DB_APP_NAME_DEPRECATED = 'LlamacppWebui';
|
||||
|
||||
export const ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.alwaysAllowedTools`;
|
||||
export const CONFIG_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.config`;
|
||||
export const DISABLED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.disabledTools`;
|
||||
export const FAVORITE_MODELS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.favoriteModels`;
|
||||
export const MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.mcpDefaultEnabled`;
|
||||
export const USER_OVERRIDES_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME}.userOverrides`;
|
||||
|
||||
// Deprecated old key names (kept for backward compat while users migrate)
|
||||
/** @deprecated Use {@link ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.alwaysAllowedTools`;
|
||||
/** @deprecated Use {@link CONFIG_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_CONFIG_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.config`;
|
||||
/** @deprecated Use {@link DISABLED_TOOLS_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_DISABLED_TOOLS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.disabledTools`;
|
||||
/** @deprecated Use {@link FAVORITE_MODELS_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_FAVORITE_MODELS_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.favoriteModels`;
|
||||
/** @deprecated Use {@link MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.mcpDefaultEnabled`;
|
||||
/** @deprecated Use {@link USER_OVERRIDES_LOCALSTORAGE_KEY} instead */
|
||||
export const DEPRECATED_USER_OVERRIDES_LOCALSTORAGE_KEY = `${STORAGE_APP_NAME_DEPRECATED}.userOverrides`;
|
||||
|
||||
/** Maps new keys to their deprecated fallback keys */
|
||||
export const NEW_TO_DEPRECATED_MAP: Record<string, string> = {
|
||||
[ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY]: DEPRECATED_ALWAYS_ALLOWED_TOOLS_LOCALSTORAGE_KEY,
|
||||
[CONFIG_LOCALSTORAGE_KEY]: DEPRECATED_CONFIG_LOCALSTORAGE_KEY,
|
||||
[DISABLED_TOOLS_LOCALSTORAGE_KEY]: DEPRECATED_DISABLED_TOOLS_LOCALSTORAGE_KEY,
|
||||
[FAVORITE_MODELS_LOCALSTORAGE_KEY]: DEPRECATED_FAVORITE_MODELS_LOCALSTORAGE_KEY,
|
||||
[MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY]: DEPRECATED_MCP_DEFAULT_ENABLED_LOCALSTORAGE_KEY,
|
||||
[USER_OVERRIDES_LOCALSTORAGE_KEY]: DEPRECATED_USER_OVERRIDES_LOCALSTORAGE_KEY
|
||||
};
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* Comprehensive dictionary of all supported file types in llama-ui
|
||||
* Organized by category with TypeScript enums for better type safety
|
||||
*/
|
||||
|
||||
import {
|
||||
FileExtensionAudio,
|
||||
FileExtensionImage,
|
||||
FileExtensionPdf,
|
||||
FileExtensionText,
|
||||
FileTypeAudio,
|
||||
FileTypeImage,
|
||||
FileTypePdf,
|
||||
FileTypeText,
|
||||
MimeTypeAudio,
|
||||
MimeTypeImage,
|
||||
MimeTypeApplication,
|
||||
MimeTypeText
|
||||
} from '$lib/enums';
|
||||
|
||||
// File type configuration using enums
|
||||
export const AUDIO_FILE_TYPES = {
|
||||
[FileTypeAudio.MP3]: {
|
||||
extensions: [FileExtensionAudio.MP3],
|
||||
mimeTypes: [MimeTypeAudio.MP3_MPEG, MimeTypeAudio.MP3]
|
||||
},
|
||||
[FileTypeAudio.WAV]: {
|
||||
extensions: [FileExtensionAudio.WAV],
|
||||
mimeTypes: [MimeTypeAudio.WAV]
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const IMAGE_FILE_TYPES = {
|
||||
[FileTypeImage.JPEG]: {
|
||||
extensions: [FileExtensionImage.JPG, FileExtensionImage.JPEG],
|
||||
mimeTypes: [MimeTypeImage.JPEG]
|
||||
},
|
||||
[FileTypeImage.PNG]: {
|
||||
extensions: [FileExtensionImage.PNG],
|
||||
mimeTypes: [MimeTypeImage.PNG]
|
||||
},
|
||||
[FileTypeImage.GIF]: {
|
||||
extensions: [FileExtensionImage.GIF],
|
||||
mimeTypes: [MimeTypeImage.GIF]
|
||||
},
|
||||
[FileTypeImage.WEBP]: {
|
||||
extensions: [FileExtensionImage.WEBP],
|
||||
mimeTypes: [MimeTypeImage.WEBP]
|
||||
},
|
||||
[FileTypeImage.SVG]: {
|
||||
extensions: [FileExtensionImage.SVG],
|
||||
mimeTypes: [MimeTypeImage.SVG]
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const PDF_FILE_TYPES = {
|
||||
[FileTypePdf.PDF]: {
|
||||
extensions: [FileExtensionPdf.PDF],
|
||||
mimeTypes: [MimeTypeApplication.PDF]
|
||||
}
|
||||
} as const;
|
||||
|
||||
export const TEXT_FILE_TYPES = {
|
||||
[FileTypeText.PLAIN_TEXT]: {
|
||||
extensions: [FileExtensionText.TXT],
|
||||
mimeTypes: [MimeTypeText.PLAIN]
|
||||
},
|
||||
[FileTypeText.MARKDOWN]: {
|
||||
extensions: [FileExtensionText.MD],
|
||||
mimeTypes: [MimeTypeText.MARKDOWN]
|
||||
},
|
||||
[FileTypeText.ASCIIDOC]: {
|
||||
extensions: [FileExtensionText.ADOC],
|
||||
mimeTypes: [MimeTypeText.ASCIIDOC]
|
||||
},
|
||||
[FileTypeText.JAVASCRIPT]: {
|
||||
extensions: [FileExtensionText.JS],
|
||||
mimeTypes: [MimeTypeText.JAVASCRIPT, MimeTypeText.JAVASCRIPT_APP]
|
||||
},
|
||||
[FileTypeText.TYPESCRIPT]: {
|
||||
extensions: [FileExtensionText.TS],
|
||||
mimeTypes: [MimeTypeText.TYPESCRIPT]
|
||||
},
|
||||
[FileTypeText.JSX]: {
|
||||
extensions: [FileExtensionText.JSX],
|
||||
mimeTypes: [MimeTypeText.JSX]
|
||||
},
|
||||
[FileTypeText.TSX]: {
|
||||
extensions: [FileExtensionText.TSX],
|
||||
mimeTypes: [MimeTypeText.TSX]
|
||||
},
|
||||
[FileTypeText.CSS]: {
|
||||
extensions: [FileExtensionText.CSS],
|
||||
mimeTypes: [MimeTypeText.CSS]
|
||||
},
|
||||
[FileTypeText.HTML]: {
|
||||
extensions: [FileExtensionText.HTML, FileExtensionText.HTM],
|
||||
mimeTypes: [MimeTypeText.HTML]
|
||||
},
|
||||
[FileTypeText.JSON]: {
|
||||
extensions: [FileExtensionText.JSON],
|
||||
mimeTypes: [MimeTypeText.JSON]
|
||||
},
|
||||
[FileTypeText.XML]: {
|
||||
extensions: [FileExtensionText.XML],
|
||||
mimeTypes: [MimeTypeText.XML_TEXT, MimeTypeText.XML_APP]
|
||||
},
|
||||
[FileTypeText.YAML]: {
|
||||
extensions: [FileExtensionText.YAML, FileExtensionText.YML],
|
||||
mimeTypes: [MimeTypeText.YAML_TEXT, MimeTypeText.YAML_APP]
|
||||
},
|
||||
[FileTypeText.CSV]: {
|
||||
extensions: [FileExtensionText.CSV],
|
||||
mimeTypes: [MimeTypeText.CSV]
|
||||
},
|
||||
[FileTypeText.LOG]: {
|
||||
extensions: [FileExtensionText.LOG],
|
||||
mimeTypes: [MimeTypeText.PLAIN]
|
||||
},
|
||||
[FileTypeText.PYTHON]: {
|
||||
extensions: [FileExtensionText.PY],
|
||||
mimeTypes: [MimeTypeText.PYTHON]
|
||||
},
|
||||
[FileTypeText.JAVA]: {
|
||||
extensions: [FileExtensionText.JAVA],
|
||||
mimeTypes: [MimeTypeText.JAVA]
|
||||
},
|
||||
[FileTypeText.CPP]: {
|
||||
extensions: [
|
||||
FileExtensionText.CPP,
|
||||
FileExtensionText.C,
|
||||
FileExtensionText.H,
|
||||
FileExtensionText.HPP
|
||||
],
|
||||
mimeTypes: [MimeTypeText.CPP_SRC, MimeTypeText.CPP_HDR, MimeTypeText.C_SRC, MimeTypeText.C_HDR]
|
||||
},
|
||||
[FileTypeText.PHP]: {
|
||||
extensions: [FileExtensionText.PHP],
|
||||
mimeTypes: [MimeTypeText.PHP]
|
||||
},
|
||||
[FileTypeText.RUBY]: {
|
||||
extensions: [FileExtensionText.RB],
|
||||
mimeTypes: [MimeTypeText.RUBY]
|
||||
},
|
||||
[FileTypeText.GO]: {
|
||||
extensions: [FileExtensionText.GO],
|
||||
mimeTypes: [MimeTypeText.GO]
|
||||
},
|
||||
[FileTypeText.RUST]: {
|
||||
extensions: [FileExtensionText.RS],
|
||||
mimeTypes: [MimeTypeText.RUST]
|
||||
},
|
||||
[FileTypeText.SHELL]: {
|
||||
extensions: [FileExtensionText.SH, FileExtensionText.BAT],
|
||||
mimeTypes: [MimeTypeText.SHELL, MimeTypeText.BAT]
|
||||
},
|
||||
[FileTypeText.SQL]: {
|
||||
extensions: [FileExtensionText.SQL],
|
||||
mimeTypes: [MimeTypeText.SQL]
|
||||
},
|
||||
[FileTypeText.R]: {
|
||||
extensions: [FileExtensionText.R],
|
||||
mimeTypes: [MimeTypeText.R]
|
||||
},
|
||||
[FileTypeText.SCALA]: {
|
||||
extensions: [FileExtensionText.SCALA],
|
||||
mimeTypes: [MimeTypeText.SCALA]
|
||||
},
|
||||
[FileTypeText.KOTLIN]: {
|
||||
extensions: [FileExtensionText.KT],
|
||||
mimeTypes: [MimeTypeText.KOTLIN]
|
||||
},
|
||||
[FileTypeText.SWIFT]: {
|
||||
extensions: [FileExtensionText.SWIFT],
|
||||
mimeTypes: [MimeTypeText.SWIFT]
|
||||
},
|
||||
[FileTypeText.DART]: {
|
||||
extensions: [FileExtensionText.DART],
|
||||
mimeTypes: [MimeTypeText.DART]
|
||||
},
|
||||
[FileTypeText.VUE]: {
|
||||
extensions: [FileExtensionText.VUE],
|
||||
mimeTypes: [MimeTypeText.VUE]
|
||||
},
|
||||
[FileTypeText.SVELTE]: {
|
||||
extensions: [FileExtensionText.SVELTE],
|
||||
mimeTypes: [MimeTypeText.SVELTE]
|
||||
},
|
||||
[FileTypeText.LATEX]: {
|
||||
extensions: [FileExtensionText.TEX],
|
||||
mimeTypes: [MimeTypeText.LATEX, MimeTypeText.TEX, MimeTypeText.TEX_APP]
|
||||
},
|
||||
[FileTypeText.BIBTEX]: {
|
||||
extensions: [FileExtensionText.BIB],
|
||||
mimeTypes: [MimeTypeText.BIBTEX]
|
||||
},
|
||||
[FileTypeText.CUDA]: {
|
||||
extensions: [FileExtensionText.CU, FileExtensionText.CUH],
|
||||
mimeTypes: [MimeTypeText.CUDA]
|
||||
},
|
||||
[FileTypeText.VULKAN]: {
|
||||
extensions: [FileExtensionText.COMP],
|
||||
mimeTypes: [MimeTypeText.PLAIN]
|
||||
},
|
||||
[FileTypeText.HASKELL]: {
|
||||
extensions: [FileExtensionText.HS],
|
||||
mimeTypes: [MimeTypeText.HASKELL]
|
||||
},
|
||||
[FileTypeText.CSHARP]: {
|
||||
extensions: [FileExtensionText.CS],
|
||||
mimeTypes: [MimeTypeText.CSHARP]
|
||||
},
|
||||
[FileTypeText.PROPERTIES]: {
|
||||
extensions: [FileExtensionText.PROPERTIES],
|
||||
mimeTypes: [MimeTypeText.PROPERTIES]
|
||||
}
|
||||
} as const;
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Matches <br>, <br/>, <br /> tags (case-insensitive).
|
||||
* Used to detect line breaks in table cell text content.
|
||||
*/
|
||||
export const BR_PATTERN = /<br\s*\/?\s*>/gi;
|
||||
|
||||
/**
|
||||
* Matches a complete <ul>...</ul> block.
|
||||
* Captures the inner content (group 1) for further <li> extraction.
|
||||
* Case-insensitive, allows multiline content.
|
||||
*/
|
||||
export const LIST_PATTERN = /^<ul>([\s\S]*)<\/ul>$/i;
|
||||
|
||||
/**
|
||||
* Matches individual <li>...</li> elements within a list.
|
||||
* Captures the inner content (group 1) of each list item.
|
||||
* Non-greedy to handle multiple consecutive items.
|
||||
* Case-insensitive, allows multiline content.
|
||||
*/
|
||||
export const LI_PATTERN = /<li>([\s\S]*?)<\/li>/gi;
|
||||
@@ -0,0 +1,9 @@
|
||||
/* Title generation constants */
|
||||
export const TITLE_GENERATION = {
|
||||
MIN_LENGTH: 3,
|
||||
FALLBACK: 'New Chat',
|
||||
DEFAULT_PROMPT:
|
||||
'Based on the following interaction, generate a short, concise title (maximum 6-8 words) that captures the main topic. Return ONLY the title text, nothing else. Do not use quotes.\n\nUser: {{USER}}\n\nAssistant: {{ASSISTANT}}\n\nTitle:',
|
||||
PREFIX_PATTERN: /^(Title:|Subject:|Topic:)\s*/i,
|
||||
QUOTE_PATTERN: /^["]|["]$/g
|
||||
} as const;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ToolSource } from '$lib/enums/tools';
|
||||
|
||||
export const TOOL_GROUP_LABELS = {
|
||||
[ToolSource.BUILTIN]: 'Built-in',
|
||||
[ToolSource.CUSTOM]: 'JSON Schema'
|
||||
} as const;
|
||||
|
||||
export const TOOL_SERVER_LABELS = {
|
||||
[ToolSource.BUILTIN]: 'Built-in Tools',
|
||||
[ToolSource.CUSTOM]: 'Custom Tools'
|
||||
} as const;
|
||||
@@ -0,0 +1 @@
|
||||
export const TOOLTIP_DELAY_DURATION = 500;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Settings, Search, SquarePen } from '@lucide/svelte';
|
||||
import McpLogo from '$lib/components/app/mcp/McpLogo.svelte';
|
||||
import type { Component } from 'svelte';
|
||||
import { ROUTES } from './routes';
|
||||
|
||||
export const FORK_TREE_DEPTH_PADDING = 8;
|
||||
export const SYSTEM_MESSAGE_PLACEHOLDER = 'System message';
|
||||
export const APP_NAME = import.meta.env.VITE_PUBLIC_APP_NAME || 'llama-ui';
|
||||
|
||||
export const ICON_STRIP_TRANSITION_DURATION = 150;
|
||||
export const ICON_STRIP_TRANSITION_DELAY_MULTIPLIER = 50;
|
||||
|
||||
export interface DesktopIconStripItem {
|
||||
icon: Component;
|
||||
tooltip: string;
|
||||
route?: string;
|
||||
activeRouteId?: string;
|
||||
activeRoutePrefix?: string;
|
||||
keys?: string[];
|
||||
}
|
||||
|
||||
export const SIDEBAR_ACTIONS_ITEMS: DesktopIconStripItem[] = [
|
||||
{ icon: SquarePen, tooltip: 'New chat', route: ROUTES.NEW_CHAT, keys: ['shift', 'cmd', 'o'] },
|
||||
{ icon: Search, tooltip: 'Search', keys: ['cmd', 'k'] },
|
||||
{
|
||||
icon: McpLogo,
|
||||
tooltip: 'MCP Servers',
|
||||
route: ROUTES.MCP_SERVERS,
|
||||
activeRouteId: '/mcp-servers'
|
||||
},
|
||||
{
|
||||
icon: Settings,
|
||||
tooltip: 'Settings',
|
||||
route: ROUTES.SETTINGS,
|
||||
activeRoutePrefix: '/settings'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* URI Template constants for RFC 6570 template processing.
|
||||
*/
|
||||
|
||||
/** URI scheme separator */
|
||||
export const URI_SCHEME_SEPARATOR = '://';
|
||||
|
||||
/** Regex to match template expressions like {var}, {+var}, {#var}, {/var} */
|
||||
export const TEMPLATE_EXPRESSION_REGEX = /\{([+#./;?&]?)([^}]+)\}/g;
|
||||
|
||||
/** RFC 6570 URI template operators */
|
||||
export const URI_TEMPLATE_OPERATORS = {
|
||||
/** Simple string expansion (default) */
|
||||
SIMPLE: '',
|
||||
/** Reserved expansion */
|
||||
RESERVED: '+',
|
||||
/** Fragment expansion */
|
||||
FRAGMENT: '#',
|
||||
/** Path segment expansion */
|
||||
PATH_SEGMENT: '/',
|
||||
/** Label expansion */
|
||||
LABEL: '.',
|
||||
/** Path-style parameters */
|
||||
PATH_PARAM: ';',
|
||||
/** Form-style query */
|
||||
FORM_QUERY: '?',
|
||||
/** Form-style query continuation */
|
||||
FORM_CONTINUATION: '&'
|
||||
} as const;
|
||||
|
||||
/** URI template separators used in expansion */
|
||||
export const URI_TEMPLATE_SEPARATORS = {
|
||||
/** Comma separator for list expansion */
|
||||
COMMA: ',',
|
||||
/** Slash separator for path segments */
|
||||
SLASH: '/',
|
||||
/** Period separator for label expansion */
|
||||
PERIOD: '.',
|
||||
/** Semicolon separator for path parameters */
|
||||
SEMICOLON: ';',
|
||||
/** Question mark prefix for query string */
|
||||
QUERY_PREFIX: '?',
|
||||
/** Ampersand prefix for query continuation */
|
||||
QUERY_CONTINUATION: '&'
|
||||
} as const;
|
||||
|
||||
/** Maximum number of leading slashes to strip during URI normalization */
|
||||
export const MAX_LEADING_SLASHES_TO_STRIP = 3;
|
||||
|
||||
/** Regex to strip explode modifier (*) from variable names */
|
||||
export const VARIABLE_EXPLODE_MODIFIER_REGEX = /[*]$/;
|
||||
|
||||
/** Regex to strip prefix modifier (:N) from variable names */
|
||||
export const VARIABLE_PREFIX_MODIFIER_REGEX = /:[\d]+$/;
|
||||
|
||||
/** Regex to strip one or more leading slashes */
|
||||
export const LEADING_SLASHES_REGEX = /^\/+/;
|
||||
@@ -0,0 +1,186 @@
|
||||
const STD = ['com', 'net', 'org', 'gov', 'edu'] as const;
|
||||
|
||||
const STD_MIL = [...STD, 'mil'] as const;
|
||||
|
||||
const ccTLD_PREFIXES: Record<string, readonly string[]> = {
|
||||
// --- Standard 5 only ---
|
||||
ar: STD,
|
||||
bd: STD,
|
||||
bg: STD,
|
||||
cn: STD_MIL,
|
||||
eg: STD,
|
||||
gr: STD,
|
||||
hk: STD,
|
||||
hr: STD,
|
||||
lk: STD,
|
||||
mx: STD_MIL,
|
||||
my: STD_MIL,
|
||||
ng: STD,
|
||||
ph: STD,
|
||||
pk: STD,
|
||||
pl: STD,
|
||||
ro: STD,
|
||||
ru: STD,
|
||||
sa: STD,
|
||||
si: STD,
|
||||
tr: STD,
|
||||
tw: STD,
|
||||
ua: STD,
|
||||
ve: STD,
|
||||
|
||||
au: [...STD_MIL, 'id', 'asn', 'csiro'],
|
||||
br: [
|
||||
...STD_MIL,
|
||||
'art',
|
||||
'eco',
|
||||
'eng',
|
||||
'inf',
|
||||
'med',
|
||||
'psi',
|
||||
'tmp',
|
||||
'etc',
|
||||
'adm',
|
||||
'adv',
|
||||
'arq',
|
||||
'bio',
|
||||
'bmd',
|
||||
'cim',
|
||||
'cng',
|
||||
'cnt',
|
||||
'coop',
|
||||
'ecn',
|
||||
'esp',
|
||||
'far',
|
||||
'fm',
|
||||
'fnd',
|
||||
'fot',
|
||||
'fst',
|
||||
'g12',
|
||||
'ggf',
|
||||
'imb',
|
||||
'ind',
|
||||
'jor',
|
||||
'jus',
|
||||
'leg',
|
||||
'lel',
|
||||
'mat',
|
||||
'mp',
|
||||
'mus',
|
||||
'not',
|
||||
'ntr',
|
||||
'odo',
|
||||
'ppg',
|
||||
'pro',
|
||||
'psc',
|
||||
'qsl',
|
||||
'rec',
|
||||
'slg',
|
||||
'srv',
|
||||
'trd',
|
||||
'tur',
|
||||
'tv',
|
||||
'vet',
|
||||
'vlog',
|
||||
'wiki',
|
||||
'zlg'
|
||||
],
|
||||
id: [...STD_MIL, 'co', 'go', 'or', 'web', 'sch'],
|
||||
in: [...STD_MIL, 'co', 'gen', 'ind', 'firm', 'ernet', 'nic'],
|
||||
kr: [...STD_MIL, 'co', 'go', 'or', 'ac', 're'],
|
||||
nz: [
|
||||
...STD_MIL,
|
||||
'co',
|
||||
'gen',
|
||||
'geek',
|
||||
'kiwi',
|
||||
'maori',
|
||||
'school',
|
||||
'govt',
|
||||
'health',
|
||||
'iwi',
|
||||
'parliament'
|
||||
],
|
||||
sg: [...STD, 'per'],
|
||||
th: ['co', 'go', 'or', 'in', 'ac', 'mi', 'net'],
|
||||
|
||||
ae: ['co', 'net', 'org', 'gov', 'ac', 'sch'],
|
||||
hu: ['co', 'net', 'org', 'gov', 'edu'],
|
||||
il: ['co', 'net', 'org', 'gov', 'ac', 'muni'],
|
||||
jp: ['ac', 'ad', 'co', 'ed', 'go', 'gr', 'lg', 'ne', 'or'],
|
||||
ke: ['co', 'or', 'ne', 'go', 'ac', 'sc'],
|
||||
rs: ['co', 'net', 'org', 'gov', 'edu'],
|
||||
uk: ['co', 'org', 'net', 'ac', 'gov', 'mil', 'nhs', 'police', 'mod', 'ltd', 'plc', 'me', 'sch'],
|
||||
za: ['co', 'org', 'net', 'web', 'law', 'mil']
|
||||
};
|
||||
|
||||
const WILDCARD_BASES: Record<string, readonly string[]> = {
|
||||
br: ['nom', 'blog'],
|
||||
jp: [
|
||||
'kobe',
|
||||
'kyoto',
|
||||
'nagoya',
|
||||
'osaka',
|
||||
'sapporo',
|
||||
'sendai',
|
||||
'tokyo',
|
||||
'yokohama',
|
||||
'aichi',
|
||||
'akita',
|
||||
'aomori',
|
||||
'chiba',
|
||||
'ehime',
|
||||
'fukui',
|
||||
'fukuoka',
|
||||
'fukushima',
|
||||
'gifu',
|
||||
'gunma',
|
||||
'hiroshima',
|
||||
'hokkaido',
|
||||
'hyogo',
|
||||
'ibaraki',
|
||||
'ishikawa',
|
||||
'iwate',
|
||||
'kagawa',
|
||||
'kagoshima',
|
||||
'kanagawa',
|
||||
'kochi',
|
||||
'kumamoto',
|
||||
'mie',
|
||||
'miyagi',
|
||||
'miyazaki',
|
||||
'nagano',
|
||||
'nara',
|
||||
'niigata',
|
||||
'oita',
|
||||
'okayama',
|
||||
'okinawa',
|
||||
'saga',
|
||||
'saitama',
|
||||
'shiga',
|
||||
'shimane',
|
||||
'shizuoka',
|
||||
'tochigi',
|
||||
'tokushima',
|
||||
'tottori',
|
||||
'toyama',
|
||||
'wakayama',
|
||||
'yamagata',
|
||||
'yamaguchi',
|
||||
'yamanashi'
|
||||
]
|
||||
};
|
||||
|
||||
function buildSuffixSet(suffixes: Record<string, readonly string[]>): Set<string> {
|
||||
const set = new Set<string>();
|
||||
|
||||
for (const [tld, parts] of Object.entries(suffixes)) {
|
||||
for (const part of parts) {
|
||||
set.add(`${part}.${tld}`);
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
export const TWO_PART_PUBLIC_SUFFIXES = buildSuffixSet(ccTLD_PREFIXES);
|
||||
export const WILDCARD_PUBLIC_SUFFIXES = buildSuffixSet(WILDCARD_BASES);
|
||||
@@ -0,0 +1 @@
|
||||
export const DEFAULT_MOBILE_BREAKPOINT = 768;
|
||||
Reference in New Issue
Block a user