import { MagnifyingGlassIcon } from "@heroicons/react/20/solid"; import { Form, useFetcher, Link } from "@remix-run/react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { Button, LinkButton } from "~/components/primitives/Buttons"; import { Input } from "~/components/primitives/Input"; import { PaginationControls } from "~/components/primitives/Pagination"; import { Paragraph } from "~/components/primitives/Paragraph"; import { Table, TableBlankRow, TableBody, TableCell, TableHeader, TableHeaderCell, TableRow, } from "~/components/primitives/Table"; import { prisma } from "~/db.server"; import { dashboardAction, dashboardLoader } from "~/services/routeBuilders/dashboardBuilder"; import { createSearchParams } from "~/utils/searchParams"; import { seedLlmPricing, syncLlmCatalog } from "@internal/llm-model-catalog"; import { llmPricingRegistry } from "~/v3/llmPricingRegistry.server"; const PAGE_SIZE = 50; const SearchParams = z.object({ page: z.coerce.number().optional(), search: z.string().optional(), }); export const loader = dashboardLoader( { authorization: { requireSuper: true } }, async ({ request }) => { const searchParams = createSearchParams(request.url, SearchParams); if (!searchParams.success) throw new Error(searchParams.error); const { page: rawPage, search } = searchParams.params.getAll(); const page = rawPage ?? 1; const where = { projectId: null as string | null, ...(search ? { modelName: { contains: search, mode: "insensitive" as const } } : {}), }; const [rawModels, total] = await Promise.all([ prisma.llmModel.findMany({ where, include: { pricingTiers: { include: { prices: true }, orderBy: { priority: "asc" } }, }, orderBy: { modelName: "asc" }, skip: (page - 1) * PAGE_SIZE, take: PAGE_SIZE, }), prisma.llmModel.count({ where }), ]); // Convert Prisma Decimal to plain numbers for serialization const models = rawModels.map((m) => ({ ...m, pricingTiers: m.pricingTiers.map((t) => ({ ...t, prices: t.prices.map((p) => ({ ...p, price: Number(p.price) })), })), })); return typedjson({ models, total, page, pageCount: Math.ceil(total / PAGE_SIZE), filters: { search }, }); } ); export const action = dashboardAction( { authorization: { requireSuper: true } }, async ({ request }) => { const formData = await request.formData(); const _action = formData.get("_action"); if (_action === "seed") { console.log("[admin] seed action started"); const result = await seedLlmPricing(prisma); console.log( `[admin] seed complete: ${result.modelsCreated} created, ${result.modelsSkipped} skipped, ${result.modelsUpdated} updated` ); await llmPricingRegistry?.reload(); console.log("[admin] registry reloaded after seed"); return typedjson({ success: true, message: `Seeded: ${result.modelsCreated} created, ${result.modelsSkipped} skipped, ${result.modelsUpdated} updated`, }); } if (_action === "sync") { console.log("[admin] sync catalog action started"); const result = await syncLlmCatalog(prisma); console.log( `[admin] sync complete: ${result.modelsUpdated} updated, ${result.modelsSkipped} skipped` ); await llmPricingRegistry?.reload(); console.log("[admin] registry reloaded after sync"); return typedjson({ success: true, message: `Synced: ${result.modelsUpdated} updated, ${result.modelsSkipped} skipped`, }); } if (_action === "reload") { console.log("[admin] reload action started"); await llmPricingRegistry?.reload(); console.log("[admin] registry reloaded"); return typedjson({ success: true, message: "Registry reloaded" }); } if (_action === "test") { const modelString = formData.get("modelString"); if (typeof modelString !== "string" || !modelString) { return typedjson({ testResult: null }); } // Use the registry's match() which handles prefix stripping automatically const matched = llmPricingRegistry?.match(modelString) ?? null; return typedjson({ testResult: { modelString, match: matched ? { friendlyId: matched.friendlyId, modelName: matched.modelName } : null, }, }); } if (_action === "delete") { const modelId = formData.get("modelId"); if (typeof modelId === "string") { await prisma.llmModel.delete({ where: { id: modelId } }); await llmPricingRegistry?.reload(); } return typedjson({ success: true }); } return typedjson({ error: "Unknown action" }, { status: 400 }); } ); export default function AdminLlmModelsRoute() { const { models, filters, page, pageCount, total } = useTypedLoaderData(); const seedFetcher = useFetcher(); const syncFetcher = useFetcher(); const reloadFetcher = useFetcher(); const testFetcher = useFetcher<{ testResult?: { modelString: string; match: { friendlyId: string; modelName: string } | null; } | null; }>(); const testResult = testFetcher.data?.testResult; return (
Missing models Add model
{/* Model tester */}
{testResult !== undefined && testResult !== null && (
Testing:{" "} {testResult.modelString} {testResult.match ? (
Match:{" "} {testResult.match.modelName}
) : (
No match found — this model has no pricing data
)}
)}
{total} global models (page {page} of {pageCount})
Model Name Source Input $/tok Output $/tok Other prices {models.length === 0 ? ( No models found ) : ( models.map((model) => { // Get default tier prices const defaultTier = model.pricingTiers.find((t) => t.isDefault) ?? model.pricingTiers[0]; const priceMap = defaultTier ? Object.fromEntries(defaultTier.prices.map((p) => [p.usageType, p.price])) : {}; const inputPrice = priceMap["input"]; const outputPrice = priceMap["output"]; const otherPrices = defaultTier ? defaultTier.prices.filter( (p) => p.usageType !== "input" && p.usageType !== "output" ) : []; return ( {model.modelName} {model.source ?? "default"} {inputPrice != null ? formatPrice(inputPrice) : "-"} {outputPrice != null ? formatPrice(outputPrice) : "-"} {otherPrices.length > 0 ? ( p.usageType).join(", ")} > +{otherPrices.length} more ) : ( - )} ); }) )}
); } /** Format a per-token price as $/M tokens for readability */ function formatPrice(perToken: number): string { const perMillion = perToken * 1_000_000; if (perMillion >= 1) return `$${perMillion.toFixed(2)}/M`; if (perMillion >= 0.01) return `$${perMillion.toFixed(4)}/M`; return `$${perMillion.toFixed(6)}/M`; }