|
@@ -1,6 +1,14 @@
|
|
|
import { query } from "@solidjs/router"
|
|
import { query } from "@solidjs/router"
|
|
|
|
|
|
|
|
export const modelCatalogSourceUrl = "https://models.dev/models.json"
|
|
export const modelCatalogSourceUrl = "https://models.dev/models.json"
|
|
|
|
|
+export const modelCatalogPricingUrl = "https://models.dev/api.json"
|
|
|
|
|
+
|
|
|
|
|
+export type ModelCatalogCost = {
|
|
|
|
|
+ input: number
|
|
|
|
|
+ output: number
|
|
|
|
|
+ cacheRead?: number
|
|
|
|
|
+ cacheWrite?: number
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
export type ModelCatalogEntry = {
|
|
export type ModelCatalogEntry = {
|
|
|
id: string
|
|
id: string
|
|
@@ -18,6 +26,7 @@ export type ModelCatalogEntry = {
|
|
|
toolCall: boolean
|
|
toolCall: boolean
|
|
|
attachment: boolean
|
|
attachment: boolean
|
|
|
temperature: boolean
|
|
temperature: boolean
|
|
|
|
|
+ cost?: ModelCatalogCost
|
|
|
weights: { label: string; url: string }[]
|
|
weights: { label: string; url: string }[]
|
|
|
benchmarks: ModelCatalogBenchmark[]
|
|
benchmarks: ModelCatalogBenchmark[]
|
|
|
}
|
|
}
|
|
@@ -46,10 +55,11 @@ export type ModelCatalog = {
|
|
|
|
|
|
|
|
export const getModelCatalog = query(async () => {
|
|
export const getModelCatalog = query(async () => {
|
|
|
"use server"
|
|
"use server"
|
|
|
- const payload = await fetch(modelCatalogSourceUrl)
|
|
|
|
|
- .then((response): Promise<unknown> => (response.ok ? (response.json() as Promise<unknown>) : Promise.resolve()))
|
|
|
|
|
- .catch(() => undefined)
|
|
|
|
|
- return buildModelCatalog(payload)
|
|
|
|
|
|
|
+ const [models, pricing] = await Promise.all([
|
|
|
|
|
+ fetchCatalogPayload(modelCatalogSourceUrl),
|
|
|
|
|
+ fetchCatalogPayload(modelCatalogPricingUrl),
|
|
|
|
|
+ ])
|
|
|
|
|
+ return buildModelCatalog(models, pricing)
|
|
|
}, "getModelCatalog")
|
|
}, "getModelCatalog")
|
|
|
|
|
|
|
|
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
|
|
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
|
|
@@ -99,9 +109,18 @@ export function catalogSlug(value: string) {
|
|
|
.replace(/-{2,}/g, "-")
|
|
.replace(/-{2,}/g, "-")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function buildModelCatalog(payload: unknown): ModelCatalog {
|
|
|
|
|
|
|
+function buildModelCatalog(payload: unknown, pricingPayload?: unknown): ModelCatalog {
|
|
|
|
|
+ const costs = readCatalogCosts(pricingPayload)
|
|
|
const models = (Array.isArray(payload) ? payload : isRecord(payload) ? Object.values(payload) : [])
|
|
const models = (Array.isArray(payload) ? payload : isRecord(payload) ? Object.values(payload) : [])
|
|
|
.flatMap(readModelCatalogEntry)
|
|
.flatMap(readModelCatalogEntry)
|
|
|
|
|
+ .map((model) => ({
|
|
|
|
|
+ ...model,
|
|
|
|
|
+ cost:
|
|
|
|
|
+ costs.get(catalogIdKey(model.id)) ??
|
|
|
|
|
+ costs.get(`${model.lab}/${model.slug}`) ??
|
|
|
|
|
+ costs.get(model.slug) ??
|
|
|
|
|
+ model.cost,
|
|
|
|
|
+ }))
|
|
|
.toSorted((a, b) => a.lab.localeCompare(b.lab) || displayDateTime(b.releaseDate) - displayDateTime(a.releaseDate))
|
|
.toSorted((a, b) => a.lab.localeCompare(b.lab) || displayDateTime(b.releaseDate) - displayDateTime(a.releaseDate))
|
|
|
return {
|
|
return {
|
|
|
models,
|
|
models,
|
|
@@ -142,12 +161,61 @@ function readModelCatalogEntry(value: unknown): ModelCatalogEntry[] {
|
|
|
toolCall: booleanValue(value.tool_call),
|
|
toolCall: booleanValue(value.tool_call),
|
|
|
attachment: booleanValue(value.attachment),
|
|
attachment: booleanValue(value.attachment),
|
|
|
temperature: booleanValue(value.temperature),
|
|
temperature: booleanValue(value.temperature),
|
|
|
|
|
+ cost: readCatalogCost(value.cost),
|
|
|
weights: readCatalogWeights(value.weights),
|
|
weights: readCatalogWeights(value.weights),
|
|
|
benchmarks: readCatalogBenchmarks(value.benchmarks),
|
|
benchmarks: readCatalogBenchmarks(value.benchmarks),
|
|
|
},
|
|
},
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+async function fetchCatalogPayload(url: string) {
|
|
|
|
|
+ return fetch(url)
|
|
|
|
|
+ .then((response): Promise<unknown> => (response.ok ? (response.json() as Promise<unknown>) : Promise.resolve()))
|
|
|
|
|
+ .catch(() => undefined)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function readCatalogCosts(payload: unknown) {
|
|
|
|
|
+ const costs = new Map<string, ModelCatalogCost>()
|
|
|
|
|
+ const add = (model: unknown, provider?: string) => {
|
|
|
|
|
+ if (!isRecord(model)) return
|
|
|
|
|
+ const id = stringValue(model.id)
|
|
|
|
|
+ const cost = readCatalogCost(model.cost)
|
|
|
|
|
+ if (!id || !cost) return
|
|
|
|
|
+ costs.set(catalogIdKey(id), cost)
|
|
|
|
|
+ costs.set(catalogSlug(id), cost)
|
|
|
|
|
+ if (provider && !id.includes("/")) costs.set(`${catalogSlug(provider)}/${catalogSlug(id)}`, cost)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (Array.isArray(payload)) {
|
|
|
|
|
+ payload.forEach((model) => add(model))
|
|
|
|
|
+ return costs
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isRecord(payload)) return costs
|
|
|
|
|
+
|
|
|
|
|
+ Object.entries(payload).forEach(([key, value]) => {
|
|
|
|
|
+ if (!isRecord(value)) return
|
|
|
|
|
+ if (isRecord(value.models)) {
|
|
|
|
|
+ Object.values(value.models).forEach((model) => add(model, stringValue(value.id) ?? key))
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ add(value)
|
|
|
|
|
+ })
|
|
|
|
|
+ return costs
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function readCatalogCost(value: unknown): ModelCatalogCost | undefined {
|
|
|
|
|
+ if (!isRecord(value)) return undefined
|
|
|
|
|
+ const input = numberValue(value.input)
|
|
|
|
|
+ const output = numberValue(value.output)
|
|
|
|
|
+ if (input === undefined || output === undefined) return undefined
|
|
|
|
|
+ return {
|
|
|
|
|
+ input,
|
|
|
|
|
+ output,
|
|
|
|
|
+ cacheRead: numberValue(value.cache_read),
|
|
|
|
|
+ cacheWrite: numberValue(value.cache_write),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function readCatalogLimit(value: unknown) {
|
|
function readCatalogLimit(value: unknown) {
|
|
|
if (!isRecord(value)) return undefined
|
|
if (!isRecord(value)) return undefined
|
|
|
return {
|
|
return {
|
|
@@ -201,6 +269,10 @@ function displayDateTime(value: string | undefined) {
|
|
|
return value ? new Date(value).getTime() || 0 : 0
|
|
return value ? new Date(value).getTime() || 0 : 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function catalogIdKey(value: string) {
|
|
|
|
|
+ return value.split("/").map(catalogSlug).join("/")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
|
}
|
|
}
|