docs: Architecture documentation
This commit is contained in:
parent
db479523ec
commit
bc577266b9
|
|
@ -0,0 +1,254 @@
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph Routes["📍 Routes"]
|
||||||
|
R1["/ (+page.svelte)"]
|
||||||
|
R2["/chat/[id]"]
|
||||||
|
RL["+layout.svelte"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Components["🧩 Components"]
|
||||||
|
direction TB
|
||||||
|
subgraph LayoutComponents["Layout"]
|
||||||
|
C_Sidebar["ChatSidebar"]
|
||||||
|
C_Screen["ChatScreen"]
|
||||||
|
end
|
||||||
|
subgraph ChatComponents["Chat"]
|
||||||
|
C_Form["ChatForm"]
|
||||||
|
C_Messages["ChatMessages"]
|
||||||
|
C_Message["ChatMessage"]
|
||||||
|
end
|
||||||
|
subgraph ControlComponents["Controls"]
|
||||||
|
C_SelectorModel["SelectorModel"]
|
||||||
|
C_Settings["ChatSettings"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Stores["🗄️ Stores"]
|
||||||
|
direction TB
|
||||||
|
subgraph S1["chatStore"]
|
||||||
|
S1State["<b>State:</b><br/>isLoading, currentResponse<br/>errorDialogState<br/>activeProcessingState<br/>chatLoadingStates<br/>chatStreamingStates<br/>abortControllers<br/>processingStates<br/>activeConversationId<br/>isStreamingActive"]
|
||||||
|
S1LoadState["<b>Loading State:</b><br/>setChatLoading()<br/>isChatLoading()<br/>syncLoadingStateForChat()<br/>clearUIState()<br/>isChatLoadingPublic()<br/>getAllLoadingChats()<br/>getAllStreamingChats()"]
|
||||||
|
S1ProcState["<b>Processing State:</b><br/>setActiveProcessingConversation()<br/>getProcessingState()<br/>clearProcessingState()<br/>getActiveProcessingState()<br/>updateProcessingStateFromTimings()<br/>getCurrentProcessingStateSync()<br/>restoreProcessingStateFromMessages()"]
|
||||||
|
S1Stream["<b>Streaming:</b><br/>streamChatCompletion()<br/>startStreaming()<br/>stopStreaming()<br/>stopGeneration()<br/>isStreaming()"]
|
||||||
|
S1Error["<b>Error Handling:</b><br/>showErrorDialog()<br/>dismissErrorDialog()<br/>isAbortError()"]
|
||||||
|
S1Msg["<b>Message Operations:</b><br/>addMessage()<br/>sendMessage()<br/>updateMessage()<br/>deleteMessage()<br/>getDeletionInfo()"]
|
||||||
|
S1Regen["<b>Regeneration:</b><br/>regenerateMessage()<br/>regenerateMessageWithBranching()<br/>continueAssistantMessage()"]
|
||||||
|
S1Edit["<b>Editing:</b><br/>editAssistantMessage()<br/>editUserMessagePreserveResponses()<br/>editMessageWithBranching()"]
|
||||||
|
S1Utils["<b>Utilities:</b><br/>getApiOptions()<br/>parseTimingData()<br/>getOrCreateAbortController()<br/>getConversationModel()"]
|
||||||
|
end
|
||||||
|
subgraph S2["conversationsStore"]
|
||||||
|
S2State["<b>State:</b><br/>conversations<br/>activeConversation<br/>activeMessages<br/>usedModalities<br/>isInitialized<br/>titleUpdateConfirmationCallback"]
|
||||||
|
S2Modal["<b>Modalities:</b><br/>getModalitiesUpToMessage()<br/>calculateModalitiesFromMessages()"]
|
||||||
|
S2Lifecycle["<b>Lifecycle:</b><br/>initialize()<br/>loadConversations()<br/>clearActiveConversation()"]
|
||||||
|
S2ConvCRUD["<b>Conversation CRUD:</b><br/>createConversation()<br/>loadConversation()<br/>deleteConversation()<br/>updateConversationName()<br/>updateConversationTitleWithConfirmation()"]
|
||||||
|
S2MsgMgmt["<b>Message Management:</b><br/>refreshActiveMessages()<br/>addMessageToActive()<br/>updateMessageAtIndex()<br/>findMessageIndex()<br/>sliceActiveMessages()<br/>removeMessageAtIndex()<br/>getConversationMessages()"]
|
||||||
|
S2Nav["<b>Navigation:</b><br/>navigateToSibling()<br/>updateCurrentNode()<br/>updateConversationTimestamp()"]
|
||||||
|
S2Export["<b>Import/Export:</b><br/>downloadConversation()<br/>exportAllConversations()<br/>importConversations()<br/>triggerDownload()"]
|
||||||
|
S2Utils["<b>Utilities:</b><br/>setTitleUpdateConfirmationCallback()"]
|
||||||
|
end
|
||||||
|
subgraph S3["modelsStore"]
|
||||||
|
S3State["<b>State:</b><br/>models, routerModels<br/>selectedModelId<br/>selectedModelName<br/>loading, updating, error<br/>modelUsage<br/>modelLoadingStates<br/>modelPropsCache<br/>modelPropsFetching"]
|
||||||
|
S3Getters["<b>Computed Getters:</b><br/>selectedModel<br/>loadedModelIds<br/>loadingModelIds"]
|
||||||
|
S3Modal["<b>Modalities:</b><br/>getModelModalities()<br/>modelSupportsVision()<br/>modelSupportsAudio()<br/>getModelProps()<br/>updateModelModalities()"]
|
||||||
|
S3Status["<b>Status Queries:</b><br/>isModelLoaded()<br/>isModelOperationInProgress()<br/>getModelStatus()<br/>isModelPropsFetching()"]
|
||||||
|
S3Fetch["<b>Data Fetching:</b><br/>fetch()<br/>fetchRouterModels()<br/>fetchModelProps()<br/>fetchModalitiesForLoadedModels()"]
|
||||||
|
S3Select["<b>Model Selection:</b><br/>selectModelById()<br/>selectModelByName()<br/>clearSelection()<br/>findModelByName()<br/>findModelById()<br/>hasModel()"]
|
||||||
|
S3LoadUnload["<b>Loading/Unloading Models:</b><br/>loadModel()<br/>unloadModel()<br/>ensureModelLoaded()<br/>waitForModelStatus()<br/>pollForModelStatus()"]
|
||||||
|
S3Usage["<b>Usage Tracking:</b><br/>registerModelUsage()<br/>unregisterModelUsage()<br/>clearConversationUsage()<br/>getModelUsage()<br/>isModelInUse()"]
|
||||||
|
S3Utils["<b>Utilities:</b><br/>toDisplayName()<br/>clear()"]
|
||||||
|
end
|
||||||
|
subgraph S4["serverStore"]
|
||||||
|
S4State["<b>State:</b><br/>props<br/>loading, error<br/>role<br/>fetchPromise"]
|
||||||
|
S4Getters["<b>Getters:</b><br/>modelName<br/>supportedModalities<br/>supportsVision<br/>supportsAudio<br/>defaultParams<br/>contextSize<br/>slotsEndpointAvailable<br/>isRouterMode<br/>isModelMode"]
|
||||||
|
S4Data["<b>Data Handling:</b><br/>fetch()<br/>getErrorMessage()<br/>clear()"]
|
||||||
|
S4Utils["<b>Utilities:</b><br/>detectRole()"]
|
||||||
|
end
|
||||||
|
subgraph S5["settingsStore"]
|
||||||
|
S5State["<b>State:</b><br/>config<br/>theme<br/>isInitialized<br/>userOverrides"]
|
||||||
|
S5Lifecycle["<b>Lifecycle:</b><br/>initialize()<br/>loadConfig()<br/>saveConfig()<br/>loadTheme()<br/>saveTheme()"]
|
||||||
|
S5Update["<b>Config Updates:</b><br/>updateConfig()<br/>updateMultipleConfig()<br/>updateTheme()"]
|
||||||
|
S5Reset["<b>Reset:</b><br/>resetConfig()<br/>resetTheme()<br/>resetAll()<br/>resetParameterToServerDefault()"]
|
||||||
|
S5Sync["<b>Server Sync:</b><br/>syncWithServerDefaults()<br/>forceSyncWithServerDefaults()"]
|
||||||
|
S5Utils["<b>Utilities:</b><br/>getConfig()<br/>getAllConfig()<br/>getParameterInfo()<br/>getParameterDiff()<br/>getServerDefaults()<br/>clearAllUserOverrides()"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ReactiveExports["⚡ Reactive Exports"]
|
||||||
|
direction LR
|
||||||
|
subgraph ChatExports["chatStore"]
|
||||||
|
RE1["isLoading()"]
|
||||||
|
RE2["currentResponse()"]
|
||||||
|
RE3["errorDialog()"]
|
||||||
|
RE4["activeProcessingState()"]
|
||||||
|
RE5["isChatStreaming()"]
|
||||||
|
RE6["isChatLoading()"]
|
||||||
|
RE7["getChatStreaming()"]
|
||||||
|
RE8["getAllLoadingChats()"]
|
||||||
|
RE9["getAllStreamingChats()"]
|
||||||
|
end
|
||||||
|
subgraph ConvExports["conversationsStore"]
|
||||||
|
RE10["conversations()"]
|
||||||
|
RE11["activeConversation()"]
|
||||||
|
RE12["activeMessages()"]
|
||||||
|
RE13["isConversationsInitialized()"]
|
||||||
|
RE14["usedModalities()"]
|
||||||
|
end
|
||||||
|
subgraph ModelsExports["modelsStore"]
|
||||||
|
RE15["modelOptions()"]
|
||||||
|
RE16["routerModels()"]
|
||||||
|
RE17["modelsLoading()"]
|
||||||
|
RE18["modelsUpdating()"]
|
||||||
|
RE19["modelsError()"]
|
||||||
|
RE20["selectedModelId()"]
|
||||||
|
RE21["selectedModelName()"]
|
||||||
|
RE22["selectedModelOption()"]
|
||||||
|
RE23["loadedModelIds()"]
|
||||||
|
RE24["loadingModelIds()"]
|
||||||
|
end
|
||||||
|
subgraph ServerExports["serverStore"]
|
||||||
|
RE25["serverProps()"]
|
||||||
|
RE26["serverLoading()"]
|
||||||
|
RE27["serverError()"]
|
||||||
|
RE28["serverRole()"]
|
||||||
|
RE29["modelName()"]
|
||||||
|
RE30["supportedModalities()"]
|
||||||
|
RE31["supportsVision()"]
|
||||||
|
RE32["supportsAudio()"]
|
||||||
|
RE33["slotsEndpointAvailable()"]
|
||||||
|
RE34["defaultParams()"]
|
||||||
|
RE35["contextSize()"]
|
||||||
|
RE36["isRouterMode()"]
|
||||||
|
RE37["isModelMode()"]
|
||||||
|
end
|
||||||
|
subgraph SettingsExports["settingsStore"]
|
||||||
|
RE38["config()"]
|
||||||
|
RE39["theme()"]
|
||||||
|
RE40["isInitialized()"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Services["⚙️ Services"]
|
||||||
|
direction TB
|
||||||
|
subgraph SV1["ChatService"]
|
||||||
|
SV1Msg["<b>Messaging:</b><br/>sendMessage()"]
|
||||||
|
SV1Stream["<b>Streaming:</b><br/>handleStreamResponse()<br/>parseSSEChunk()"]
|
||||||
|
SV1Convert["<b>Conversion:</b><br/>convertMessageToChatData()<br/>convertExtraToApiFormat()"]
|
||||||
|
SV1Utils["<b>Utilities:</b><br/>extractReasoningContent()<br/>getServerProps()<br/>getModels()"]
|
||||||
|
end
|
||||||
|
subgraph SV2["ModelsService"]
|
||||||
|
SV2List["<b>Listing:</b><br/>list()<br/>listRouter()"]
|
||||||
|
SV2LoadUnload["<b>Load/Unload:</b><br/>load()<br/>unload()"]
|
||||||
|
SV2Status["<b>Status:</b><br/>getStatus()<br/>isModelLoaded()<br/>isModelLoading()"]
|
||||||
|
end
|
||||||
|
subgraph SV3["PropsService"]
|
||||||
|
SV3Fetch["<b>Fetching:</b><br/>fetch()<br/>fetchForModel()"]
|
||||||
|
end
|
||||||
|
subgraph SV4["DatabaseService"]
|
||||||
|
SV4Conv["<b>Conversations:</b><br/>createConversation()<br/>getConversation()<br/>getAllConversations()<br/>updateConversation()<br/>deleteConversation()"]
|
||||||
|
SV4Msg["<b>Messages:</b><br/>createMessageBranch()<br/>createRootMessage()<br/>getConversationMessages()<br/>updateMessage()<br/>deleteMessage()<br/>deleteMessageCascading()"]
|
||||||
|
SV4Node["<b>Navigation:</b><br/>updateCurrentNode()"]
|
||||||
|
SV4Import["<b>Import:</b><br/>importConversations()"]
|
||||||
|
end
|
||||||
|
subgraph SV5["ParameterSyncService"]
|
||||||
|
SV5Extract["<b>Extraction:</b><br/>extractServerDefaults()"]
|
||||||
|
SV5Merge["<b>Merging:</b><br/>mergeWithServerDefaults()"]
|
||||||
|
SV5Info["<b>Info:</b><br/>getParameterInfo()<br/>canSyncParameter()<br/>getSyncableParameterKeys()<br/>validateServerParameter()"]
|
||||||
|
SV5Diff["<b>Diff:</b><br/>createParameterDiff()"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Storage["💾 Storage"]
|
||||||
|
ST1["IndexedDB"]
|
||||||
|
ST2["conversations"]
|
||||||
|
ST3["messages"]
|
||||||
|
ST5["LocalStorage"]
|
||||||
|
ST6["config"]
|
||||||
|
ST7["userOverrides"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph APIs["🌐 llama-server API"]
|
||||||
|
API1["/v1/chat/completions"]
|
||||||
|
API2["/props<br/>/props?model="]
|
||||||
|
API3["/models<br/>/models/load<br/>/models/unload<br/>/models/status"]
|
||||||
|
API4["/v1/models"]
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Routes render Components
|
||||||
|
R1 --> C_Screen
|
||||||
|
R2 --> C_Screen
|
||||||
|
RL --> C_Sidebar
|
||||||
|
|
||||||
|
%% Component hierarchy
|
||||||
|
C_Screen --> C_Form & C_Messages & C_Settings
|
||||||
|
C_Messages --> C_Message
|
||||||
|
C_Message --> C_SelectorModel
|
||||||
|
C_Form --> C_SelectorModel
|
||||||
|
|
||||||
|
%% Components use Stores
|
||||||
|
C_Screen --> S1 & S2
|
||||||
|
C_Messages --> S2
|
||||||
|
C_Message --> S1 & S2 & S3
|
||||||
|
C_Form --> S1 & S3
|
||||||
|
C_Sidebar --> S2
|
||||||
|
C_SelectorModel --> S3 & S4
|
||||||
|
C_Settings --> S5
|
||||||
|
|
||||||
|
%% Stores export Reactive State
|
||||||
|
S1 -. exports .-> ChatExports
|
||||||
|
S2 -. exports .-> ConvExports
|
||||||
|
S3 -. exports .-> ModelsExports
|
||||||
|
S4 -. exports .-> ServerExports
|
||||||
|
S5 -. exports .-> SettingsExports
|
||||||
|
|
||||||
|
%% Stores use Services
|
||||||
|
S1 --> SV1 & SV4
|
||||||
|
S2 --> SV4
|
||||||
|
S3 --> SV2 & SV3
|
||||||
|
S4 --> SV3
|
||||||
|
S5 --> SV5
|
||||||
|
|
||||||
|
%% Services to Storage
|
||||||
|
SV4 --> ST1
|
||||||
|
ST1 --> ST2 & ST3
|
||||||
|
SV5 --> ST5
|
||||||
|
ST5 --> ST6 & ST7
|
||||||
|
|
||||||
|
%% Services to APIs
|
||||||
|
SV1 --> API1
|
||||||
|
SV2 --> API3 & API4
|
||||||
|
SV3 --> API2
|
||||||
|
|
||||||
|
%% Styling
|
||||||
|
classDef routeStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
|
||||||
|
classDef componentStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
|
||||||
|
classDef componentGroupStyle fill:#e1bee7,stroke:#7b1fa2,stroke-width:1px
|
||||||
|
classDef storeStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
|
||||||
|
classDef stateStyle fill:#ffe0b2,stroke:#e65100,stroke-width:1px
|
||||||
|
classDef methodStyle fill:#ffecb3,stroke:#e65100,stroke-width:1px
|
||||||
|
classDef reactiveStyle fill:#fffde7,stroke:#f9a825,stroke-width:1px
|
||||||
|
classDef serviceStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
|
||||||
|
classDef serviceMStyle fill:#c8e6c9,stroke:#2e7d32,stroke-width:1px
|
||||||
|
classDef storageStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
|
||||||
|
classDef apiStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
|
||||||
|
|
||||||
|
class R1,R2,RL routeStyle
|
||||||
|
class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message componentStyle
|
||||||
|
class C_SelectorModel,C_Settings componentStyle
|
||||||
|
class LayoutComponents,ChatComponents,ControlComponents componentGroupStyle
|
||||||
|
class S1,S2,S3,S4,S5 storeStyle
|
||||||
|
class S1State,S2State,S3State,S4State,S5State stateStyle
|
||||||
|
class S1Msg,S1Regen,S1Edit,S1Stream,S1LoadState,S1ProcState,S1Error,S1Utils methodStyle
|
||||||
|
class S2Lifecycle,S2ConvCRUD,S2MsgMgmt,S2Nav,S2Modal,S2Export,S2Utils methodStyle
|
||||||
|
class S3Getters,S3Modal,S3Status,S3Fetch,S3Select,S3LoadUnload,S3Usage,S3Utils methodStyle
|
||||||
|
class S4Getters,S4Data,S4Utils methodStyle
|
||||||
|
class S5Lifecycle,S5Update,S5Reset,S5Sync,S5Utils methodStyle
|
||||||
|
class ChatExports,ConvExports,ModelsExports,ServerExports,SettingsExports reactiveStyle
|
||||||
|
class SV1,SV2,SV3,SV4,SV5 serviceStyle
|
||||||
|
class SV1Msg,SV1Stream,SV1Convert,SV1Utils serviceMStyle
|
||||||
|
class SV2List,SV2LoadUnload,SV2Status serviceMStyle
|
||||||
|
class SV3Fetch serviceMStyle
|
||||||
|
class SV4Conv,SV4Msg,SV4Node,SV4Import serviceMStyle
|
||||||
|
class SV5Extract,SV5Merge,SV5Info,SV5Diff serviceMStyle
|
||||||
|
class ST1,ST2,ST3,ST5,ST6,ST7 storageStyle
|
||||||
|
class API1,API2,API3,API4 apiStyle
|
||||||
|
```
|
||||||
|
|
@ -54,6 +54,10 @@ import type { SettingsChatServiceOptions } from '$lib/types/settings';
|
||||||
* - Request lifecycle management (abort via AbortSignal)
|
* - Request lifecycle management (abort via AbortSignal)
|
||||||
*/
|
*/
|
||||||
export class ChatService {
|
export class ChatService {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Messaging
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a chat completion request to the llama.cpp server.
|
* Sends a chat completion request to the llama.cpp server.
|
||||||
* Supports both streaming and non-streaming responses with comprehensive parameter configuration.
|
* Supports both streaming and non-streaming responses with comprehensive parameter configuration.
|
||||||
|
|
@ -261,6 +265,10 @@ export class ChatService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Streaming
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles streaming response from the chat completion API
|
* Handles streaming response from the chat completion API
|
||||||
* @param response - The Response object from the fetch request
|
* @param response - The Response object from the fetch request
|
||||||
|
|
@ -571,6 +579,10 @@ export class ChatService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Conversion
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a database message with attachments to API chat message format.
|
* Converts a database message with attachments to API chat message format.
|
||||||
* Processes various attachment types (images, text files, PDFs) and formats them
|
* Processes various attachment types (images, text files, PDFs) and formats them
|
||||||
|
|
@ -681,6 +693,10 @@ export class ChatService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get server properties - static method for API compatibility (to be refactored)
|
* Get server properties - static method for API compatibility (to be refactored)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,10 @@ import { v4 as uuid } from 'uuid';
|
||||||
* `currNode` tracks the currently active branch endpoint.
|
* `currNode` tracks the currently active branch endpoint.
|
||||||
*/
|
*/
|
||||||
export class DatabaseService {
|
export class DatabaseService {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Conversations
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new conversation.
|
* Creates a new conversation.
|
||||||
*
|
*
|
||||||
|
|
@ -84,6 +88,10 @@ export class DatabaseService {
|
||||||
return conversation;
|
return conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Messages
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new message branch by adding a message and updating parent/child relationships.
|
* Creates a new message branch by adding a message and updating parent/child relationships.
|
||||||
* Also updates the conversation's currNode to point to the new message.
|
* Also updates the conversation's currNode to point to the new message.
|
||||||
|
|
@ -277,6 +285,10 @@ export class DatabaseService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Navigation
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the conversation's current node (active branch).
|
* Updates the conversation's current node (active branch).
|
||||||
* This determines which conversation path is currently being viewed.
|
* This determines which conversation path is currently being viewed.
|
||||||
|
|
@ -304,6 +316,10 @@ export class DatabaseService {
|
||||||
await db.messages.update(id, updates);
|
await db.messages.update(id, updates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Import
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports multiple conversations and their messages.
|
* Imports multiple conversations and their messages.
|
||||||
* Skips conversations that already exist.
|
* Skips conversations that already exist.
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import type {
|
||||||
*/
|
*/
|
||||||
export class ModelsService {
|
export class ModelsService {
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// MODEL + ROUTER mode - OpenAI-compatible API
|
// Listing
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,10 +46,6 @@ export class ModelsService {
|
||||||
return response.json() as Promise<ApiModelListResponse>;
|
return response.json() as Promise<ApiModelListResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
// ROUTER mode only - Model management API
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch list of all models with detailed metadata (ROUTER mode)
|
* Fetch list of all models with detailed metadata (ROUTER mode)
|
||||||
* Returns models with load status, paths, and other metadata
|
* Returns models with load status, paths, and other metadata
|
||||||
|
|
@ -66,6 +62,10 @@ export class ModelsService {
|
||||||
return response.json() as Promise<ApiRouterModelsListResponse>;
|
return response.json() as Promise<ApiRouterModelsListResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Load/Unload
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a model (ROUTER mode)
|
* Load a model (ROUTER mode)
|
||||||
* POST /models/load
|
* POST /models/load
|
||||||
|
|
@ -112,6 +112,10 @@ export class ModelsService {
|
||||||
return response.json() as Promise<ApiRouterModelsUnloadResponse>;
|
return response.json() as Promise<ApiRouterModelsUnloadResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Status
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get status of a specific model (ROUTER mode)
|
* Get status of a specific model (ROUTER mode)
|
||||||
* @param modelId - Model identifier to check
|
* @param modelId - Model identifier to check
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ export const SYNCABLE_PARAMETERS: SyncableParameter[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export class ParameterSyncService {
|
export class ParameterSyncService {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Extraction
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Round floating-point numbers to avoid JavaScript precision issues
|
* Round floating-point numbers to avoid JavaScript precision issues
|
||||||
*/
|
*/
|
||||||
|
|
@ -95,6 +99,10 @@ export class ParameterSyncService {
|
||||||
return extracted;
|
return extracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Merging
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge server defaults with current user settings
|
* Merge server defaults with current user settings
|
||||||
* Returns updated settings that respect user overrides while using server defaults
|
* Returns updated settings that respect user overrides while using server defaults
|
||||||
|
|
@ -116,6 +124,10 @@ export class ParameterSyncService {
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Info
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get parameter information including source and values
|
* Get parameter information including source and values
|
||||||
*/
|
*/
|
||||||
|
|
@ -172,6 +184,10 @@ export class ParameterSyncService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Diff
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a diff between current settings and server defaults
|
* Create a diff between current settings and server defaults
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ import { getAuthHeaders } from '$lib/utils/api-headers';
|
||||||
* - serverStore: Primary consumer for server state management
|
* - serverStore: Primary consumer for server state management
|
||||||
*/
|
*/
|
||||||
export class PropsService {
|
export class PropsService {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Fetching
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches server properties from the /props endpoint
|
* Fetches server properties from the /props endpoint
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -55,78 +55,24 @@ import type { DatabaseMessage, DatabaseMessageExtra } from '$lib/types/database'
|
||||||
* - Automatic state sync when switching between conversations
|
* - Automatic state sync when switching between conversations
|
||||||
*/
|
*/
|
||||||
class ChatStore {
|
class ChatStore {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// State
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
activeProcessingState = $state<ApiProcessingState | null>(null);
|
activeProcessingState = $state<ApiProcessingState | null>(null);
|
||||||
currentResponse = $state('');
|
currentResponse = $state('');
|
||||||
errorDialogState = $state<{ type: 'timeout' | 'server'; message: string } | null>(null);
|
errorDialogState = $state<{ type: 'timeout' | 'server'; message: string } | null>(null);
|
||||||
isLoading = $state(false);
|
isLoading = $state(false);
|
||||||
chatLoadingStates = new SvelteMap<string, boolean>();
|
chatLoadingStates = new SvelteMap<string, boolean>();
|
||||||
chatStreamingStates = new SvelteMap<string, { response: string; messageId: string }>();
|
chatStreamingStates = new SvelteMap<string, { response: string; messageId: string }>();
|
||||||
|
|
||||||
// Abort controllers for per-conversation request cancellation
|
|
||||||
private abortControllers = new SvelteMap<string, AbortController>();
|
private abortControllers = new SvelteMap<string, AbortController>();
|
||||||
|
|
||||||
// Processing state tracking - per-conversation timing/context info
|
|
||||||
private processingStates = new SvelteMap<string, ApiProcessingState | null>();
|
private processingStates = new SvelteMap<string, ApiProcessingState | null>();
|
||||||
private activeConversationId = $state<string | null>(null);
|
private activeConversationId = $state<string | null>(null);
|
||||||
private isStreamingActive = $state(false);
|
private isStreamingActive = $state(false);
|
||||||
|
|
||||||
// ============ API Options ============
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Loading State
|
||||||
private getApiOptions(): Record<string, unknown> {
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
const currentConfig = config();
|
|
||||||
const hasValue = (value: unknown): boolean =>
|
|
||||||
value !== undefined && value !== null && value !== '';
|
|
||||||
|
|
||||||
const apiOptions: Record<string, unknown> = { stream: true, timings_per_token: true };
|
|
||||||
|
|
||||||
// Model selection (required in ROUTER mode)
|
|
||||||
if (isRouterMode()) {
|
|
||||||
const modelName = selectedModelName();
|
|
||||||
if (modelName) apiOptions.model = modelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config options needed by ChatService
|
|
||||||
if (currentConfig.systemMessage) apiOptions.systemMessage = currentConfig.systemMessage;
|
|
||||||
if (currentConfig.disableReasoningFormat) apiOptions.disableReasoningFormat = true;
|
|
||||||
|
|
||||||
if (hasValue(currentConfig.temperature))
|
|
||||||
apiOptions.temperature = Number(currentConfig.temperature);
|
|
||||||
if (hasValue(currentConfig.max_tokens))
|
|
||||||
apiOptions.max_tokens = Number(currentConfig.max_tokens);
|
|
||||||
if (hasValue(currentConfig.dynatemp_range))
|
|
||||||
apiOptions.dynatemp_range = Number(currentConfig.dynatemp_range);
|
|
||||||
if (hasValue(currentConfig.dynatemp_exponent))
|
|
||||||
apiOptions.dynatemp_exponent = Number(currentConfig.dynatemp_exponent);
|
|
||||||
if (hasValue(currentConfig.top_k)) apiOptions.top_k = Number(currentConfig.top_k);
|
|
||||||
if (hasValue(currentConfig.top_p)) apiOptions.top_p = Number(currentConfig.top_p);
|
|
||||||
if (hasValue(currentConfig.min_p)) apiOptions.min_p = Number(currentConfig.min_p);
|
|
||||||
if (hasValue(currentConfig.xtc_probability))
|
|
||||||
apiOptions.xtc_probability = Number(currentConfig.xtc_probability);
|
|
||||||
if (hasValue(currentConfig.xtc_threshold))
|
|
||||||
apiOptions.xtc_threshold = Number(currentConfig.xtc_threshold);
|
|
||||||
if (hasValue(currentConfig.typ_p)) apiOptions.typ_p = Number(currentConfig.typ_p);
|
|
||||||
if (hasValue(currentConfig.repeat_last_n))
|
|
||||||
apiOptions.repeat_last_n = Number(currentConfig.repeat_last_n);
|
|
||||||
if (hasValue(currentConfig.repeat_penalty))
|
|
||||||
apiOptions.repeat_penalty = Number(currentConfig.repeat_penalty);
|
|
||||||
if (hasValue(currentConfig.presence_penalty))
|
|
||||||
apiOptions.presence_penalty = Number(currentConfig.presence_penalty);
|
|
||||||
if (hasValue(currentConfig.frequency_penalty))
|
|
||||||
apiOptions.frequency_penalty = Number(currentConfig.frequency_penalty);
|
|
||||||
if (hasValue(currentConfig.dry_multiplier))
|
|
||||||
apiOptions.dry_multiplier = Number(currentConfig.dry_multiplier);
|
|
||||||
if (hasValue(currentConfig.dry_base)) apiOptions.dry_base = Number(currentConfig.dry_base);
|
|
||||||
if (hasValue(currentConfig.dry_allowed_length))
|
|
||||||
apiOptions.dry_allowed_length = Number(currentConfig.dry_allowed_length);
|
|
||||||
if (hasValue(currentConfig.dry_penalty_last_n))
|
|
||||||
apiOptions.dry_penalty_last_n = Number(currentConfig.dry_penalty_last_n);
|
|
||||||
if (currentConfig.samplers) apiOptions.samplers = currentConfig.samplers;
|
|
||||||
if (currentConfig.custom) apiOptions.custom = currentConfig.custom;
|
|
||||||
|
|
||||||
return apiOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============ Loading State Management ============
|
|
||||||
|
|
||||||
private setChatLoading(convId: string, loading: boolean): void {
|
private setChatLoading(convId: string, loading: boolean): void {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|
@ -171,28 +117,9 @@ class ChatStore {
|
||||||
this.currentResponse = '';
|
this.currentResponse = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Processing State Management ============
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Processing State
|
||||||
/**
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
* Start streaming session tracking
|
|
||||||
*/
|
|
||||||
startStreaming(): void {
|
|
||||||
this.isStreamingActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop streaming session tracking
|
|
||||||
*/
|
|
||||||
stopStreaming(): void {
|
|
||||||
this.isStreamingActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if currently in a streaming session
|
|
||||||
*/
|
|
||||||
isStreaming(): boolean {
|
|
||||||
return this.isStreamingActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the active conversation for statistics display
|
* Set the active conversation for statistics display
|
||||||
|
|
@ -301,6 +228,31 @@ class ChatStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Streaming
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start streaming session tracking
|
||||||
|
*/
|
||||||
|
startStreaming(): void {
|
||||||
|
this.isStreamingActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop streaming session tracking
|
||||||
|
*/
|
||||||
|
stopStreaming(): void {
|
||||||
|
this.isStreamingActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if currently in a streaming session
|
||||||
|
*/
|
||||||
|
isStreaming(): boolean {
|
||||||
|
return this.isStreamingActive;
|
||||||
|
}
|
||||||
|
|
||||||
private getContextTotal(): number {
|
private getContextTotal(): number {
|
||||||
const activeState = this.getActiveProcessingState();
|
const activeState = this.getActiveProcessingState();
|
||||||
|
|
||||||
|
|
@ -360,8 +312,6 @@ class ChatStore {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Model Detection ============
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the model used in a conversation based on the latest assistant message.
|
* Gets the model used in a conversation based on the latest assistant message.
|
||||||
* Returns the model from the most recent assistant message that has a model field set.
|
* Returns the model from the most recent assistant message that has a model field set.
|
||||||
|
|
@ -380,7 +330,9 @@ class ChatStore {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Error Handling ============
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Error Handling
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private isAbortError(error: unknown): boolean {
|
private isAbortError(error: unknown): boolean {
|
||||||
return error instanceof Error && (error.name === 'AbortError' || error instanceof DOMException);
|
return error instanceof Error && (error.name === 'AbortError' || error instanceof DOMException);
|
||||||
|
|
@ -394,7 +346,9 @@ class ChatStore {
|
||||||
this.errorDialogState = null;
|
this.errorDialogState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Message Creation ============
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Message Operations
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async addMessage(
|
async addMessage(
|
||||||
role: ChatRole,
|
role: ChatRole,
|
||||||
|
|
@ -475,8 +429,6 @@ class ChatStore {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Streaming ============
|
|
||||||
|
|
||||||
private async streamChatCompletion(
|
private async streamChatCompletion(
|
||||||
allMessages: DatabaseMessage[],
|
allMessages: DatabaseMessage[],
|
||||||
assistantMessage: DatabaseMessage,
|
assistantMessage: DatabaseMessage,
|
||||||
|
|
@ -614,8 +566,6 @@ class ChatStore {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Send Message ============
|
|
||||||
|
|
||||||
async sendMessage(content: string, extras?: DatabaseMessageExtra[]): Promise<void> {
|
async sendMessage(content: string, extras?: DatabaseMessageExtra[]): Promise<void> {
|
||||||
if (!content.trim() && (!extras || extras.length === 0)) return;
|
if (!content.trim() && (!extras || extras.length === 0)) return;
|
||||||
const activeConv = conversationsStore.activeConversation;
|
const activeConv = conversationsStore.activeConversation;
|
||||||
|
|
@ -661,8 +611,6 @@ class ChatStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Graceful Stop ============
|
|
||||||
|
|
||||||
async stopGeneration(): Promise<void> {
|
async stopGeneration(): Promise<void> {
|
||||||
const activeConv = conversationsStore.activeConversation;
|
const activeConv = conversationsStore.activeConversation;
|
||||||
if (!activeConv) return;
|
if (!activeConv) return;
|
||||||
|
|
@ -746,8 +694,6 @@ class ChatStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Message Updates ============
|
|
||||||
|
|
||||||
async updateMessage(messageId: string, newContent: string): Promise<void> {
|
async updateMessage(messageId: string, newContent: string): Promise<void> {
|
||||||
const activeConv = conversationsStore.activeConversation;
|
const activeConv = conversationsStore.activeConversation;
|
||||||
if (!activeConv) return;
|
if (!activeConv) return;
|
||||||
|
|
@ -803,6 +749,10 @@ class ChatStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Regeneration
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async regenerateMessage(messageId: string): Promise<void> {
|
async regenerateMessage(messageId: string): Promise<void> {
|
||||||
const activeConv = conversationsStore.activeConversation;
|
const activeConv = conversationsStore.activeConversation;
|
||||||
if (!activeConv || this.isLoading) return;
|
if (!activeConv || this.isLoading) return;
|
||||||
|
|
@ -900,7 +850,9 @@ class ChatStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Branching Operations ============
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Editing
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async editAssistantMessage(
|
async editAssistantMessage(
|
||||||
messageId: string,
|
messageId: string,
|
||||||
|
|
@ -1268,6 +1220,64 @@ class ChatStore {
|
||||||
public getAllStreamingChats(): string[] {
|
public getAllStreamingChats(): string[] {
|
||||||
return Array.from(this.chatStreamingStates.keys());
|
return Array.from(this.chatStreamingStates.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private getApiOptions(): Record<string, unknown> {
|
||||||
|
const currentConfig = config();
|
||||||
|
const hasValue = (value: unknown): boolean =>
|
||||||
|
value !== undefined && value !== null && value !== '';
|
||||||
|
|
||||||
|
const apiOptions: Record<string, unknown> = { stream: true, timings_per_token: true };
|
||||||
|
|
||||||
|
// Model selection (required in ROUTER mode)
|
||||||
|
if (isRouterMode()) {
|
||||||
|
const modelName = selectedModelName();
|
||||||
|
if (modelName) apiOptions.model = modelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config options needed by ChatService
|
||||||
|
if (currentConfig.systemMessage) apiOptions.systemMessage = currentConfig.systemMessage;
|
||||||
|
if (currentConfig.disableReasoningFormat) apiOptions.disableReasoningFormat = true;
|
||||||
|
|
||||||
|
if (hasValue(currentConfig.temperature))
|
||||||
|
apiOptions.temperature = Number(currentConfig.temperature);
|
||||||
|
if (hasValue(currentConfig.max_tokens))
|
||||||
|
apiOptions.max_tokens = Number(currentConfig.max_tokens);
|
||||||
|
if (hasValue(currentConfig.dynatemp_range))
|
||||||
|
apiOptions.dynatemp_range = Number(currentConfig.dynatemp_range);
|
||||||
|
if (hasValue(currentConfig.dynatemp_exponent))
|
||||||
|
apiOptions.dynatemp_exponent = Number(currentConfig.dynatemp_exponent);
|
||||||
|
if (hasValue(currentConfig.top_k)) apiOptions.top_k = Number(currentConfig.top_k);
|
||||||
|
if (hasValue(currentConfig.top_p)) apiOptions.top_p = Number(currentConfig.top_p);
|
||||||
|
if (hasValue(currentConfig.min_p)) apiOptions.min_p = Number(currentConfig.min_p);
|
||||||
|
if (hasValue(currentConfig.xtc_probability))
|
||||||
|
apiOptions.xtc_probability = Number(currentConfig.xtc_probability);
|
||||||
|
if (hasValue(currentConfig.xtc_threshold))
|
||||||
|
apiOptions.xtc_threshold = Number(currentConfig.xtc_threshold);
|
||||||
|
if (hasValue(currentConfig.typ_p)) apiOptions.typ_p = Number(currentConfig.typ_p);
|
||||||
|
if (hasValue(currentConfig.repeat_last_n))
|
||||||
|
apiOptions.repeat_last_n = Number(currentConfig.repeat_last_n);
|
||||||
|
if (hasValue(currentConfig.repeat_penalty))
|
||||||
|
apiOptions.repeat_penalty = Number(currentConfig.repeat_penalty);
|
||||||
|
if (hasValue(currentConfig.presence_penalty))
|
||||||
|
apiOptions.presence_penalty = Number(currentConfig.presence_penalty);
|
||||||
|
if (hasValue(currentConfig.frequency_penalty))
|
||||||
|
apiOptions.frequency_penalty = Number(currentConfig.frequency_penalty);
|
||||||
|
if (hasValue(currentConfig.dry_multiplier))
|
||||||
|
apiOptions.dry_multiplier = Number(currentConfig.dry_multiplier);
|
||||||
|
if (hasValue(currentConfig.dry_base)) apiOptions.dry_base = Number(currentConfig.dry_base);
|
||||||
|
if (hasValue(currentConfig.dry_allowed_length))
|
||||||
|
apiOptions.dry_allowed_length = Number(currentConfig.dry_allowed_length);
|
||||||
|
if (hasValue(currentConfig.dry_penalty_last_n))
|
||||||
|
apiOptions.dry_penalty_last_n = Number(currentConfig.dry_penalty_last_n);
|
||||||
|
if (currentConfig.samplers) apiOptions.samplers = currentConfig.samplers;
|
||||||
|
if (currentConfig.custom) apiOptions.custom = currentConfig.custom;
|
||||||
|
|
||||||
|
return apiOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const chatStore = new ChatStore();
|
export const chatStore = new ChatStore();
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,10 @@ import type { ModelModalities } from '$lib/types/models';
|
||||||
* - `isInitialized`: Store initialization status
|
* - `isInitialized`: Store initialization status
|
||||||
*/
|
*/
|
||||||
class ConversationsStore {
|
class ConversationsStore {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// State
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** List of all conversations */
|
/** List of all conversations */
|
||||||
conversations = $state<DatabaseConversation[]>([]);
|
conversations = $state<DatabaseConversation[]>([]);
|
||||||
|
|
||||||
|
|
@ -64,6 +68,13 @@ class ConversationsStore {
|
||||||
/** Whether the store has been initialized */
|
/** Whether the store has been initialized */
|
||||||
isInitialized = $state(false);
|
isInitialized = $state(false);
|
||||||
|
|
||||||
|
/** Callback for title update confirmation dialog */
|
||||||
|
titleUpdateConfirmationCallback?: (currentTitle: string, newTitle: string) => Promise<boolean>;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Modalities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modalities used in the active conversation.
|
* Modalities used in the active conversation.
|
||||||
* Computed from attachments in activeMessages.
|
* Computed from attachments in activeMessages.
|
||||||
|
|
@ -113,15 +124,16 @@ class ConversationsStore {
|
||||||
return this.calculateModalitiesFromMessages(messagesBefore);
|
return this.calculateModalitiesFromMessages(messagesBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Callback for title update confirmation dialog */
|
|
||||||
titleUpdateConfirmationCallback?: (currentTitle: string, newTitle: string) => Promise<boolean>;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (browser) {
|
if (browser) {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Lifecycle
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the conversations store by loading conversations from the database
|
* Initializes the conversations store by loading conversations from the database
|
||||||
*/
|
*/
|
||||||
|
|
@ -141,6 +153,10 @@ class ConversationsStore {
|
||||||
this.conversations = await DatabaseService.getAllConversations();
|
this.conversations = await DatabaseService.getAllConversations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Conversation CRUD
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new conversation and navigates to it
|
* Creates a new conversation and navigates to it
|
||||||
* @param name - Optional name for the conversation
|
* @param name - Optional name for the conversation
|
||||||
|
|
@ -202,6 +218,10 @@ class ConversationsStore {
|
||||||
// Active processing conversation is now managed by chatStore
|
// Active processing conversation is now managed by chatStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Message Management
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes active messages based on currNode after branch navigation
|
* Refreshes active messages based on currNode after branch navigation
|
||||||
*/
|
*/
|
||||||
|
|
@ -248,16 +268,6 @@ class ConversationsStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the callback function for title update confirmations
|
|
||||||
* @param callback - Function to call when confirmation is needed
|
|
||||||
*/
|
|
||||||
setTitleUpdateConfirmationCallback(
|
|
||||||
callback: (currentTitle: string, newTitle: string) => Promise<boolean>
|
|
||||||
): void {
|
|
||||||
this.titleUpdateConfirmationCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates conversation title with optional confirmation dialog based on settings
|
* Updates conversation title with optional confirmation dialog based on settings
|
||||||
* @param convId - The conversation ID to update
|
* @param convId - The conversation ID to update
|
||||||
|
|
@ -289,6 +299,10 @@ class ConversationsStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Navigation
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the current node of the active conversation
|
* Updates the current node of the active conversation
|
||||||
* @param nodeId - The new current node ID
|
* @param nodeId - The new current node ID
|
||||||
|
|
@ -376,6 +390,10 @@ class ConversationsStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Import/Export
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads a conversation as JSON file
|
* Downloads a conversation as JSON file
|
||||||
* @param convId - The conversation ID to download
|
* @param convId - The conversation ID to download
|
||||||
|
|
@ -580,6 +598,20 @@ class ConversationsStore {
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the callback function for title update confirmations
|
||||||
|
* @param callback - Function to call when confirmation is needed
|
||||||
|
*/
|
||||||
|
setTitleUpdateConfirmationCallback(
|
||||||
|
callback: (currentTitle: string, newTitle: string) => Promise<boolean>
|
||||||
|
): void {
|
||||||
|
this.titleUpdateConfirmationCallback = callback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const conversationsStore = new ConversationsStore();
|
export const conversationsStore = new ConversationsStore();
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Methods - Model Modalities
|
// Modalities
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -133,7 +133,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Methods - Model Status
|
// Status Queries
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
isModelLoaded(modelId: string): boolean {
|
isModelLoaded(modelId: string): boolean {
|
||||||
|
|
@ -160,7 +160,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Fetch Models
|
// Data Fetching
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -328,7 +328,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Select Model
|
// Model Selection
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -382,7 +382,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Load/Unload Models (ROUTER mode)
|
// Loading/Unloading Models
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -501,7 +501,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Model Usage Tracking
|
// Usage Tracking
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -546,7 +546,7 @@ class ModelsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Private Helpers
|
// Utilities
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private toDisplayName(id: string): string {
|
private toDisplayName(id: string): string {
|
||||||
|
|
@ -556,10 +556,6 @@ class ModelsStore {
|
||||||
return candidate && candidate.trim().length > 0 ? candidate : id;
|
return candidate && candidate.trim().length > 0 ? candidate : id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
// Clear State
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.models = [];
|
this.models = [];
|
||||||
this.routerModels = [];
|
this.routerModels = [];
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,20 @@ import { ServerRole, ModelModality } from '$lib/enums';
|
||||||
* apply to MODEL mode (single model).
|
* apply to MODEL mode (single model).
|
||||||
*/
|
*/
|
||||||
class ServerStore {
|
class ServerStore {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// State
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
props = $state<ApiLlamaCppServerProps | null>(null);
|
props = $state<ApiLlamaCppServerProps | null>(null);
|
||||||
loading = $state(false);
|
loading = $state(false);
|
||||||
error = $state<string | null>(null);
|
error = $state<string | null>(null);
|
||||||
role = $state<ServerRole | null>(null);
|
role = $state<ServerRole | null>(null);
|
||||||
private fetchPromise: Promise<void> | null = null;
|
private fetchPromise: Promise<void> | null = null;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Getters
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get model name from server props.
|
* Get model name from server props.
|
||||||
* In MODEL mode: extracts from model_path or model_alias
|
* In MODEL mode: extracts from model_path or model_alias
|
||||||
|
|
@ -89,13 +97,9 @@ class ServerStore {
|
||||||
return this.role === ServerRole.MODEL;
|
return this.role === ServerRole.MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
private detectRole(props: ApiLlamaCppServerProps): void {
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
const newRole = props?.role === ServerRole.ROUTER ? ServerRole.ROUTER : ServerRole.MODEL;
|
// Data Handling
|
||||||
if (this.role !== newRole) {
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
this.role = newRole;
|
|
||||||
console.info(`Server running in ${newRole === ServerRole.ROUTER ? 'ROUTER' : 'MODEL'} mode`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetch(): Promise<void> {
|
async fetch(): Promise<void> {
|
||||||
if (this.fetchPromise) return this.fetchPromise;
|
if (this.fetchPromise) return this.fetchPromise;
|
||||||
|
|
@ -147,6 +151,7 @@ class ServerStore {
|
||||||
|
|
||||||
return 'Failed to connect to server';
|
return 'Failed to connect to server';
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.props = null;
|
this.props = null;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
@ -154,6 +159,18 @@ class ServerStore {
|
||||||
this.role = null;
|
this.role = null;
|
||||||
this.fetchPromise = null;
|
this.fetchPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private detectRole(props: ApiLlamaCppServerProps): void {
|
||||||
|
const newRole = props?.role === ServerRole.ROUTER ? ServerRole.ROUTER : ServerRole.MODEL;
|
||||||
|
if (this.role !== newRole) {
|
||||||
|
this.role = newRole;
|
||||||
|
console.info(`Server running in ${newRole === ServerRole.ROUTER ? 'ROUTER' : 'MODEL'} mode`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serverStore = new ServerStore();
|
export const serverStore = new ServerStore();
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,19 @@ import {
|
||||||
} from '$lib/constants/localstorage-keys';
|
} from '$lib/constants/localstorage-keys';
|
||||||
|
|
||||||
class SettingsStore {
|
class SettingsStore {
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// State
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
config = $state<SettingsConfigType>({ ...SETTING_CONFIG_DEFAULT });
|
config = $state<SettingsConfigType>({ ...SETTING_CONFIG_DEFAULT });
|
||||||
theme = $state<string>('auto');
|
theme = $state<string>('auto');
|
||||||
isInitialized = $state(false);
|
isInitialized = $state(false);
|
||||||
userOverrides = $state<Set<string>>(new Set());
|
userOverrides = $state<Set<string>>(new Set());
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities (private helpers)
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to get server defaults with null safety
|
* Helper method to get server defaults with null safety
|
||||||
* Centralizes the pattern of getting and extracting server defaults
|
* Centralizes the pattern of getting and extracting server defaults
|
||||||
|
|
@ -63,6 +71,10 @@ class SettingsStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Lifecycle
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the settings store by loading from localStorage
|
* Initialize the settings store by loading from localStorage
|
||||||
*/
|
*/
|
||||||
|
|
@ -113,6 +125,10 @@ class SettingsStore {
|
||||||
|
|
||||||
this.theme = localStorage.getItem('theme') || 'auto';
|
this.theme = localStorage.getItem('theme') || 'auto';
|
||||||
}
|
}
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Config Updates
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a specific configuration setting
|
* Update a specific configuration setting
|
||||||
* @param key - The configuration key to update
|
* @param key - The configuration key to update
|
||||||
|
|
@ -213,6 +229,10 @@ class SettingsStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Reset
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset configuration to defaults
|
* Reset configuration to defaults
|
||||||
*/
|
*/
|
||||||
|
|
@ -238,22 +258,32 @@ class SettingsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific configuration value
|
* Reset a parameter to server default (or webui default if no server default)
|
||||||
* @param key - The configuration key to get
|
|
||||||
* @returns The configuration value
|
|
||||||
*/
|
*/
|
||||||
getConfig<K extends keyof SettingsConfigType>(key: K): SettingsConfigType[K] {
|
resetParameterToServerDefault(key: string): void {
|
||||||
return this.config[key];
|
const serverDefaults = this.getServerDefaults();
|
||||||
|
|
||||||
|
if (serverDefaults[key] !== undefined) {
|
||||||
|
const value = normalizeFloatingPoint(serverDefaults[key]);
|
||||||
|
|
||||||
|
this.config[key as keyof SettingsConfigType] =
|
||||||
|
value as SettingsConfigType[keyof SettingsConfigType];
|
||||||
|
} else {
|
||||||
|
if (key in SETTING_CONFIG_DEFAULT) {
|
||||||
|
const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key);
|
||||||
|
|
||||||
|
setConfigValue(this.config, key, defaultValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
this.userOverrides.delete(key);
|
||||||
* Get the entire configuration object
|
this.saveConfig();
|
||||||
* @returns The complete configuration object
|
|
||||||
*/
|
|
||||||
getAllConfig(): SettingsConfigType {
|
|
||||||
return { ...this.config };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Server Sync
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize settings with props defaults when server properties are first loaded
|
* Initialize settings with props defaults when server properties are first loaded
|
||||||
* This sets up the default values from /props endpoint
|
* This sets up the default values from /props endpoint
|
||||||
|
|
@ -287,15 +317,6 @@ class SettingsStore {
|
||||||
console.log('Current user overrides after sync:', Array.from(this.userOverrides));
|
console.log('Current user overrides after sync:', Array.from(this.userOverrides));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all user overrides (for debugging)
|
|
||||||
*/
|
|
||||||
clearAllUserOverrides(): void {
|
|
||||||
this.userOverrides.clear();
|
|
||||||
this.saveConfig();
|
|
||||||
console.log('Cleared all user overrides');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset all parameters to their default values (from props)
|
* Reset all parameters to their default values (from props)
|
||||||
* This is used by the "Reset to Default" functionality
|
* This is used by the "Reset to Default" functionality
|
||||||
|
|
@ -324,6 +345,27 @@ class SettingsStore {
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Utilities
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific configuration value
|
||||||
|
* @param key - The configuration key to get
|
||||||
|
* @returns The configuration value
|
||||||
|
*/
|
||||||
|
getConfig<K extends keyof SettingsConfigType>(key: K): SettingsConfigType[K] {
|
||||||
|
return this.config[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the entire configuration object
|
||||||
|
* @returns The complete configuration object
|
||||||
|
*/
|
||||||
|
getAllConfig(): SettingsConfigType {
|
||||||
|
return { ...this.config };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get parameter information including source for a specific parameter
|
* Get parameter information including source for a specific parameter
|
||||||
*/
|
*/
|
||||||
|
|
@ -339,29 +381,6 @@ class SettingsStore {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset a parameter to server default (or webui default if no server default)
|
|
||||||
*/
|
|
||||||
resetParameterToServerDefault(key: string): void {
|
|
||||||
const serverDefaults = this.getServerDefaults();
|
|
||||||
|
|
||||||
if (serverDefaults[key] !== undefined) {
|
|
||||||
const value = normalizeFloatingPoint(serverDefaults[key]);
|
|
||||||
|
|
||||||
this.config[key as keyof SettingsConfigType] =
|
|
||||||
value as SettingsConfigType[keyof SettingsConfigType];
|
|
||||||
} else {
|
|
||||||
if (key in SETTING_CONFIG_DEFAULT) {
|
|
||||||
const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key);
|
|
||||||
|
|
||||||
setConfigValue(this.config, key, defaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.userOverrides.delete(key);
|
|
||||||
this.saveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get diff between current settings and server defaults
|
* Get diff between current settings and server defaults
|
||||||
*/
|
*/
|
||||||
|
|
@ -376,6 +395,15 @@ class SettingsStore {
|
||||||
|
|
||||||
return ParameterSyncService.createParameterDiff(configAsRecord, serverDefaults);
|
return ParameterSyncService.createParameterDiff(configAsRecord, serverDefaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all user overrides (for debugging)
|
||||||
|
*/
|
||||||
|
clearAllUserOverrides(): void {
|
||||||
|
this.userOverrides.clear();
|
||||||
|
this.saveConfig();
|
||||||
|
console.log('Cleared all user overrides');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const settingsStore = new SettingsStore();
|
export const settingsStore = new SettingsStore();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue