HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { CompareButton } from '@/components/compare/compare-button';4import { CompareTrayBar } from '@/components/compare/compare-tray-bar';5import { QUANTS } from '@/components/hardware/fit-form';6import { FINDER_USE_CASES, FinderForm, type FinderInputs, MODALITIES } from '@/components/intelligence/finder-form';7import { FitCell, Methodology, RankChips } from '@/components/intelligence/bits';8import { TerminalLayout } from '@/components/layout/terminal';9import { Chip, Estimated, OpennessBadge } from '@/components/ui/badges';10import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';11import { EntityLink } from '@/components/ui/entity';12import { Note, PageHeader } from '@/components/ui/section';13import { EmptyState, Unavailable } from '@/components/ui/unavailable';14import { WatchButton } from '@/components/watchlist/watch-button';15import { ApiError, intel, safe } from '@/lib/api';16import { fmtDate, fmtInt, fmtParams, fmtTokens, fmtUsdPerM, num } from '@/lib/format';17import { routes, SITE_NAME, SITE_URL } from '@/lib/site';18import type { FinderPayload } from '@/lib/types';1920export const revalidate = 300;21type SP = Record<string, string | string[] | undefined>;22const one = (v: string | string[] | undefined) => (Array.isArray(v) ? v[0] : v);2324function parse(sp: SP): FinderInputs {25 const custom = num(one(sp.memory_custom));26 const preset = num(one(sp.memory_gb));27 const mods = (Array.isArray(sp.mod) ? sp.mod : sp.mod ? [sp.mod] : one(sp.modalities)?.split(',') ?? []).filter((m) => MODALITIES.includes(m));28 const dep = one(sp.deployment);29 const lic = one(sp.license);30 return {31 useCase: FINDER_USE_CASES.some((u) => u.value === one(sp.use_case)) ? (one(sp.use_case) as string) : '',32 deployment: dep === 'local' || dep === 'api' ? dep : 'any',33 memory: custom !== null && custom > 0 ? custom : preset !== null && preset > 0 ? preset : null,34 quant: QUANTS.some((q) => q.value === one(sp.quant)) ? (one(sp.quant) as string) : '4bit',35 contextMin: num(one(sp.context_min)),36 license: lic === 'commercial' ? 'commercial' : 'any',37 openness: ['open-source', 'open-weights', 'restricted-weights', 'proprietary'].includes(one(sp.openness) ?? '') ? (one(sp.openness) as string) : '',38 maxIn: num(one(sp.max_input_price)),39 maxOut: num(one(sp.max_output_price)),40 modalities: mods,41 };42}43const active = (v: FinderInputs) => [v.useCase, v.deployment !== 'any', v.memory, v.contextMin, v.license !== 'any', v.openness, v.maxIn, v.maxOut, v.modalities.length].filter(Boolean).length;4445const TITLE = 'Find a model — guided search over observed dimensions';46const DESC = 'Tell the atlas what you need — use case, local or API deployment, memory, licence, openness, context, price ceiling, modalities — and it lists the canonical models that satisfy the criteria, with the reasons, their observed dimensions, an estimated local fit and current deployments. Deterministic rules, no composite score, no single winner.';47export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {48 const v = parse(await searchParams);49 const n = active(v);50 const title = n ? `Models for ${[FINDER_USE_CASES.find((u) => u.value === v.useCase)?.label, v.deployment !== 'any' ? v.deployment : null, v.memory ? `${v.memory} GB` : null].filter(Boolean).join(' · ') || 'your criteria'}` : TITLE;51 return { title, description: DESC, alternates: { canonical: routes.findAModel() }, openGraph: { title: `${title} | ${SITE_NAME}`, description: DESC, url: `${SITE_URL}${routes.findAModel()}`, siteName: SITE_NAME }, robots: n ? { index: false, follow: true } : undefined };52}5354export default async function FindAModelPage({ searchParams }: { searchParams: Promise<SP> }) {55 const sp = await searchParams;56 const v = parse(sp);57 const n = active(v);58 let res: FinderPayload | null = null;59 let error: string | null = null;60 if (n > 0) {61 try {62 res = await intel.findAModel({ use_case: v.useCase || undefined, deployment: v.deployment, memory_gb: v.memory ?? undefined, quant: v.deployment === 'local' || v.memory ? v.quant : undefined, context_min: v.contextMin ?? undefined, license: v.license, openness: v.openness || undefined, max_input_price: v.maxIn ?? undefined, max_output_price: v.maxOut ?? undefined, modalities: v.modalities.length ? v.modalities.join(',') : undefined, limit: 40 });63 } catch (e) {64 error = e instanceof ApiError ? e.detail ?? e.message : 'API unreachable';65 }66 }67 const rules = res?.rules ?? (await safe(intel.methodology()))?.find_a_model ?? null;68 const rulesObj = rules && typeof rules === 'object' ? (rules as Record<string, string>) : null;69 const apiMode = v.deployment === 'api';70 const localMode = v.deployment === 'local' || v.memory !== null;7172 const inspector = (73 <div className="space-y-3 text-xs leading-relaxed text-ink-2">74 <p className="font-medium text-ink">Rules (deterministic)</p>75 {rulesObj ? (76 <dl className="space-y-1.5">77 {Object.entries(rulesObj).map(([k, r]) => (78 <div key={k}>79 <dt className="mono text-[11px] text-ink-3">{k}</dt>80 <dd>{r}</dd>81 </div>82 ))}83 </dl>84 ) : (85 <p className="text-ink-3">Rules unavailable from the API.</p>86 )}87 <p>88 <Link href="/methodology" className="link">/methodology</Link> · <Link href="/developers" className="link">GET /find-a-model</Link>89 </p>90 </div>91 );9293 return (94 <TerminalLayout filters={<FinderForm v={v} />} inspector={inspector} filtersTitle="What do you need?" inspectorTitle="Rules" storageKey="aia-inspector-finder" filterCount={n}>95 <PageHeader eyebrow="Model finder" title="Find a model" lede="Describe the need; the atlas applies deterministic rules to observed attributes, current prices and current benchmark results and returns the models that satisfy them — sorted by how many criteria they meet, then best benchmark rank, then release date. No composite score, no single winner." className="pt-4 md:pt-6" />96 <div className="pb-16">97 {n === 0 ? (98 <EmptyState title="Pick at least one criterion">99 Examples: <Link href="/find-a-model?use_case=coding&deployment=local&memory_gb=64" className="link">coding · local · 64 GB</Link>, <Link href="/find-a-model?use_case=vision&deployment=api&max_output_price=5" className="link">vision · API · output ≤ $5</Link>, <Link href="/find-a-model?use_case=long_context&context_min=1000000&license=commercial" className="link">1M context · commercial licence</Link>.100 </EmptyState>101 ) : !res ? (102 <Unavailable what="Model finder" reason={error ?? undefined} />103 ) : (104 <>105 <div className="border-y border-rule py-3">106 <p className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">107 <span className="font-semibold text-ink">108 {fmtInt(res.total)} model{res.total === 1 ? '' : 's'} satisfy {res.matches[0]?.why.length ? 'at least one of' : ''} the criteria109 </span>110 <span className="text-ink-3">No single winner — these are the observed dimensions; the order is criteria met → best rank → release date.</span>111 </p>112 <p className="mt-1.5 flex flex-wrap gap-1.5">113 {Object.entries(res.filters_applied).map(([k, val]) => (114 <Chip key={k}>115 <span className="text-ink-3">{k.replace(/_/g, ' ')}</span> {String(val)}116 </Chip>117 ))}118 </p>119 </div>120 {res.matches.length === 0 ? (121 <EmptyState title="No model satisfies these criteria" className="mt-4">122 Relax a bound (price, memory, context) or drop a modality. Every rule is listed in the Rules pane.123 </EmptyState>124 ) : (125 <DataTable scroll compact className="mt-4">126 <thead>127 <tr>128 <Th>Model</Th>129 <Th>Why</Th>130 <Th>Observed dimensions</Th>131 {localMode && (132 <Th>133 Fit <Estimated className="ml-1 align-middle" />134 </Th>135 )}136 {apiMode && <Th>Deployments</Th>}137 <Th>Ranks</Th>138 <Th className="w-40" aria-label="Actions" />139 </tr>140 </thead>141 <tbody>142 {res.matches.length === 0 && <EmptyRow cols={7} />}143 {res.matches.map((m) => {144 const o = m.observed ?? {};145 const mods = Array.isArray(o.modalities) ? (o.modalities as string[]) : [];146 return (147 <tr key={m.model.id} data-finder-row>148 <Td primary>149 <span className="flex flex-wrap items-center gap-x-2 gap-y-0.5">150 <EntityLink e={m.model} />151 {typeof o.openness === 'string' && <OpennessBadge openness={o.openness} />}152 </span>153 {m.model.organization && <span className="block text-xs text-ink-3">{m.model.organization.name}</span>}154 </Td>155 <Td label="Why" wide>156 <ul className="flex flex-wrap gap-1">157 {m.why.map((w) => (158 <li key={w} className={`rounded-[3px] px-1.5 py-[1px] text-[11px] leading-4 ${/estimate/i.test(w) ? 'border border-dashed border-warning/60 text-warning' : 'bg-positive-soft text-positive'}`}>159 {w}160 </li>161 ))}162 {m.why.length === 0 && <li className="text-xs text-ink-3">—</li>}163 </ul>164 </Td>165 <Td label="Observed" className="tnum text-xs text-ink-2" wide>166 {[167 num(o.parameter_count) !== null ? `${fmtParams(o.parameter_count)} params` : null,168 num(o.context_length) !== null ? `${fmtTokens(o.context_length)} context` : null,169 typeof o.license === 'string' ? o.license : null,170 mods.length ? mods.join(' · ') : null,171 o.reasoning === true ? 'reasoning' : null,172 o.tool_calling === true ? 'tool calling' : null,173 typeof o.release_date === 'string' ? `released ${fmtDate(o.release_date)}` : null,174 num(o.cheapest_input_per_mtok) !== null ? `from ${fmtUsdPerM(o.cheapest_input_per_mtok)} in / ${fmtUsdPerM(o.cheapest_output_per_mtok)} out` : null,175 num(o.providers) !== null ? `${fmtInt(o.providers)} provider${num(o.providers) === 1 ? '' : 's'}` : null,176 ]177 .filter(Boolean)178 .join(' · ') || '—'}179 </Td>180 {localMode && (181 <Td label="Fit (estimated)">182 <FitCell fit={m.estimated_fit} />183 </Td>184 )}185 {apiMode && (186 <Td label="Deployments" className="text-xs">187 {m.deployments && m.deployments.length ? (188 <ul className="space-y-0.5">189 {m.deployments.slice(0, 4).map((d) => (190 <li key={d.id} className="tnum flex flex-wrap gap-x-2">191 <EntityLink e={d.provider} className="text-ink-2" />192 <span className="text-accent-2">193 {fmtUsdPerM(d.prices.input)} / {fmtUsdPerM(d.prices.output)}194 </span>195 </li>196 ))}197 {m.deployments.length > 4 && <li className="text-ink-3">+{m.deployments.length - 4} more</li>}198 </ul>199 ) : (200 <span className="text-ink-3">—</span>201 )}202 </Td>203 )}204 <Td label="Ranks">205 <RankChips ranks={o.benchmark_ranks ?? null} max={3} />206 </Td>207 <Td className="text-right">208 <span className="inline-flex flex-wrap justify-end gap-1">209 <CompareButton e={m.model} size="sm" />210 <WatchButton e={m.model} size="sm" />211 </span>212 </Td>213 </tr>214 );215 })}216 </tbody>217 </DataTable>218 )}219 <Note className="mt-3">Green chips = criteria satisfied by an observed fact; dashed amber = satisfied by an estimate (hardware fit). Ranks are positions inside each benchmark's primary comparability group.</Note>220 <Methodology text={res.note} />221 </>222 )}223 </div>224 <CompareTrayBar />225 </TerminalLayout>226 );227}228