import { createMemo, createSignal } from "solid-js" import { useLocal } from "@tui/context/local" import { useSync } from "@tui/context/sync" import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda" import { DialogSelect } from "@tui/ui/dialog-select" import { useDialog } from "@tui/ui/dialog" import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { DialogVariant } from "./dialog-variant" import { useKeybind } from "../context/keybind" import * as fuzzysort from "fuzzysort" import { useConnected } from "./use-connected" export function DialogModel(props: { providerID?: string }) { const local = useLocal() const sync = useSync() const dialog = useDialog() const keybind = useKeybind() const [query, setQuery] = createSignal("") const connected = useConnected() const providers = createDialogProviderOptions() const showExtra = createMemo(() => connected() && !props.providerID) const options = createMemo(() => { const needle = query().trim() const showSections = showExtra() && needle.length === 0 const favorites = connected() ? local.model.favorite() : [] const recents = local.model.recent() function toOptions(items: typeof favorites, category: string) { if (!showSections) return [] return items.flatMap((item) => { const provider = sync.data.provider.find((x) => x.id === item.providerID) if (!provider) return [] const model = provider.models[item.modelID] if (!model) return [] return [ { key: item, value: { providerID: provider.id, modelID: model.id }, title: model.name ?? item.modelID, description: provider.name, category, disabled: provider.id === "opencode" && model.id.includes("-nano"), footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, onSelect: () => { onSelect(provider.id, model.id) }, }, ] }) } const favoriteOptions = toOptions(favorites, "Favorites") const recentOptions = toOptions( recents.filter( (item) => !favorites.some((fav) => fav.providerID === item.providerID && fav.modelID === item.modelID), ), "Recent", ) const providerOptions = pipe( sync.data.provider, sortBy( (provider) => provider.id !== "opencode", (provider) => provider.name, ), flatMap((provider) => pipe( provider.models, entries(), filter(([_, info]) => info.status !== "deprecated"), filter(([_, info]) => (props.providerID ? info.providerID === props.providerID : true)), map(([model, info]) => ({ value: { providerID: provider.id, modelID: model }, title: info.name ?? model, description: favorites.some((item) => item.providerID === provider.id && item.modelID === model) ? "(Favorite)" : undefined, category: connected() ? provider.name : undefined, disabled: provider.id === "opencode" && model.includes("-nano"), footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, onSelect() { onSelect(provider.id, model) }, })), filter((x) => { if (!showSections) return true if (favorites.some((item) => item.providerID === x.value.providerID && item.modelID === x.value.modelID)) return false if (recents.some((item) => item.providerID === x.value.providerID && item.modelID === x.value.modelID)) return false return true }), sortBy( (x) => x.footer !== "Free", (x) => x.title, ), ), ), ) const popularProviders = !connected() ? pipe( providers(), map((option) => ({ ...option, category: "Popular providers", })), take(6), ) : [] if (needle) { return [ ...fuzzysort.go(needle, providerOptions, { keys: ["title", "category"] }).map((x) => x.obj), ...fuzzysort.go(needle, popularProviders, { keys: ["title"] }).map((x) => x.obj), ] } return [...favoriteOptions, ...recentOptions, ...providerOptions, ...popularProviders] }) const provider = createMemo(() => props.providerID ? sync.data.provider.find((x) => x.id === props.providerID) : null, ) const title = createMemo(() => { const value = provider() if (!value) return "Select model" return value.name }) function onSelect(providerID: string, modelID: string) { local.model.set({ providerID, modelID }, { recent: true }) const list = local.model.variant.list() const cur = local.model.variant.selected() if (cur === "default" || (cur && list.includes(cur))) { dialog.clear() return } if (list.length > 0) { dialog.replace(() => ) return } dialog.clear() } return ( [number]["value"]> options={options()} keybind={[ { keybind: keybind.all.model_provider_list?.[0], title: connected() ? "Connect provider" : "View all providers", onTrigger() { dialog.replace(() => ) }, }, { keybind: keybind.all.model_favorite_toggle?.[0], title: "Favorite", disabled: !connected(), onTrigger: (option) => { local.model.toggleFavorite(option.value as { providerID: string; modelID: string }) }, }, ]} onFilter={setQuery} flat={true} skipFilter={true} title={title()} current={local.model.current()} /> ) }