diff options
Diffstat (limited to 'src/features/chat/ui/ModelSelector.svelte')
| -rw-r--r-- | src/features/chat/ui/ModelSelector.svelte | 46 |
1 files changed, 44 insertions, 2 deletions
diff --git a/src/features/chat/ui/ModelSelector.svelte b/src/features/chat/ui/ModelSelector.svelte index 03acb79..11b9feb 100644 --- a/src/features/chat/ui/ModelSelector.svelte +++ b/src/features/chat/ui/ModelSelector.svelte @@ -1,20 +1,32 @@ <script lang="ts"> - import { joinModelName, modelKeys, modelsForKey, splitModelName } from "../model-select"; + import type { ModelMetadata } from "@dispatch/transport-contract"; + import { isVisionModel, joinModelName, modelKeys, modelsForKey, splitModelName } from "../model-select"; let { models, selected, onSelect, + modelInfo = {}, }: { models: readonly string[]; selected: string; onSelect: (model: string) => void; + /** + * Per-model metadata from `GET /models` (`{ [name]: ModelMetadata }`). + * Used to show a "vision" badge next to models with `vision: true` (they + * natively accept images; others rely on the server's vision handoff). + * Optional โ absent metadata โ no badge (treated as non-vision). + */ + modelInfo?: Readonly<Record<string, ModelMetadata>>; } = $props(); const keys = $derived(modelKeys(models)); const current = $derived(splitModelName(selected)); const keyModels = $derived(modelsForKey(models, current.key)); + // Whether the currently-selected full model name is vision-capable. + const selectedVision = $derived(isVisionModel(modelInfo, selected)); + // Switching key jumps to the first model available under it. function selectKey(key: string): void { const first = modelsForKey(models, key)[0] ?? ""; @@ -24,6 +36,11 @@ function selectModel(model: string): void { onSelect(joinModelName(current.key, model)); } + + // The full `<key>/<model>` name for a model suffix under the current key. + function fullNameFor(modelSuffix: string): string { + return joinModelName(current.key, modelSuffix); + } </script> <div class="flex flex-col gap-2"> @@ -44,7 +61,32 @@ aria-label="Model selector" > {#each keyModels as model (model)} - <option value={model}>{model}</option> + <option value={model}> + {model}{#if isVisionModel(modelInfo, fullNameFor(model))} ยท vision{/if} + </option> {/each} </select> + {#if selectedVision} + <div class="flex items-center gap-1 text-xs text-base-content/60"> + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + stroke-width="2" + stroke-linecap="round" + stroke-linejoin="round" + class="h-3.5 w-3.5" + aria-hidden="true" + > + <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> + <circle cx="12" cy="12" r="3"></circle> + </svg> + <span>Vision โ this model sees images natively</span> + </div> + {:else} + <div class="text-xs text-base-content/40"> + Pasted images are auto-described (vision handoff) + </div> + {/if} </div> |
