models.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { createMemo } from "solid-js"
  2. import { createStore } from "solid-js/store"
  3. import { DateTime } from "luxon"
  4. import { filter, firstBy, flat, groupBy, mapValues, pipe, uniqueBy, values } from "remeda"
  5. import { createSimpleContext } from "@opencode-ai/ui/context"
  6. import { useProviders } from "@/hooks/use-providers"
  7. import { Persist, persisted } from "@/utils/persist"
  8. export type ModelKey = { providerID: string; modelID: string }
  9. type Visibility = "show" | "hide"
  10. type User = ModelKey & { visibility: Visibility; favorite?: boolean }
  11. type Store = {
  12. user: User[]
  13. recent: ModelKey[]
  14. variant?: Record<string, string | undefined>
  15. }
  16. export const { use: useModels, provider: ModelsProvider } = createSimpleContext({
  17. name: "Models",
  18. init: () => {
  19. const providers = useProviders()
  20. const [store, setStore, _, ready] = persisted(
  21. Persist.global("model", ["model.v1"]),
  22. createStore<Store>({
  23. user: [],
  24. recent: [],
  25. variant: {},
  26. }),
  27. )
  28. const available = createMemo(() =>
  29. providers.connected().flatMap((p) =>
  30. Object.values(p.models).map((m) => ({
  31. ...m,
  32. provider: p,
  33. })),
  34. ),
  35. )
  36. const latest = createMemo(() =>
  37. pipe(
  38. available(),
  39. filter((x) => Math.abs(DateTime.fromISO(x.release_date).diffNow().as("months")) < 6),
  40. groupBy((x) => x.provider.id),
  41. mapValues((models) =>
  42. pipe(
  43. models,
  44. groupBy((x) => x.family),
  45. values(),
  46. (groups) =>
  47. groups.flatMap((g) => {
  48. const first = firstBy(g, [(x) => x.release_date, "desc"])
  49. return first ? [{ modelID: first.id, providerID: first.provider.id }] : []
  50. }),
  51. ),
  52. ),
  53. values(),
  54. flat(),
  55. ),
  56. )
  57. const latestSet = createMemo(() => new Set(latest().map((x) => `${x.providerID}:${x.modelID}`)))
  58. const visibility = createMemo(() => {
  59. const map = new Map<string, Visibility>()
  60. for (const item of store.user) map.set(`${item.providerID}:${item.modelID}`, item.visibility)
  61. return map
  62. })
  63. const list = createMemo(() =>
  64. available().map((m) => ({
  65. ...m,
  66. name: m.name.replace("(latest)", "").trim(),
  67. latest: m.name.includes("(latest)"),
  68. })),
  69. )
  70. const find = (key: ModelKey) => list().find((m) => m.id === key.modelID && m.provider.id === key.providerID)
  71. function update(model: ModelKey, state: Visibility) {
  72. const index = store.user.findIndex((x) => x.modelID === model.modelID && x.providerID === model.providerID)
  73. if (index >= 0) {
  74. setStore("user", index, { visibility: state })
  75. return
  76. }
  77. setStore("user", store.user.length, { ...model, visibility: state })
  78. }
  79. const visible = (model: ModelKey) => {
  80. const key = `${model.providerID}:${model.modelID}`
  81. const state = visibility().get(key)
  82. if (state === "hide") return false
  83. if (state === "show") return true
  84. if (latestSet().has(key)) return true
  85. const m = find(model)
  86. if (!m?.release_date || !DateTime.fromISO(m.release_date).isValid) return true
  87. return false
  88. }
  89. const setVisibility = (model: ModelKey, state: boolean) => {
  90. update(model, state ? "show" : "hide")
  91. }
  92. const push = (model: ModelKey) => {
  93. const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
  94. if (uniq.length > 5) uniq.pop()
  95. setStore("recent", uniq)
  96. }
  97. const variantKey = (model: ModelKey) => `${model.providerID}/${model.modelID}`
  98. const getVariant = (model: ModelKey) => store.variant?.[variantKey(model)]
  99. const setVariant = (model: ModelKey, value: string | undefined) => {
  100. const key = variantKey(model)
  101. if (!store.variant) {
  102. setStore("variant", { [key]: value })
  103. return
  104. }
  105. setStore("variant", key, value)
  106. }
  107. return {
  108. ready,
  109. list,
  110. find,
  111. visible,
  112. setVisibility,
  113. recent: {
  114. list: createMemo(() => store.recent),
  115. push,
  116. },
  117. variant: {
  118. get: getVariant,
  119. set: setVariant,
  120. },
  121. }
  122. },
  123. })