mirror of
https://github.com/LostRuins/koboldcpp.git
synced 2026-09-08 05:49:10 +02:00
cae63579b6
* ui : strip trailing container-format segments from parsed model names * ui : show reasoning and modality icons on model options and search by modality * ui : keep reasoning submenu visible regardless of model state * ui : add show-org-name-in-trigger display setting * ui : move model list into a submenu within the model selector * ui : make model option hover and focus highlight override the active state * ui : add raw model id tooltip to model selector options * feat: Enable microphone input as default for audio models * ui : fix eslint issues in chat form and model selector * ui: show modality icons instead of file submenu in chat add menu Assisted-by: pi * chore: Format * chore: Format * ui: add ModelCapability enum and shared modality/capability icon constants Assisted by: pi:GLM-5.3-Flash * ui: derive modality badge icons and labels from shared constants Assisted by: pi:GLM-5.3-Flash * ui: split model option icons into capabilities and modalities Replace the supportsThinking flag on ModelId with a capabilities object keyed like ModelModalities, so future capabilities (tool calls, etc.) slot in alongside reasoning. Icons and labels now come from the shared CAPABILITY_ICONS/MODALITY_ICONS constants. Assisted by: pi:GLM-5.3-Flash
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import { ModelModality } from '$lib/enums';
|
|
import type { ModelOption } from '$lib/types/models';
|
|
import { SvelteMap } from 'svelte/reactivity';
|
|
|
|
export interface ModelItem {
|
|
option: ModelOption;
|
|
flatIndex: number;
|
|
}
|
|
|
|
export interface OrgGroup {
|
|
orgName: string | null;
|
|
items: ModelItem[];
|
|
}
|
|
|
|
export interface GroupedModelOptions {
|
|
loaded: ModelItem[];
|
|
favorites: ModelItem[];
|
|
available: OrgGroup[];
|
|
}
|
|
|
|
function matchesModality(option: ModelOption, term: string): boolean {
|
|
const modalities = option.modalities;
|
|
|
|
if (!modalities) return false;
|
|
|
|
switch (term) {
|
|
case ModelModality.VISION.toLowerCase():
|
|
return modalities.vision;
|
|
case ModelModality.AUDIO.toLowerCase():
|
|
return modalities.audio;
|
|
case ModelModality.VIDEO.toLowerCase():
|
|
return modalities.video;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function filterModelOptions(options: ModelOption[], searchTerm: string): ModelOption[] {
|
|
const term = searchTerm.trim().toLowerCase();
|
|
|
|
if (!term) return options;
|
|
|
|
return options.filter(
|
|
(option) =>
|
|
option.model.toLowerCase().includes(term) ||
|
|
option.name?.toLowerCase().includes(term) ||
|
|
option.aliases?.some((alias: string) => alias.toLowerCase().includes(term)) ||
|
|
option.tags?.some((tag: string) => tag.toLowerCase().includes(term)) ||
|
|
matchesModality(option, term)
|
|
);
|
|
}
|
|
|
|
export function groupModelOptions(
|
|
filteredOptions: ModelOption[],
|
|
favoriteIds: Set<string>,
|
|
isModelLoaded: (model: string) => boolean
|
|
): GroupedModelOptions {
|
|
// Loaded models
|
|
const loaded: ModelItem[] = [];
|
|
|
|
for (let i = 0; i < filteredOptions.length; i++) {
|
|
if (isModelLoaded(filteredOptions[i].model)) {
|
|
loaded.push({ flatIndex: i, option: filteredOptions[i] });
|
|
}
|
|
}
|
|
|
|
// Favorites (excluding loaded)
|
|
const loadedModelIds = new Set(loaded.map((item) => item.option.model));
|
|
const favorites: ModelItem[] = [];
|
|
|
|
for (let i = 0; i < filteredOptions.length; i++) {
|
|
if (
|
|
favoriteIds.has(filteredOptions[i].model) &&
|
|
!loadedModelIds.has(filteredOptions[i].model)
|
|
) {
|
|
favorites.push({ flatIndex: i, option: filteredOptions[i] });
|
|
}
|
|
}
|
|
|
|
// Available models grouped by org (excluding loaded and favorites)
|
|
const available: OrgGroup[] = [];
|
|
const orgGroups = new SvelteMap<string, ModelItem[]>();
|
|
|
|
for (let i = 0; i < filteredOptions.length; i++) {
|
|
const option = filteredOptions[i];
|
|
|
|
if (loadedModelIds.has(option.model) || favoriteIds.has(option.model)) continue;
|
|
|
|
const key = option.parsedId?.orgName ?? '';
|
|
|
|
if (!orgGroups.has(key)) orgGroups.set(key, []);
|
|
|
|
orgGroups.get(key)!.push({ flatIndex: i, option });
|
|
}
|
|
|
|
for (const [orgName, items] of orgGroups) {
|
|
available.push({ items, orgName: orgName || null });
|
|
}
|
|
|
|
return { available, favorites, loaded };
|
|
}
|