diff --git a/tools/ui/src/lib/stores/models.svelte.ts b/tools/ui/src/lib/stores/models.svelte.ts index 0b4d7b55d9..dc6c01db27 100644 --- a/tools/ui/src/lib/stores/models.svelte.ts +++ b/tools/ui/src/lib/stores/models.svelte.ts @@ -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'))); diff --git a/tools/ui/src/lib/types/api.d.ts b/tools/ui/src/lib/types/api.d.ts index 5421f8b7f3..bdfcddfae7 100644 --- a/tools/ui/src/lib/types/api.d.ts +++ b/tools/ui/src/lib/types/api.d.ts @@ -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 | 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. */