SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
5.8 KB · 145 lines typescript
Raw Blame History
1import type { CompiledFilter, CompiledQuery2 } from '@/lib/types';23/*4  Search compiler v2 helpers (server-safe, pure). Two ways to "drop a chip":5  - re-query: remove the chip's `source_span` from the text and search again (works for every filter);6  - structured: map the remaining compiled filters to `/models?…` parameters when the compiled type is `model`.7  Filters the listing API cannot express are reported, never silently dropped.8*/910export function withoutSpan(text: string, span: string | null | undefined): string {11  if (!span) return text;12  const i = text.toLowerCase().indexOf(span.toLowerCase());13  if (i < 0) return text;14  return `${text.slice(0, i)} ${text.slice(i + span.length)}`.replace(/\s{2,}/g, ' ').trim();15}1617export type Mapped = { params: URLSearchParams; unmapped: { filter: CompiledFilter; why: string }[]; links: { label: string; href: string }[] };1819const SORT_MAP: Record<string, { sort: string; order?: string }> = { newest: { sort: 'release' }, largest: { sort: 'params' }, smallest: { sort: 'params', order: 'asc' }, cheapest: { sort: 'cheapest' }, best: { sort: 'quality' } };2021/** Map compiled filters to `/models` query parameters (docs/API.md `/models`). */22export function toModelsParams(compiled: CompiledFilter[], residual?: string | null): Mapped {23  const p = new URLSearchParams();24  const unmapped: Mapped['unmapped'] = [];25  const links: Mapped['links'] = [];26  const slugOf = (v: unknown) => (v && typeof v === 'object' && 'slug' in (v as Record<string, unknown>) ? String((v as { slug: unknown }).slug) : typeof v === 'string' ? v : null);27  for (const f of compiled) {28    const v = f.value;29    switch (f.filter) {30      case 'entity_type':31        break;32      case 'params_min':33        p.set('min_params', String(v));34        break;35      case 'params_max':36        p.set('max_params', String(v));37        break;38      case 'params_range':39        if (Array.isArray(v) && v.length === 2) {40          p.set('min_params', String(v[0]));41          p.set('max_params', String(v[1]));42        }43        break;44      case 'context_min':45        p.set('min_context', String(v));46        break;47      case 'year':48        p.set('year_from', String(v));49        p.set('year_to', String(v));50        break;51      case 'year_from':52        p.set('year_from', String(v));53        break;54      case 'year_to':55        p.set('year_to', String(v));56        break;57      case 'license':58        p.set('license', String(v));59        break;60      case 'modality':61        p.set('modality', String(v));62        break;63      case 'openness':64        p.set('openness', String(v));65        break;66      case 'organization': {67        const s = slugOf(v);68        if (s) p.set('org', s);69        else unmapped.push({ filter: f, why: 'organization not resolved to a slug' });70        break;71      }72      case 'family': {73        const s = slugOf(v);74        if (s) p.set('family', s);75        break;76      }77      case 'reasoning':78        p.set('reasoning', v ? '1' : '0');79        break;80      case 'status':81        p.set('status', String(v));82        break;83      case 'sort': {84        const m = SORT_MAP[String(v)];85        if (m) {86          p.set('sort', m.sort);87          if (m.order) p.set('order', m.order);88        } else unmapped.push({ filter: f, why: `sort “${String(v)}” has no /models equivalent` });89        break;90      }91      case 'benchmark': {92        const s = slugOf(v);93        if (s) links.push({ label: `${f.label} → leaderboard`, href: `/benchmarks/${encodeURIComponent(s)}` });94        break;95      }96      case 'provider': {97        const s = slugOf(v);98        if (s) links.push({ label: `${f.label} → provider page`, href: `/providers/${encodeURIComponent(s)}` });99        break;100      }101      case 'max_input_price':102      case 'max_output_price':103        unmapped.push({ filter: f, why: '/models has no price filter — use Prices sorted cheapest' });104        links.push({ label: 'Prices, cheapest first', href: `/prices?sort=${f.filter === 'max_input_price' ? 'input' : 'output'}` });105        break;106      case 'memory_gb':107        unmapped.push({ filter: f, why: 'an ESTIMATED parameter bound — use Run locally for a real fit' });108        links.push({ label: `Run locally with ${String(v)} GB`, href: `/run-locally?memory_gb=${encodeURIComponent(String(v))}` });109        break;110      case 'days_back':111        unmapped.push({ filter: f, why: '/models filters by release year, not by a rolling window' });112        break;113      case 'commercial_use':114        unmapped.push({ filter: f, why: 'licence permissions are not a /models filter — see Licenses' });115        links.push({ label: 'Licenses (commercial use)', href: '/licenses' });116        break;117      default:118        unmapped.push({ filter: f, why: 'no /models equivalent' });119    }120  }121  if (residual && residual.trim()) p.set('q', residual.trim());122  return { params: p, unmapped, links };123}124125/** Which model columns the query implies (always Model · Org; the rest depends on the compiled filters). */126export function impliedColumns(compiled: CompiledFilter[], sort?: string | null): Set<string> {127  const cols = new Set<string>(['params', 'context', 'openness', 'released']);128  for (const f of compiled) {129    if (/params|memory_gb/.test(f.filter)) cols.add('params');130    if (/context/.test(f.filter)) cols.add('context');131    if (/price/.test(f.filter)) cols.add('price');132    if (/openness|license|commercial/.test(f.filter)) cols.add('openness');133    if (/year|days_back/.test(f.filter)) cols.add('released');134    if (f.filter === 'reasoning') cols.add('reasoning');135    if (f.filter === 'modality') cols.add('modalities');136  }137  if (sort === 'cheapest') cols.add('price');138  return cols;139}140141export function compiledType(q: CompiledQuery2 | undefined): string | null {142  const t = (q?.entity_type ?? q?.type) as string | null | undefined;143  return t ?? null;144}145