]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
ui: read model modalities from the router model list (#26709)
authorPascal <redacted>
Fri, 7 Aug 2026 10:07:58 +0000 (12:07 +0200)
committerGitHub <redacted>
Fri, 7 Aug 2026 10:07:58 +0000 (12:07 +0200)
* ui: read model modalities from the router model list

The router advertises input modalities for every model, loaded or not.
Reading them at list build time lets the UI accept image and audio
uploads for a model selected through ?model=, which has no /props yet.

* enum

tools/ui/src/lib/stores/models.svelte.ts
tools/ui/src/lib/types/api.d.ts

index 0b4d7b55d9393b5183fd6981f8088d31d33ebd6e..dc6c01db276239ee9005ac56293c0b132487a90b 100644 (file)
@@ -1,7 +1,12 @@
 import { base } from '$app/paths';
 import { SvelteMap, SvelteSet } from 'svelte/reactivity';
 import { toast } from 'svelte-sonner';
-import { ServerModelStatus, ServerModelsSseEventType, ModelModality } from '$lib/enums';
+import {
+       ServerModelStatus,
+       ServerModelsSseEventType,
+       ModelModality,
+       FileTypeCategory
+} from '$lib/enums';
 import { ModelsService } from '$lib/services/models.service';
 import { PropsService } from '$lib/services/props.service';
 import { serverStore, isRouterMode } from '$lib/stores/server.svelte';
@@ -394,6 +399,7 @@ class ModelsStore {
                                model: modelId,
                                description: details?.description,
                                capabilities: rawCapabilities.filter((value: unknown): value is string => Boolean(value)),
+                               modalities: this.buildArchitectureModalities(item.architecture),
                                details: details?.details,
                                meta: item.meta ?? null,
                                parsedId: ModelsService.parseModelId(modelId),
@@ -1000,6 +1006,21 @@ class ModelsStore {
                };
        }
 
+       /** Map the router modalities, the only source available while a model is not loaded. */
+       private buildArchitectureModalities(
+               architecture: ApiModelDataEntry['architecture']
+       ): ModelModalities | undefined {
+               if (!architecture) return undefined;
+
+               const inputs = architecture.input_modalities;
+
+               return {
+                       vision: inputs.includes(FileTypeCategory.IMAGE),
+                       audio: inputs.includes(FileTypeCategory.AUDIO),
+                       video: inputs.includes(FileTypeCategory.VIDEO)
+               };
+       }
+
        clear(): void {
                this.unsubscribeStatus();
                this.statusWaiters.forEach((waiter) => waiter.reject(new Error('Models store cleared')));
index 5421f8b7f35c87a558cec6fc03941c9a6520f3b9..bdfcddfae7049795c13ca9099ceb9f02d5bb245c 100644 (file)
@@ -98,10 +98,21 @@ export interface ApiModelDataEntry {
        aliases?: string[];
        /** Informational tags for this model */
        tags?: string[];
+       /** Modality capabilities, reported by the router for every model regardless of load state */
+       architecture?: ApiModelArchitecture;
        /** Legacy meta field (may be present in older responses) */
        meta?: Record<string, unknown> | null;
 }
 
+/**
+ * Modality capabilities of a model, as advertised by the ROUTER /models endpoint.
+ * Read from the model manifest, so it is available before the model is loaded.
+ */
+export interface ApiModelArchitecture {
+       /** Accepted input modalities, always contains "text" */
+       input_modalities: string[];
+}
+
 /**
  * Load stage reported by the /models/sse feed, in load order.
  */