refactor: Enums

This commit is contained in:
Aleksander Grygier 2026-01-25 13:37:08 +01:00
parent 7f5284d597
commit ee9efae203
20 changed files with 268 additions and 134 deletions

View File

@ -919,11 +919,11 @@ export class MCPClient {
async runHealthCheck(server: HealthCheckParams): Promise<void> {
const trimmedUrl = server.url.trim();
const logs: MCPConnectionLog[] = [];
let currentPhase: MCPConnectionPhase = MCPConnectionPhase.Idle;
let currentPhase: MCPConnectionPhase = MCPConnectionPhase.IDLE;
if (!trimmedUrl) {
mcpStore.updateHealthCheck(server.id, {
status: HealthCheckStatus.Error,
status: HealthCheckStatus.ERROR,
message: 'Please enter a server URL first.',
logs: []
});
@ -932,8 +932,8 @@ export class MCPClient {
// Initial connecting state
mcpStore.updateHealthCheck(server.id, {
status: HealthCheckStatus.Connecting,
phase: MCPConnectionPhase.TransportCreating,
status: HealthCheckStatus.CONNECTING,
phase: MCPConnectionPhase.TRANSPORT_CREATING,
logs: []
});
@ -957,7 +957,7 @@ export class MCPClient {
currentPhase = phase;
logs.push(log);
mcpStore.updateHealthCheck(server.id, {
status: HealthCheckStatus.Connecting,
status: HealthCheckStatus.CONNECTING,
phase,
logs: [...logs]
});
@ -976,7 +976,7 @@ export class MCPClient {
);
mcpStore.updateHealthCheck(server.id, {
status: HealthCheckStatus.Success,
status: HealthCheckStatus.SUCCESS,
tools,
serverInfo: connection.serverInfo,
capabilities,
@ -992,12 +992,12 @@ export class MCPClient {
const message = error instanceof Error ? error.message : 'Unknown error occurred';
logs.push({
timestamp: new Date(),
phase: MCPConnectionPhase.Error,
phase: MCPConnectionPhase.ERROR,
message: `Connection failed: ${message}`,
level: MCPLogLevel.Error
level: MCPLogLevel.ERROR
});
mcpStore.updateHealthCheck(server.id, {
status: HealthCheckStatus.Error,
status: HealthCheckStatus.ERROR,
message,
phase: currentPhase,
logs

View File

@ -1,6 +1,7 @@
<script lang="ts">
import { ChatMessageMcpPromptContent, RemoveButton } from '$lib/components/app';
import type { DatabaseMessageExtraMcpPrompt } from '$lib/types';
import { McpPromptVariant } from '$lib/enums';
interface Props {
class?: string;
@ -22,7 +23,12 @@
</script>
<div class="group relative {className}">
<ChatMessageMcpPromptContent {prompt} variant="attachment" {isLoading} {loadError} />
<ChatMessageMcpPromptContent
{prompt}
variant={McpPromptVariant.ATTACHMENT}
{isLoading}
{loadError}
/>
{#if !readonly && onRemove}
<div

View File

@ -67,7 +67,10 @@
}
type ReasoningSegment = {
type: 'text' | 'reasoning' | 'reasoning_pending';
type:
| AgenticSectionType.TEXT
| AgenticSectionType.REASONING
| AgenticSectionType.REASONING_PENDING;
content: string;
};
@ -90,7 +93,7 @@
if (startIndex === -1) {
const remainingText = rawContent.slice(cursor);
if (remainingText) {
segments.push({ type: 'text', content: remainingText });
segments.push({ type: AgenticSectionType.TEXT, content: remainingText });
}
break;
}
@ -98,7 +101,7 @@
if (startIndex > cursor) {
const textBefore = rawContent.slice(cursor, startIndex);
if (textBefore) {
segments.push({ type: 'text', content: textBefore });
segments.push({ type: AgenticSectionType.TEXT, content: textBefore });
}
}
@ -108,14 +111,14 @@
if (endIndex === -1) {
const pendingContent = rawContent.slice(contentStart);
segments.push({
type: 'reasoning_pending',
type: AgenticSectionType.REASONING_PENDING,
content: stripPartialMarker(pendingContent)
});
break;
}
const reasoningContent = rawContent.slice(contentStart, endIndex);
segments.push({ type: 'reasoning', content: reasoningContent });
segments.push({ type: AgenticSectionType.REASONING, content: reasoningContent });
cursor = endIndex + REASONING_TAGS.END.length;
}

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { ChatMessageActions, ChatMessageMcpPromptContent } from '$lib/components/app';
import { MessageRole } from '$lib/enums';
import { MessageRole, McpPromptVariant } from '$lib/enums';
import type { DatabaseMessageExtraMcpPrompt } from '$lib/types';
interface Props {
@ -44,7 +44,11 @@
class="group flex flex-col items-end gap-3 md:gap-2 {className}"
role="group"
>
<ChatMessageMcpPromptContent prompt={mcpPrompt} variant="message" class="w-full max-w-[80%]" />
<ChatMessageMcpPromptContent
prompt={mcpPrompt}
variant={McpPromptVariant.MESSAGE}
class="w-full max-w-[80%]"
/>
{#if message.timestamp}
<div class="max-w-[80%]">

View File

@ -4,6 +4,7 @@
import { getFaviconUrl } from '$lib/utils';
import { mcpStore } from '$lib/stores/mcp.svelte';
import { SvelteMap } from 'svelte/reactivity';
import { McpPromptVariant } from '$lib/enums';
interface ContentPart {
text: string;
@ -13,7 +14,7 @@
interface Props {
class?: string;
prompt: DatabaseMessageExtraMcpPrompt;
variant?: 'message' | 'attachment';
variant?: McpPromptVariant;
isLoading?: boolean;
loadError?: string;
}
@ -21,7 +22,7 @@
let {
class: className = '',
prompt,
variant = 'message',
variant = McpPromptVariant.MESSAGE,
isLoading = false,
loadError
}: Props = $props();
@ -99,7 +100,7 @@
});
let showArgBadges = $derived(hasArguments && !isLoading && !loadError);
let isAttachment = $derived(variant === 'attachment');
let isAttachment = $derived(variant === McpPromptVariant.ATTACHMENT);
let textSizeClass = $derived(isAttachment ? 'text-sm' : 'text-md');
let maxHeightStyle = $derived(
isAttachment

View File

@ -20,7 +20,7 @@
let healthyEnabledMcpServers = $derived(
enabledMcpServersForChat.filter((s) => {
const healthState = mcpStore.getHealthCheckState(s.id);
return healthState.status !== HealthCheckStatus.Error;
return healthState.status !== HealthCheckStatus.ERROR;
})
);
let hasEnabledMcpServers = $derived(enabledMcpServersForChat.length > 0);

View File

@ -27,41 +27,41 @@
let healthState = $derived<HealthCheckState>(mcpStore.getHealthCheckState(server.id));
let displayName = $derived(mcpStore.getServerLabel(server));
let isIdle = $derived(healthState.status === HealthCheckStatus.Idle);
let isHealthChecking = $derived(healthState.status === HealthCheckStatus.Connecting);
let isConnected = $derived(healthState.status === HealthCheckStatus.Success);
let isError = $derived(healthState.status === HealthCheckStatus.Error);
let isIdle = $derived(healthState.status === HealthCheckStatus.IDLE);
let isHealthChecking = $derived(healthState.status === HealthCheckStatus.CONNECTING);
let isConnected = $derived(healthState.status === HealthCheckStatus.SUCCESS);
let isError = $derived(healthState.status === HealthCheckStatus.ERROR);
let showSkeleton = $derived(isIdle || isHealthChecking);
let errorMessage = $derived(
healthState.status === HealthCheckStatus.Error ? healthState.message : undefined
healthState.status === HealthCheckStatus.ERROR ? healthState.message : undefined
);
let tools = $derived(healthState.status === HealthCheckStatus.Success ? healthState.tools : []);
let tools = $derived(healthState.status === HealthCheckStatus.SUCCESS ? healthState.tools : []);
let connectionLogs = $derived(
healthState.status === HealthCheckStatus.Connecting ||
healthState.status === HealthCheckStatus.Success ||
healthState.status === HealthCheckStatus.Error
healthState.status === HealthCheckStatus.CONNECTING ||
healthState.status === HealthCheckStatus.SUCCESS ||
healthState.status === HealthCheckStatus.ERROR
? healthState.logs
: []
);
let serverInfo = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.serverInfo : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.serverInfo : undefined
);
let capabilities = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.capabilities : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.capabilities : undefined
);
let transportType = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.transportType : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.transportType : undefined
);
let protocolVersion = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.protocolVersion : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.protocolVersion : undefined
);
let connectionTimeMs = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.connectionTimeMs : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.connectionTimeMs : undefined
);
let instructions = $derived(
healthState.status === HealthCheckStatus.Success ? healthState.instructions : undefined
healthState.status === HealthCheckStatus.SUCCESS ? healthState.instructions : undefined
);
let isEditing = $state(!server.url.trim());

View File

@ -27,14 +27,14 @@
}: Props = $props();
const transportLabels: Record<MCPTransportType, string> = {
[MCPTransportType.Websocket]: 'WebSocket',
[MCPTransportType.StreamableHttp]: 'HTTP',
[MCPTransportType.WEBSOCKET]: 'WebSocket',
[MCPTransportType.STREAMABLE_HTTP]: 'HTTP',
[MCPTransportType.SSE]: 'SSE'
};
const transportIcons: Record<MCPTransportType, typeof Cable> = {
[MCPTransportType.Websocket]: Zap,
[MCPTransportType.StreamableHttp]: Globe,
[MCPTransportType.WEBSOCKET]: Zap,
[MCPTransportType.STREAMABLE_HTTP]: Globe,
[MCPTransportType.SSE]: Radio
};
</script>

View File

@ -26,3 +26,7 @@ export { MCPConnectionPhase, MCPLogLevel, MCPTransportType, HealthCheckStatus }
export { ModelModality } from './model';
export { ServerRole, ServerModelStatus } from './server';
export { ParameterSource, SyncableParameterType } from './settings';
export { ColorMode, McpPromptVariant, UrlPrefix } from './ui';

View File

@ -2,32 +2,32 @@
* Connection lifecycle phases for MCP protocol
*/
export enum MCPConnectionPhase {
Idle = 'idle',
TransportCreating = 'transport_creating',
TransportReady = 'transport_ready',
Initializing = 'initializing',
CapabilitiesExchanged = 'capabilities_exchanged',
ListingTools = 'listing_tools',
Connected = 'connected',
Error = 'error',
Disconnected = 'disconnected'
IDLE = 'idle',
TRANSPORT_CREATING = 'transport_creating',
TRANSPORT_READY = 'transport_ready',
INITIALIZING = 'initializing',
CAPABILITIES_EXCHANGED = 'capabilities_exchanged',
LISTING_TOOLS = 'listing_tools',
CONNECTED = 'connected',
ERROR = 'error',
DISCONNECTED = 'disconnected'
}
/**
* Log level for connection events
*/
export enum MCPLogLevel {
Info = 'info',
Warn = 'warn',
Error = 'error'
INFO = 'info',
WARN = 'warn',
ERROR = 'error'
}
/**
* Transport types for MCP connections
*/
export enum MCPTransportType {
Websocket = 'websocket',
StreamableHttp = 'streamable_http',
WEBSOCKET = 'websocket',
STREAMABLE_HTTP = 'streamable_http',
SSE = 'sse'
}
@ -35,8 +35,8 @@ export enum MCPTransportType {
* Health check status for MCP servers
*/
export enum HealthCheckStatus {
Idle = 'idle',
Connecting = 'connecting',
Success = 'success',
Error = 'error'
IDLE = 'idle',
CONNECTING = 'connecting',
SUCCESS = 'success',
ERROR = 'error'
}

View File

@ -0,0 +1,16 @@
/**
* Parameter source - indicates whether a parameter uses default or custom value
*/
export enum ParameterSource {
DEFAULT = 'default',
CUSTOM = 'custom'
}
/**
* Syncable parameter type - data types for parameters that can be synced with server
*/
export enum SyncableParameterType {
NUMBER = 'number',
STRING = 'string',
BOOLEAN = 'boolean'
}

View File

@ -3,3 +3,22 @@ export enum ColorMode {
DARK = 'dark',
SYSTEM = 'system'
}
/**
* MCP prompt display variant
*/
export enum McpPromptVariant {
MESSAGE = 'message',
ATTACHMENT = 'attachment'
}
/**
* URL prefixes for protocol detection
*/
export enum UrlPrefix {
DATA = 'data:',
HTTP = 'http://',
HTTPS = 'https://',
WEBSOCKET = 'ws://',
WEBSOCKET_SECURE = 'wss://'
}

View File

@ -1,7 +1,7 @@
import type { Root as HastRoot } from 'hast';
import { visit } from 'unist-util-visit';
import type { DatabaseMessage, DatabaseMessageExtraImageFile } from '$lib/types/database';
import { AttachmentType } from '$lib/enums';
import { AttachmentType, UrlPrefix } from '$lib/enums';
/**
* Rehype plugin to resolve attachment image sources.
@ -14,7 +14,7 @@ export function rehypeResolveAttachmentImages(options: { message?: DatabaseMessa
const src = String(node.properties.src);
// Skip data URLs and external URLs
if (src.startsWith('data:') || src.startsWith('http')) {
if (src.startsWith(UrlPrefix.DATA) || src.startsWith(UrlPrefix.HTTP)) {
return;
}

View File

@ -59,7 +59,7 @@ export class MCPService {
private static createLog(
phase: MCPConnectionPhase,
message: string,
level: MCPLogLevel = MCPLogLevel.Info,
level: MCPLogLevel = MCPLogLevel.INFO,
details?: unknown
): MCPConnectionLog {
return {
@ -100,7 +100,7 @@ export class MCPService {
return {
transport: new WebSocketClientTransport(url),
type: MCPTransportType.Websocket
type: MCPTransportType.WEBSOCKET
};
}
@ -112,7 +112,7 @@ export class MCPService {
requestInit,
sessionId: config.sessionId
}),
type: MCPTransportType.StreamableHttp
type: MCPTransportType.STREAMABLE_HTTP
};
} catch (httpError) {
console.warn(`[MCPService] StreamableHTTP failed, trying SSE transport...`, httpError);
@ -169,9 +169,9 @@ export class MCPService {
// Phase: Creating transport
onPhase?.(
MCPConnectionPhase.TransportCreating,
MCPConnectionPhase.TRANSPORT_CREATING,
this.createLog(
MCPConnectionPhase.TransportCreating,
MCPConnectionPhase.TRANSPORT_CREATING,
`Creating transport for ${serverConfig.url}`
)
);
@ -181,8 +181,8 @@ export class MCPService {
// Phase: Transport ready
onPhase?.(
MCPConnectionPhase.TransportReady,
this.createLog(MCPConnectionPhase.TransportReady, `Transport ready (${transportType})`),
MCPConnectionPhase.TRANSPORT_READY,
this.createLog(MCPConnectionPhase.TRANSPORT_READY, `Transport ready (${transportType})`),
{ transportType }
);
@ -199,8 +199,8 @@ export class MCPService {
// Phase: Initializing
onPhase?.(
MCPConnectionPhase.Initializing,
this.createLog(MCPConnectionPhase.Initializing, 'Sending initialize request...')
MCPConnectionPhase.INITIALIZING,
this.createLog(MCPConnectionPhase.INITIALIZING, 'Sending initialize request...')
);
console.log(`[MCPService][${serverName}] Connecting to server...`);
@ -213,11 +213,11 @@ export class MCPService {
// Phase: Capabilities exchanged
onPhase?.(
MCPConnectionPhase.CapabilitiesExchanged,
MCPConnectionPhase.CAPABILITIES_EXCHANGED,
this.createLog(
MCPConnectionPhase.CapabilitiesExchanged,
MCPConnectionPhase.CAPABILITIES_EXCHANGED,
'Capabilities exchanged successfully',
MCPLogLevel.Info,
MCPLogLevel.INFO,
{
serverCapabilities,
serverInfo
@ -233,8 +233,8 @@ export class MCPService {
// Phase: Listing tools
onPhase?.(
MCPConnectionPhase.ListingTools,
this.createLog(MCPConnectionPhase.ListingTools, 'Listing available tools...')
MCPConnectionPhase.LISTING_TOOLS,
this.createLog(MCPConnectionPhase.LISTING_TOOLS, 'Listing available tools...')
);
console.log(`[MCPService][${serverName}] Connected, listing tools...`);
@ -251,9 +251,9 @@ export class MCPService {
// Phase: Connected
onPhase?.(
MCPConnectionPhase.Connected,
MCPConnectionPhase.CONNECTED,
this.createLog(
MCPConnectionPhase.Connected,
MCPConnectionPhase.CONNECTED,
`Connection established with ${tools.length} tools (${connectionTimeMs}ms)`
)
);

View File

@ -13,77 +13,157 @@
*/
import { normalizeFloatingPoint } from '$lib/utils';
import type {
SyncableParameter,
ParameterRecord,
ParameterInfo,
ParameterValue,
ParameterSource
} from '$lib/types';
import type { SyncableParameter, ParameterRecord, ParameterInfo, ParameterValue } from '$lib/types';
import { SyncableParameterType, ParameterSource } from '$lib/enums';
/**
* Mapping of webui setting keys to server parameter keys
* Only parameters that should be synced from server are included
*/
export const SYNCABLE_PARAMETERS: SyncableParameter[] = [
{ key: 'temperature', serverKey: 'temperature', type: 'number', canSync: true },
{ key: 'top_k', serverKey: 'top_k', type: 'number', canSync: true },
{ key: 'top_p', serverKey: 'top_p', type: 'number', canSync: true },
{ key: 'min_p', serverKey: 'min_p', type: 'number', canSync: true },
{ key: 'dynatemp_range', serverKey: 'dynatemp_range', type: 'number', canSync: true },
{ key: 'dynatemp_exponent', serverKey: 'dynatemp_exponent', type: 'number', canSync: true },
{ key: 'xtc_probability', serverKey: 'xtc_probability', type: 'number', canSync: true },
{ key: 'xtc_threshold', serverKey: 'xtc_threshold', type: 'number', canSync: true },
{ key: 'typ_p', serverKey: 'typ_p', type: 'number', canSync: true },
{ key: 'repeat_last_n', serverKey: 'repeat_last_n', type: 'number', canSync: true },
{ key: 'repeat_penalty', serverKey: 'repeat_penalty', type: 'number', canSync: true },
{ key: 'presence_penalty', serverKey: 'presence_penalty', type: 'number', canSync: true },
{ key: 'frequency_penalty', serverKey: 'frequency_penalty', type: 'number', canSync: true },
{ key: 'dry_multiplier', serverKey: 'dry_multiplier', type: 'number', canSync: true },
{ key: 'dry_base', serverKey: 'dry_base', type: 'number', canSync: true },
{ key: 'dry_allowed_length', serverKey: 'dry_allowed_length', type: 'number', canSync: true },
{ key: 'dry_penalty_last_n', serverKey: 'dry_penalty_last_n', type: 'number', canSync: true },
{ key: 'max_tokens', serverKey: 'max_tokens', type: 'number', canSync: true },
{ key: 'samplers', serverKey: 'samplers', type: 'string', canSync: true },
{
key: 'temperature',
serverKey: 'temperature',
type: SyncableParameterType.NUMBER,
canSync: true
},
{ key: 'top_k', serverKey: 'top_k', type: SyncableParameterType.NUMBER, canSync: true },
{ key: 'top_p', serverKey: 'top_p', type: SyncableParameterType.NUMBER, canSync: true },
{ key: 'min_p', serverKey: 'min_p', type: SyncableParameterType.NUMBER, canSync: true },
{
key: 'dynatemp_range',
serverKey: 'dynatemp_range',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'dynatemp_exponent',
serverKey: 'dynatemp_exponent',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'xtc_probability',
serverKey: 'xtc_probability',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'xtc_threshold',
serverKey: 'xtc_threshold',
type: SyncableParameterType.NUMBER,
canSync: true
},
{ key: 'typ_p', serverKey: 'typ_p', type: SyncableParameterType.NUMBER, canSync: true },
{
key: 'repeat_last_n',
serverKey: 'repeat_last_n',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'repeat_penalty',
serverKey: 'repeat_penalty',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'presence_penalty',
serverKey: 'presence_penalty',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'frequency_penalty',
serverKey: 'frequency_penalty',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'dry_multiplier',
serverKey: 'dry_multiplier',
type: SyncableParameterType.NUMBER,
canSync: true
},
{ key: 'dry_base', serverKey: 'dry_base', type: SyncableParameterType.NUMBER, canSync: true },
{
key: 'dry_allowed_length',
serverKey: 'dry_allowed_length',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'dry_penalty_last_n',
serverKey: 'dry_penalty_last_n',
type: SyncableParameterType.NUMBER,
canSync: true
},
{ key: 'max_tokens', serverKey: 'max_tokens', type: SyncableParameterType.NUMBER, canSync: true },
{ key: 'samplers', serverKey: 'samplers', type: SyncableParameterType.STRING, canSync: true },
{
key: 'pasteLongTextToFileLen',
serverKey: 'pasteLongTextToFileLen',
type: 'number',
type: SyncableParameterType.NUMBER,
canSync: true
},
{
key: 'pdfAsImage',
serverKey: 'pdfAsImage',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{ key: 'pdfAsImage', serverKey: 'pdfAsImage', type: 'boolean', canSync: true },
{
key: 'showThoughtInProgress',
serverKey: 'showThoughtInProgress',
type: 'boolean',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{
key: 'keepStatsVisible',
serverKey: 'keepStatsVisible',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{
key: 'showMessageStats',
serverKey: 'showMessageStats',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{ key: 'keepStatsVisible', serverKey: 'keepStatsVisible', type: 'boolean', canSync: true },
{ key: 'showMessageStats', serverKey: 'showMessageStats', type: 'boolean', canSync: true },
{
key: 'askForTitleConfirmation',
serverKey: 'askForTitleConfirmation',
type: 'boolean',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{
key: 'disableAutoScroll',
serverKey: 'disableAutoScroll',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{ key: 'disableAutoScroll', serverKey: 'disableAutoScroll', type: 'boolean', canSync: true },
{
key: 'renderUserContentAsMarkdown',
serverKey: 'renderUserContentAsMarkdown',
type: 'boolean',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{
key: 'autoMicOnEmpty',
serverKey: 'autoMicOnEmpty',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{ key: 'autoMicOnEmpty', serverKey: 'autoMicOnEmpty', type: 'boolean', canSync: true },
{
key: 'pyInterpreterEnabled',
serverKey: 'pyInterpreterEnabled',
type: 'boolean',
type: SyncableParameterType.BOOLEAN,
canSync: true
},
{
key: 'enableContinueGeneration',
serverKey: 'enableContinueGeneration',
type: 'boolean',
type: SyncableParameterType.BOOLEAN,
canSync: true
}
];
@ -196,7 +276,7 @@ export class ParameterSyncService {
const isUserOverride = userOverrides.has(key);
// Simple logic: either using default (from props) or custom (user override)
const source: ParameterSource = isUserOverride ? 'custom' : 'default';
const source = isUserOverride ? ParameterSource.CUSTOM : ParameterSource.DEFAULT;
return {
value: currentValue,
@ -228,11 +308,11 @@ export class ParameterSyncService {
if (!param) return false;
switch (param.type) {
case 'number':
case SyncableParameterType.NUMBER:
return typeof value === 'number' && !isNaN(value);
case 'string':
case SyncableParameterType.STRING:
return typeof value === 'string';
case 'boolean':
case SyncableParameterType.BOOLEAN:
return typeof value === 'boolean';
default:
return false;

View File

@ -158,7 +158,7 @@ class MCPStore {
*/
getServerLabel(server: MCPServerSettingsEntry): string {
const healthState = this.getHealthCheckState(server.id);
if (healthState?.status === HealthCheckStatus.Success) {
if (healthState?.status === HealthCheckStatus.SUCCESS) {
return (
healthState.serverInfo?.title || healthState.serverInfo?.name || server.name || server.url
);
@ -175,7 +175,7 @@ class MCPStore {
return servers.some((s) => {
const state = this.getHealthCheckState(s.id);
return (
state.status === HealthCheckStatus.Idle || state.status === HealthCheckStatus.Connecting
state.status === HealthCheckStatus.IDLE || state.status === HealthCheckStatus.CONNECTING
);
});
}

View File

@ -76,7 +76,6 @@ export type {
SettingsFieldConfig,
SettingsChatServiceOptions,
SettingsConfigType,
ParameterSource,
ParameterValue,
ParameterRecord,
ParameterInfo,

View File

@ -136,20 +136,20 @@ export interface MCPConnection {
* Extended health check state with detailed connection info
*/
export type HealthCheckState =
| { status: import('$lib/enums/mcp').HealthCheckStatus.Idle }
| { status: import('$lib/enums/mcp').HealthCheckStatus.IDLE }
| {
status: import('$lib/enums/mcp').HealthCheckStatus.Connecting;
status: import('$lib/enums/mcp').HealthCheckStatus.CONNECTING;
phase: MCPConnectionPhase;
logs: MCPConnectionLog[];
}
| {
status: import('$lib/enums/mcp').HealthCheckStatus.Error;
status: import('$lib/enums/mcp').HealthCheckStatus.ERROR;
message: string;
phase?: MCPConnectionPhase;
logs: MCPConnectionLog[];
}
| {
status: import('$lib/enums/mcp').HealthCheckStatus.Success;
status: import('$lib/enums/mcp').HealthCheckStatus.SUCCESS;
tools: MCPToolInfo[];
serverInfo?: MCPServerInfo;
capabilities?: MCPCapabilitiesInfo;

View File

@ -2,6 +2,7 @@ import type { SETTING_CONFIG_DEFAULT } from '$lib/constants/settings-config';
import type { ChatMessagePromptProgress, ChatMessageTimings } from './chat';
import type { OpenAIToolDefinition } from './mcp';
import type { DatabaseMessageExtra } from './database';
import type { ParameterSource, SyncableParameterType } from '$lib/enums';
export type SettingsConfigValue = string | number | boolean;
@ -72,8 +73,8 @@ export type SettingsConfigType = typeof SETTING_CONFIG_DEFAULT & {
/**
* Parameter synchronization types for server defaults and user overrides
* Note: ParameterSource and SyncableParameterType enums are imported from '$lib/enums'
*/
export type ParameterSource = 'default' | 'custom';
export type ParameterValue = string | number | boolean;
export type ParameterRecord = Record<string, ParameterValue>;
@ -87,6 +88,6 @@ export interface ParameterInfo {
export interface SyncableParameter {
key: string;
serverKey: string;
type: 'number' | 'string' | 'boolean';
type: SyncableParameterType;
canSync: boolean;
}

View File

@ -1,5 +1,5 @@
import type { MCPServerSettingsEntry } from '$lib/types';
import { MCPTransportType, MCPLogLevel } from '$lib/enums';
import { MCPTransportType, MCPLogLevel, UrlPrefix } from '$lib/enums';
import { DEFAULT_MCP_CONFIG } from '$lib/constants/mcp';
import { Info, AlertTriangle, XCircle } from '@lucide/svelte';
import type { Component } from 'svelte';
@ -11,9 +11,10 @@ import type { Component } from 'svelte';
export function detectMcpTransportFromUrl(url: string): MCPTransportType {
const normalized = url.trim().toLowerCase();
return normalized.startsWith('ws://') || normalized.startsWith('wss://')
? MCPTransportType.Websocket
: MCPTransportType.StreamableHttp;
return normalized.startsWith(UrlPrefix.WEBSOCKET) ||
normalized.startsWith(UrlPrefix.WEBSOCKET_SECURE)
? MCPTransportType.WEBSOCKET
: MCPTransportType.STREAMABLE_HTTP;
}
/**
@ -70,9 +71,9 @@ export function parseMcpServerSettings(rawServers: unknown): MCPServerSettingsEn
*/
export function getMcpLogLevelIcon(level: MCPLogLevel): Component {
switch (level) {
case MCPLogLevel.Error:
case MCPLogLevel.ERROR:
return XCircle;
case MCPLogLevel.Warn:
case MCPLogLevel.WARN:
return AlertTriangle;
default:
return Info;
@ -87,9 +88,9 @@ export function getMcpLogLevelIcon(level: MCPLogLevel): Component {
*/
export function getMcpLogLevelClass(level: MCPLogLevel): string {
switch (level) {
case MCPLogLevel.Error:
case MCPLogLevel.ERROR:
return 'text-destructive';
case MCPLogLevel.Warn:
case MCPLogLevel.WARN:
return 'text-yellow-600 dark:text-yellow-500';
default:
return 'text-muted-foreground';