ui: Constants refactor (#26908)

* refactor: Constants

* refactor: Constants/Enums cleanup

* refactor: Constant objects instead of multiple single value constants

* refactor: Cleanup constants
This commit is contained in:
Aleksander Grygier
2026-08-13 06:53:56 +02:00
committed by GitHub
parent 8e7f22b67e
commit e21152dc96
209 changed files with 1101 additions and 1137 deletions
+12 -9
View File
@@ -5,10 +5,8 @@ import {
API_CHAT,
API_SLOTS,
API_STREAM,
ATTACHMENT_LABEL_MCP_PROMPT,
ATTACHMENT_LABEL_MCP_RESOURCE,
ATTACHMENT_LABEL_PDF_FILE,
CONTROL_ACTION,
HEADERS,
LEGACY_AGENTIC_REGEX,
REASONING_EFFORT_TOKENS,
SETTINGS_KEYS,
@@ -19,6 +17,7 @@ import {
STREAM_VISIBILITY_KICK_MS
} from '$lib/constants';
import {
AttachmentLabel,
AttachmentType,
ContentPartType,
MessageRole,
@@ -341,7 +340,7 @@ export class ChatService {
// server side replay buffer and powers discoverActiveStream on tab reopen. with an explicit
// model the ::model suffix keeps the per model session distinct
if (stream && conversationId) {
headers['X-Conversation-Id'] = streamIdentity(conversationId, options.model);
headers[HEADERS.X_CONVERSATION_ID_HEADER] = streamIdentity(conversationId, options.model);
// persist the pending stream before the fetch: a reload during the model load or
// the prompt processing must still find its way back to the session once it exists
ChatService.saveStreamState(conversationId, 0, options.model ?? null);
@@ -1291,7 +1290,7 @@ export class ChatService {
for (const textFile of textFiles) {
contentParts.push({
text: formatAttachmentText('File', textFile.name, textFile.content),
text: formatAttachmentText(AttachmentLabel.FILE, textFile.name, textFile.content),
type: ContentPartType.TEXT
});
}
@@ -1304,7 +1303,11 @@ export class ChatService {
for (const legacyContextFile of legacyContextFiles) {
contentParts.push({
text: formatAttachmentText('File', legacyContextFile.name, legacyContextFile.content),
text: formatAttachmentText(
AttachmentLabel.FILE,
legacyContextFile.name,
legacyContextFile.content
),
type: ContentPartType.TEXT
});
}
@@ -1382,7 +1385,7 @@ export class ChatService {
}
} else {
contentParts.push({
text: formatAttachmentText(ATTACHMENT_LABEL_PDF_FILE, pdfFile.name, pdfFile.content),
text: formatAttachmentText(AttachmentLabel.PDF_FILE, pdfFile.name, pdfFile.content),
type: ContentPartType.TEXT
});
}
@@ -1396,7 +1399,7 @@ export class ChatService {
for (const mcpPrompt of mcpPrompts) {
contentParts.push({
text: formatAttachmentText(
ATTACHMENT_LABEL_MCP_PROMPT,
AttachmentLabel.MCP_PROMPT,
mcpPrompt.name,
mcpPrompt.content,
mcpPrompt.serverName
@@ -1413,7 +1416,7 @@ export class ChatService {
for (const mcpResource of mcpResources) {
contentParts.push({
text: formatAttachmentText(
ATTACHMENT_LABEL_MCP_RESOURCE,
AttachmentLabel.MCP_RESOURCE,
mcpResource.name,
mcpResource.content,
mcpResource.serverName
+1 -1
View File
@@ -276,7 +276,7 @@ export { MCPService } from './mcp.service';
* - **toolsStore**: Exposes the tool definition when the sandbox is enabled
* - **agenticStore**: Dispatches ToolSource.FRONTEND calls here
*
* @see buildSandboxToolDefinition in constants/sandbox.ts - tool schema sent to the LLM
* @see buildSandboxToolDefinition in utils/sandbox-tool - tool schema sent to the LLM
* @see agenticStore in stores/agentic.svelte.ts - tool dispatch
*/
export { SandboxService } from './sandbox.service';
+7 -7
View File
@@ -13,12 +13,12 @@ import type {
Tool
} from '@modelcontextprotocol/sdk/types.js';
import {
CORS_PROXY,
CORS_PROXY_ENDPOINT,
CORS_PROXY_HEADER_PREFIX,
DEFAULT_CLIENT_VERSION,
DEFAULT_IMAGE_MIME_TYPE,
DEFAULT_MCP_CONFIG,
MCP_PARTIAL_REDACT_HEADERS
HEADERS
} from '$lib/constants';
import {
MCPConnectionPhase,
@@ -120,7 +120,7 @@ export class MCPService {
const details: DiagnosticRequestDetails = {
body: summarizeRequestBody(body),
credentials: init?.credentials ?? baseInit.credentials,
headers: sanitizeHeaders(requestHeaders, extraRedactedHeaders, MCP_PARTIAL_REDACT_HEADERS),
headers: sanitizeHeaders(requestHeaders, extraRedactedHeaders, HEADERS.PARTIAL_REDACT),
method: getRequestMethod(input, init, baseInit).toUpperCase(),
mode: init?.mode ?? baseInit.mode,
url: getRequestUrl(input)
@@ -141,8 +141,8 @@ export class MCPService {
) {
for (const [key, value] of new Headers(headers).entries()) {
const proxiedKey =
useProxy && !key.toLowerCase().startsWith(CORS_PROXY_HEADER_PREFIX)
? `${CORS_PROXY_HEADER_PREFIX}${key}`
useProxy && !key.toLowerCase().startsWith(CORS_PROXY.HEADER_PREFIX)
? `${CORS_PROXY.HEADER_PREFIX}${key}`
: key;
requestHeaders.set(proxiedKey, value);
@@ -361,7 +361,7 @@ export class MCPService {
{
response: {
durationMs,
headers: sanitizeHeaders(response.headers, undefined, MCP_PARTIAL_REDACT_HEADERS),
headers: sanitizeHeaders(response.headers, undefined, HEADERS.PARTIAL_REDACT),
status: response.status,
statusText: response.statusText,
url
@@ -751,7 +751,7 @@ export class MCPService {
headers: sanitizeHeaders(
serverConfig.headers,
Object.keys(serverConfig.headers ?? {}),
MCP_PARTIAL_REDACT_HEADERS
HEADERS.PARTIAL_REDACT
),
serverName,
transportType,
@@ -22,12 +22,13 @@ import {
DB_APP_NAME_DEPRECATED,
IDXDB_STORES,
IDXDB_TABLES,
LEGACY_AGENTIC_REGEX,
LEGACY_REASONING_TAGS,
NEW_TO_DEPRECATED_MAP,
SETTINGS_KEYS,
STORAGE_APP_NAME,
STORAGE_APP_NAME_DEPRECATED
} from '$lib/constants';
import { LEGACY_AGENTIC_REGEX, LEGACY_REASONING_TAGS } from '$lib/constants/agentic';
import { SETTINGS_KEYS } from '$lib/constants/settings-keys';
import { MessageRole } from '$lib/enums';
import Dexie from 'dexie';
+19 -31
View File
@@ -1,16 +1,4 @@
import {
API_MODELS,
MODEL_ACTIVATED_PARAMS_RE,
MODEL_CUSTOM_QUANTIZATION_PREFIX_RE,
MODEL_ID_NOT_FOUND,
MODEL_ID_ORG_SEPARATOR,
MODEL_ID_QUANTIZATION_SEPARATOR,
MODEL_ID_SEGMENT_SEPARATOR,
MODEL_IGNORED_SEGMENTS,
MODEL_PARAMS_RE,
MODEL_QUANTIZATION_SEGMENT_RE,
MODEL_WEIGHT_EXTENSION_RE
} from '$lib/constants';
import { API_MODELS, MODEL_ID } from '$lib/constants';
import { ServerModelStatus } from '$lib/enums';
import type { ParsedModelId } from '$lib/types/models';
import { apiFetch, apiPost, normalizeModelName } from '$lib/utils';
@@ -142,13 +130,13 @@ export class ModelsService {
};
// strip directory path and weight extension so a bare `-m /path/file.gguf`
// parses like a clean repo id; the HF `org/model` form is preserved
const source = normalizeModelName(modelId).replace(MODEL_WEIGHT_EXTENSION_RE, '');
const source = normalizeModelName(modelId).replace(MODEL_ID.WEIGHT_EXTENSION_RE, '');
// 1. Extract colon-separated quantization (e.g. `model:Q4_K_M`)
const colonIdx = source.indexOf(MODEL_ID_QUANTIZATION_SEPARATOR);
const colonIdx = source.indexOf(MODEL_ID.QUANTIZATION_SEPARATOR);
let modelPath: string;
if (colonIdx !== MODEL_ID_NOT_FOUND) {
if (colonIdx !== MODEL_ID.NOT_FOUND) {
result.quantization = source.slice(colonIdx + 1) || null;
modelPath = source.slice(0, colonIdx);
} else {
@@ -156,11 +144,11 @@ export class ModelsService {
}
// 2. Extract org name (e.g. `org/model` -> org = "org")
const slashIdx = modelPath.indexOf(MODEL_ID_ORG_SEPARATOR);
const slashIdx = modelPath.indexOf(MODEL_ID.ORG_SEPARATOR);
let modelStr: string;
if (slashIdx !== MODEL_ID_NOT_FOUND) {
if (slashIdx !== MODEL_ID.NOT_FOUND) {
result.orgName = modelPath.slice(0, slashIdx);
modelStr = modelPath.slice(slashIdx + 1);
} else {
@@ -170,16 +158,16 @@ export class ModelsService {
// 3. Handle dot-separated quantization (e.g. `model-name.Q4_K_M`)
const dotIdx = modelStr.lastIndexOf('.');
if (dotIdx !== MODEL_ID_NOT_FOUND && !result.quantization) {
if (dotIdx !== MODEL_ID.NOT_FOUND && !result.quantization) {
const afterDot = modelStr.slice(dotIdx + 1);
if (MODEL_QUANTIZATION_SEGMENT_RE.test(afterDot)) {
if (MODEL_ID.QUANTIZATION_SEGMENT_RE.test(afterDot)) {
result.quantization = afterDot;
modelStr = modelStr.slice(0, dotIdx);
}
}
const segments = modelStr.split(MODEL_ID_SEGMENT_SEPARATOR);
const segments = modelStr.split(MODEL_ID.SEGMENT_SEPARATOR);
// 4. Detect trailing quantization from dash-separated segments
// Handle UD-prefixed quantization (e.g. `UD-Q8_K_XL`) and
@@ -188,8 +176,8 @@ export class ModelsService {
const last = segments[segments.length - 1];
const secondLast = segments.length > 2 ? segments[segments.length - 2] : null;
if (MODEL_QUANTIZATION_SEGMENT_RE.test(last)) {
if (secondLast && MODEL_CUSTOM_QUANTIZATION_PREFIX_RE.test(secondLast)) {
if (MODEL_ID.QUANTIZATION_SEGMENT_RE.test(last)) {
if (secondLast && MODEL_ID.CUSTOM_QUANTIZATION_PREFIX_RE.test(secondLast)) {
result.quantization = `${secondLast}-${last}`;
segments.splice(segments.length - 2, 2);
} else {
@@ -200,33 +188,33 @@ export class ModelsService {
}
// 5. Find params and activated params
let paramsIdx = MODEL_ID_NOT_FOUND;
let activatedParamsIdx = MODEL_ID_NOT_FOUND;
let paramsIdx = MODEL_ID.NOT_FOUND;
let activatedParamsIdx = MODEL_ID.NOT_FOUND;
for (let i = 0; i < segments.length; i++) {
const seg = segments[i];
if (paramsIdx === MODEL_ID_NOT_FOUND && MODEL_PARAMS_RE.test(seg)) {
if (paramsIdx === MODEL_ID.NOT_FOUND && MODEL_ID.PARAMS_RE.test(seg)) {
paramsIdx = i;
result.params = seg.toUpperCase();
} else if (paramsIdx !== MODEL_ID_NOT_FOUND && MODEL_ACTIVATED_PARAMS_RE.test(seg)) {
} else if (paramsIdx !== MODEL_ID.NOT_FOUND && MODEL_ID.ACTIVATED_PARAMS_RE.test(seg)) {
activatedParamsIdx = i;
result.activatedParams = seg.toUpperCase();
}
}
// 6. Model name = segments before params; tags = remaining segments after params
const pivotIdx = paramsIdx !== MODEL_ID_NOT_FOUND ? paramsIdx : segments.length;
const pivotIdx = paramsIdx !== MODEL_ID.NOT_FOUND ? paramsIdx : segments.length;
result.modelName = segments.slice(0, pivotIdx).join(MODEL_ID_SEGMENT_SEPARATOR) || null;
result.modelName = segments.slice(0, pivotIdx).join(MODEL_ID.SEGMENT_SEPARATOR) || null;
if (paramsIdx !== MODEL_ID_NOT_FOUND) {
if (paramsIdx !== MODEL_ID.NOT_FOUND) {
result.tags = segments.slice(paramsIdx + 1).filter((_, relIdx) => {
const absIdx = paramsIdx + 1 + relIdx;
if (absIdx === activatedParamsIdx) return false;
return !MODEL_IGNORED_SEGMENTS.has(segments[absIdx].toUpperCase());
return !MODEL_ID.IGNORED_SEGMENTS.has(segments[absIdx].toUpperCase());
});
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { ROUTES } from '$lib/constants/routes';
import { ROUTES } from '$lib/constants';
export class RouterService {
static chat(id: string): string {
+5 -5
View File
@@ -1,5 +1,5 @@
import { base } from '$app/paths';
import { API_TOOLS, X_RESP_TYPE_HEADER, X_TOOL_CWD_HEADER } from '$lib/constants';
import { API_TOOLS, HEADERS } from '$lib/constants';
import { ToolResponseField } from '$lib/enums';
import type { ServerBuiltinToolInfo, ToolExecutionResult } from '$lib/types';
import { apiFetch } from '$lib/utils';
@@ -31,7 +31,7 @@ export class ToolsService {
): Promise<ToolExecutionResult> {
const result = await apiFetch<Record<string, unknown>>(API_TOOLS.EXECUTE, {
body: JSON.stringify({ params, tool: toolName }),
headers: cwd ? { [X_TOOL_CWD_HEADER]: cwd } : undefined,
headers: cwd ? { [HEADERS.X_TOOL_CWD_HEADER]: cwd } : undefined,
method: 'POST',
signal
});
@@ -64,9 +64,9 @@ export class ToolsService {
): Promise<Record<string, unknown>> {
const headers: Record<string, string> = {};
if (cwd) headers[X_TOOL_CWD_HEADER] = cwd;
if (cwd) headers[HEADERS.X_TOOL_CWD_HEADER] = cwd;
if (respType) headers[X_RESP_TYPE_HEADER] = respType;
if (respType) headers[HEADERS.X_RESP_TYPE_HEADER] = respType;
return apiFetch<Record<string, unknown>>(API_TOOLS.EXECUTE, {
body: JSON.stringify({ params, tool: toolName }),
@@ -99,7 +99,7 @@ export class ToolsService {
): AsyncGenerator<ToolStreamEvent> {
const headers = getJsonHeaders();
if (cwd) headers[X_TOOL_CWD_HEADER] = cwd;
if (cwd) headers[HEADERS.X_TOOL_CWD_HEADER] = cwd;
const response = await fetch(`${base}${API_TOOLS.EXECUTE}`, {
body: JSON.stringify({ params, stream: true, tool: toolName }),