webui: add model information dialog to model selector in router mode
This commit is contained in:
parent
d23355afc3
commit
2d0566d38f
|
|
@ -2,32 +2,41 @@
|
|||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
import * as Table from '$lib/components/ui/table';
|
||||
import { BadgeModality, ActionIconCopyToClipboard } from '$lib/components/app';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import { modelsStore, modelOptions, modelsLoading } from '$lib/stores/models.svelte';
|
||||
import { formatFileSize, formatParameters, formatNumber } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
modelId?: string | null; // Neue Prop für das gewählte Modell
|
||||
}
|
||||
|
||||
let { open = $bindable(), onOpenChange }: Props = $props();
|
||||
let { open = $bindable(), onOpenChange, modelId = null }: Props = $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
|
||||
let modalities = $derived.by(() => {
|
||||
if (!firstModel?.id) return [];
|
||||
return modelsStore.getModelModalitiesArray(firstModel.id);
|
||||
// Finde das spezifische Modell anhand der ID oder nimm das erste als Fallback
|
||||
let selectedModel = $derived.by(() => {
|
||||
if (modelId) {
|
||||
return models.find(m => m.id === modelId) || models[0];
|
||||
}
|
||||
return models[0] ?? null;
|
||||
});
|
||||
|
||||
// Hol die spezifischen Server-Props für dieses Modell aus dem modelStore
|
||||
let serverProps = $derived.by(() => {
|
||||
if (!selectedModel) return null;
|
||||
return modelsStore.getModelProps(selectedModel.model);
|
||||
});
|
||||
|
||||
let modelName = $derived(selectedModel?.model ?? '');
|
||||
|
||||
let modalities = $derived.by(() => {
|
||||
if (!selectedModel?.id) return [];
|
||||
return modelsStore.getModelModalitiesArray(selectedModel.id);
|
||||
});
|
||||
|
||||
// Ensure models are fetched when dialog opens
|
||||
$effect(() => {
|
||||
if (open && models.length === 0) {
|
||||
modelsStore.fetch();
|
||||
|
|
@ -56,8 +65,8 @@
|
|||
<div class="flex items-center justify-center py-8">
|
||||
<div class="text-sm text-muted-foreground">Loading model information...</div>
|
||||
</div>
|
||||
{:else if firstModel}
|
||||
{@const modelMeta = firstModel.meta}
|
||||
{:else if selectedModel}
|
||||
{@const modelMeta = selectedModel.meta}
|
||||
|
||||
{#if serverProps}
|
||||
<Table.Root>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
let activeId = $derived(selectedModelId());
|
||||
let isRouter = $derived(isRouterMode());
|
||||
let serverModel = $derived(singleModelName());
|
||||
let infoModelId = $state<string | null>(null);
|
||||
|
||||
let isHighlightedCurrentModelActive = $derived.by(() => {
|
||||
if (!isRouter || !currentModel) return false;
|
||||
|
|
@ -95,7 +96,6 @@
|
|||
items: { option: ModelOption; flatIndex: number }[];
|
||||
}[] = [];
|
||||
|
||||
// Loaded models group (top)
|
||||
const loadedItems: { option: ModelOption; flatIndex: number }[] = [];
|
||||
for (let i = 0; i < filteredOptions.length; i++) {
|
||||
if (modelsStore.isModelLoaded(filteredOptions[i].model)) {
|
||||
|
|
@ -112,7 +112,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Favourites group
|
||||
const loadedModelIds = new Set(loadedItems.map((item) => item.option.model));
|
||||
const favItems: { option: ModelOption; flatIndex: number }[] = [];
|
||||
for (let i = 0; i < filteredOptions.length; i++) {
|
||||
|
|
@ -130,7 +129,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Org groups (excluding loaded and favourites)
|
||||
const orgGroups = new SvelteMap<string, { option: ModelOption; flatIndex: number }[]>();
|
||||
for (let i = 0; i < filteredOptions.length; i++) {
|
||||
const option = filteredOptions[i];
|
||||
|
|
@ -399,7 +397,6 @@
|
|||
>
|
||||
<div class="models-list">
|
||||
{#if !isCurrentModelInCache && currentModel}
|
||||
<!-- Show unavailable model as first option (disabled) -->
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-not-allowed items-center bg-red-400/10 p-2 text-left text-sm text-red-400"
|
||||
|
|
@ -454,6 +451,11 @@
|
|||
handleSelect(option.id);
|
||||
}
|
||||
}}
|
||||
onShowInfo={async () => {
|
||||
infoModelId = option.id; // ID merken
|
||||
handleOpenChange(false);
|
||||
showModelDialog = true;
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/each}
|
||||
|
|
@ -500,6 +502,9 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
{#if showModelDialog && !isRouter}
|
||||
<DialogModelInformation bind:open={showModelDialog} />
|
||||
{#if showModelDialog}
|
||||
<DialogModelInformation
|
||||
bind:open={showModelDialog}
|
||||
modelId={infoModelId}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { CircleAlert, Heart, HeartOff, Loader2, Power, PowerOff, RotateCw } from '@lucide/svelte';
|
||||
import { CircleAlert, Heart, HeartOff, Loader2, Power, PowerOff, RotateCw, BadgeInfo } from '@lucide/svelte';
|
||||
import { cn } from '$lib/components/ui/utils';
|
||||
import { ActionIcon, ModelId } from '$lib/components/app';
|
||||
import type { ModelOption } from '$lib/types/models';
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
onSelect: (modelId: string) => void;
|
||||
onMouseEnter: () => void;
|
||||
onKeyDown: (e: KeyboardEvent) => void;
|
||||
onShowInfo: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -25,7 +26,8 @@
|
|||
showOrgName = false,
|
||||
onSelect,
|
||||
onMouseEnter,
|
||||
onKeyDown
|
||||
onKeyDown,
|
||||
onShowInfo
|
||||
}: Props = $props();
|
||||
|
||||
let currentRouterModels = $derived(routerModels());
|
||||
|
|
@ -33,6 +35,7 @@
|
|||
const model = currentRouterModels.find((m) => m.id === option.model);
|
||||
return (model?.status?.value as ServerModelStatus) ?? null;
|
||||
});
|
||||
|
||||
let isOperationInProgress = $derived(modelsStore.isModelOperationInProgress(option.model));
|
||||
let isFailed = $derived(serverStatus === ServerModelStatus.FAILED);
|
||||
let isLoaded = $derived(serverStatus === ServerModelStatus.LOADED && !isOperationInProgress);
|
||||
|
|
@ -107,12 +110,17 @@
|
|||
</div>
|
||||
</div>
|
||||
{:else if isLoaded}
|
||||
<div class="flex w-4 items-center justify-center">
|
||||
<span class="h-2 w-2 rounded-full bg-green-500 group-hover:hidden"></span>
|
||||
<div class="flex w-fit items-center justify-center gap-1"> <span class="h-2 w-2 rounded-full bg-green-500 group-hover:hidden"></span>
|
||||
|
||||
<div class="hidden group-hover:flex items-center gap-1" onclick={(e) => e.stopPropagation()}>
|
||||
<ActionIcon
|
||||
iconSize="h-2.5 w-2.5"
|
||||
icon={BadgeInfo}
|
||||
tooltip="Model information"
|
||||
class="h-3 w-3 hover:text-primary"
|
||||
onclick={() => onShowInfo()}
|
||||
/>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div class="hidden group-hover:flex" onclick={(e) => e.stopPropagation()}>
|
||||
<ActionIcon
|
||||
iconSize="h-2.5 w-2.5"
|
||||
icon={PowerOff}
|
||||
|
|
|
|||
Loading…
Reference in New Issue