141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import { createMemo } from "solid-js"
|
|
import { createStore } from "solid-js/store"
|
|
import { DateTime } from "luxon"
|
|
import { filter, firstBy, flat, groupBy, mapValues, pipe, uniqueBy, values } from "remeda"
|
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
|
import { useProviders } from "@/hooks/use-providers"
|
|
import { Persist, persisted } from "@/utils/persist"
|
|
|
|
export type ModelKey = { providerID: string; modelID: string }
|
|
|
|
type Visibility = "show" | "hide"
|
|
type User = ModelKey & { visibility: Visibility; favorite?: boolean }
|
|
type Store = {
|
|
user: User[]
|
|
recent: ModelKey[]
|
|
variant?: Record<string, string | undefined>
|
|
}
|
|
|
|
export const { use: useModels, provider: ModelsProvider } = createSimpleContext({
|
|
name: "Models",
|
|
init: () => {
|
|
const providers = useProviders()
|
|
|
|
const [store, setStore, _, ready] = persisted(
|
|
Persist.global("model", ["model.v1"]),
|
|
createStore<Store>({
|
|
user: [],
|
|
recent: [],
|
|
variant: {},
|
|
}),
|
|
)
|
|
|
|
const available = createMemo(() =>
|
|
providers.connected().flatMap((p) =>
|
|
Object.values(p.models).map((m) => ({
|
|
...m,
|
|
provider: p,
|
|
})),
|
|
),
|
|
)
|
|
|
|
const latest = createMemo(() =>
|
|
pipe(
|
|
available(),
|
|
filter((x) => Math.abs(DateTime.fromISO(x.release_date).diffNow().as("months")) < 6),
|
|
groupBy((x) => x.provider.id),
|
|
mapValues((models) =>
|
|
pipe(
|
|
models,
|
|
groupBy((x) => x.family),
|
|
values(),
|
|
(groups) =>
|
|
groups.flatMap((g) => {
|
|
const first = firstBy(g, [(x) => x.release_date, "desc"])
|
|
return first ? [{ modelID: first.id, providerID: first.provider.id }] : []
|
|
}),
|
|
),
|
|
),
|
|
values(),
|
|
flat(),
|
|
),
|
|
)
|
|
|
|
const latestSet = createMemo(() => new Set(latest().map((x) => `${x.providerID}:${x.modelID}`)))
|
|
|
|
const visibility = createMemo(() => {
|
|
const map = new Map<string, Visibility>()
|
|
for (const item of store.user) map.set(`${item.providerID}:${item.modelID}`, item.visibility)
|
|
return map
|
|
})
|
|
|
|
const list = createMemo(() =>
|
|
available().map((m) => ({
|
|
...m,
|
|
name: m.name.replace("(latest)", "").trim(),
|
|
latest: m.name.includes("(latest)"),
|
|
})),
|
|
)
|
|
|
|
const find = (key: ModelKey) => list().find((m) => m.id === key.modelID && m.provider.id === key.providerID)
|
|
|
|
function update(model: ModelKey, state: Visibility) {
|
|
const index = store.user.findIndex((x) => x.modelID === model.modelID && x.providerID === model.providerID)
|
|
if (index >= 0) {
|
|
setStore("user", index, { visibility: state })
|
|
return
|
|
}
|
|
setStore("user", store.user.length, { ...model, visibility: state })
|
|
}
|
|
|
|
const visible = (model: ModelKey) => {
|
|
const key = `${model.providerID}:${model.modelID}`
|
|
const state = visibility().get(key)
|
|
if (state === "hide") return false
|
|
if (state === "show") return true
|
|
if (latestSet().has(key)) return true
|
|
const m = find(model)
|
|
if (!m?.release_date || !DateTime.fromISO(m.release_date).isValid) return true
|
|
return false
|
|
}
|
|
|
|
const setVisibility = (model: ModelKey, state: boolean) => {
|
|
update(model, state ? "show" : "hide")
|
|
}
|
|
|
|
const push = (model: ModelKey) => {
|
|
const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
|
|
if (uniq.length > 5) uniq.pop()
|
|
setStore("recent", uniq)
|
|
}
|
|
|
|
const variantKey = (model: ModelKey) => `${model.providerID}/${model.modelID}`
|
|
const getVariant = (model: ModelKey) => store.variant?.[variantKey(model)]
|
|
|
|
const setVariant = (model: ModelKey, value: string | undefined) => {
|
|
const key = variantKey(model)
|
|
if (!store.variant) {
|
|
setStore("variant", { [key]: value })
|
|
return
|
|
}
|
|
setStore("variant", key, value)
|
|
}
|
|
|
|
return {
|
|
ready,
|
|
list,
|
|
find,
|
|
visible,
|
|
setVisibility,
|
|
recent: {
|
|
list: createMemo(() => store.recent),
|
|
push,
|
|
},
|
|
variant: {
|
|
get: getVariant,
|
|
set: setVariant,
|
|
},
|
|
}
|
|
},
|
|
})
|