Stores->>DB: load conversations
Stores->>API: GET /props
API-->>Stores: {role: "router"}
- Stores->>API: GET /models
+ Stores->>API: GET /v1/models
API-->>Stores: models[] with status (loaded/available)
loop each loaded model
Stores->>API: GET /props?model=X
alt model not loaded
Stores->>API: POST /models/load
loop poll status
- Stores->>API: GET /models
+ Stores->>API: GET /v1/models
API-->>Stores: check if loaded
end
Stores->>API: GET /props?model=X
UI->>modelsStore: fetchRouterModels()
activate modelsStore
modelsStore->>ModelsSvc: listRouter()
- ModelsSvc->>API: GET /models
+ ModelsSvc->>API: GET /v1/models
API-->>ModelsSvc: ApiRouterModelsListResponse
Note right of API: {data: [{id, status, path, in_cache}]}
modelsStore->>modelsStore: routerModels = $state(data)
loop poll every 500ms (max 60 attempts)
modelsStore->>modelsStore: fetchRouterModels()
modelsStore->>ModelsSvc: listRouter()
- ModelsSvc->>API: GET /models
+ ModelsSvc->>API: GET /v1/models
API-->>ModelsSvc: models[]
modelsStore->>modelsStore: getModelStatus(modelId)
alt status === LOADED
modelsStore->>modelsStore: pollForModelStatus(modelId, UNLOADED)
loop poll until unloaded
modelsStore->>ModelsSvc: listRouter()
- ModelsSvc->>API: GET /models
+ ModelsSvc->>API: GET /v1/models
end
modelsStore->>modelsStore: modelLoadingStates.set(modelId, false)
<script lang="ts">
import { ChatMessage } from '$lib/components/app';
- import { DatabaseService } from '$lib/services/database';
import { chatStore } from '$lib/stores/chat.svelte';
import { conversationsStore, activeConversation } from '$lib/stores/conversations.svelte';
import { getMessageSiblings } from '$lib/utils';
const conversation = activeConversation();
if (conversation) {
- DatabaseService.getConversationMessages(conversation.id).then((messages) => {
+ conversationsStore.getConversationMessages(conversation.id).then((messages) => {
allConversationMessages = messages;
});
} else {
import { Textarea } from '$lib/components/ui/textarea';
import { SETTING_CONFIG_DEFAULT, SETTING_CONFIG_INFO } from '$lib/constants/settings-config';
import { settingsStore } from '$lib/stores/settings.svelte';
- import { ParameterSyncService } from '$lib/services/parameter-sync';
import { ChatSettingsParameterSourceIndicator } from '$lib/components/app';
import type { Component } from 'svelte';
// Helper function to get parameter source info for syncable parameters
function getParameterSourceInfo(key: string) {
- if (!ParameterSyncService.canSyncParameter(key)) {
+ if (!settingsStore.canSyncParameter(key)) {
return null;
}
import { Download, Upload } from '@lucide/svelte';
import { Button } from '$lib/components/ui/button';
import { DialogConversationSelection } from '$lib/components/app';
- import { DatabaseService } from '$lib/services/database';
import { createMessageCountMap } from '$lib/utils';
- import { conversationsStore } from '$lib/stores/conversations.svelte';
+ import { conversationsStore, conversations } from '$lib/stores/conversations.svelte';
let exportedConversations = $state<DatabaseConversation[]>([]);
let importedConversations = $state<DatabaseConversation[]>([]);
async function handleExportClick() {
try {
- const allConversations = await DatabaseService.getAllConversations();
+ const allConversations = conversations();
if (allConversations.length === 0) {
alert('No conversations to export');
return;
}
const conversationsWithMessages = await Promise.all(
- allConversations.map(async (conv) => {
- const messages = await DatabaseService.getConversationMessages(conv.id);
+ allConversations.map(async (conv: DatabaseConversation) => {
+ const messages = await conversationsStore.getConversationMessages(conv.id);
return { conv, messages };
})
);
try {
const allData: ExportedConversations = await Promise.all(
selectedConversations.map(async (conv) => {
- const messages = await DatabaseService.getConversationMessages(conv.id);
+ const messages = await conversationsStore.getConversationMessages(conv.id);
return { conv: $state.snapshot(conv), messages: $state.snapshot(messages) };
})
);
.snapshot(fullImportData)
.filter((item) => selectedIds.has(item.conv.id));
- await DatabaseService.importConversations(selectedData);
-
- await conversationsStore.loadConversations();
+ await conversationsStore.importConversationsData(selectedData);
importedConversations = selectedConversations;
showImportSummary = true;
import * as Table from '$lib/components/ui/table';
import { BadgeModality, CopyToClipboardIcon } from '$lib/components/app';
import { serverStore } from '$lib/stores/server.svelte';
- import { modelsStore } from '$lib/stores/models.svelte';
- import { ChatService } from '$lib/services/chat';
+ import { modelsStore, modelOptions, modelsLoading } from '$lib/stores/models.svelte';
import { formatFileSize, formatParameters, formatNumber } from '$lib/utils';
interface Props {
let serverProps = $derived(serverStore.props);
let modelName = $derived(modelsStore.singleModelName);
+ let models = $derived(modelOptions());
+ let isLoadingModels = $derived(modelsLoading());
+
+ // Get the first model for single-model mode display
+ let firstModel = $derived(models[0] ?? null);
// Get modalities from modelStore using the model ID from the first model
- // For now it supports only for single-model mode, will be extended with further improvements for multi-model functioanlities
let modalities = $derived.by(() => {
- if (!modelsData?.data?.[0]?.id) return [];
-
- return modelsStore.getModelModalitiesArray(modelsData.data[0].id);
+ if (!firstModel?.id) return [];
+ return modelsStore.getModelModalitiesArray(firstModel.id);
});
- let modelsData = $state<ApiModelListResponse | null>(null);
- let isLoadingModels = $state(false);
-
- // Fetch models data when dialog opens
+ // Ensure models are fetched when dialog opens
$effect(() => {
- if (open && !modelsData) {
- loadModelsData();
+ if (open && models.length === 0) {
+ modelsStore.fetch();
}
});
-
- async function loadModelsData() {
- isLoadingModels = true;
-
- try {
- modelsData = await ChatService.getModels();
- } catch (error) {
- console.error('Failed to load models data:', error);
- // Set empty data to prevent infinite loading
- modelsData = { object: 'list', data: [] };
- } finally {
- isLoadingModels = false;
- }
- }
</script>
<Dialog.Root bind:open {onOpenChange}>
<div class="flex items-center justify-center py-8">
<div class="text-sm text-muted-foreground">Loading model information...</div>
</div>
- {:else if modelsData && modelsData.data.length > 0}
- {@const modelMeta = modelsData.data[0].meta}
+ {:else if firstModel}
+ {@const modelMeta = firstModel.meta}
{#if serverProps}
<Table.Root>
// Utilities
// ─────────────────────────────────────────────────────────────────────────────
- /**
- * Get server properties - static method for API compatibility (to be refactored)
- */
- static async getServerProps(): Promise<ApiLlamaCppServerProps> {
- try {
- const response = await fetch(`./props`, {
- headers: getJsonHeaders()
- });
-
- if (!response.ok) {
- throw new Error(`Failed to fetch server props: ${response.status}`);
- }
-
- const data = await response.json();
- return data;
- } catch (error) {
- console.error('Error fetching server props:', error);
- throw error;
- }
- }
-
- /**
- * Get model information from /models endpoint (to be refactored)
- */
- static async getModels(): Promise<ApiModelListResponse> {
- try {
- const response = await fetch(`./models`, {
- headers: getJsonHeaders()
- });
-
- if (!response.ok) {
- throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
- }
-
- const data = await response.json();
- return data;
- } catch (error) {
- console.error('Error fetching models:', error);
- throw error;
- }
- }
-
/**
* Injects a system message at the beginning of the conversation if provided.
* Checks for existing system messages to avoid duplication.
*
* This service handles communication with model-related endpoints:
* - `/v1/models` - OpenAI-compatible model list (MODEL + ROUTER mode)
- * - `/models` - Router-specific model management (ROUTER mode only)
+ * - `/models/load`, `/models/unload` - Router-specific model management (ROUTER mode only)
*
* **Responsibilities:**
* - List available models
* Returns models with load status, paths, and other metadata
*/
static async listRouter(): Promise<ApiRouterModelsListResponse> {
- const response = await fetch(`${base}/models`, {
+ const response = await fetch(`${base}/v1/models`, {
headers: getJsonHeaders()
});
return await DatabaseService.getConversationMessages(convId);
}
+ /**
+ * Imports conversations from provided data (without file picker)
+ * @param data - Array of conversation data with messages
+ * @returns Import result with counts
+ */
+ async importConversationsData(
+ data: ExportedConversations
+ ): Promise<{ imported: number; skipped: number }> {
+ const result = await DatabaseService.importConversations(data);
+ await this.loadConversations();
+ return result;
+ }
+
/**
* Adds a message to the active messages array
* Used by chatStore when creating new messages
return { ...this.config };
}
+ canSyncParameter(key: string): boolean {
+ return ParameterSyncService.canSyncParameter(key);
+ }
+
/**
* Get parameter information including source for a specific parameter
*/