Skip to content

Model Picker — JavaScript source

Filter every major model by context window, price, modality, and tier — sort by cost, context, or tokens-per-dollar to find the right model for the task.

This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

/**
 * Model Picker - filter + rank the AI model catalog.
 *
 * Language:   JavaScript (ES2020+, ES module; runs unmodified in Node 16+
 *             and modern browsers)
 * Source:     CosmoDev polyglot showcase port of the Model Picker tool,
 *             ported from src/lib/modelPicker.ts (the canonical TypeScript
 *             implementation).
 * Live page:  https://dev.cosmolabs.org/tools/model-picker
 * License:    display source - part of CosmoDev's polyglot tool pages.
 *
 * Design goals:
 *   - Pure + deterministic; never throws.
 *   - Functionally equivalent to the TS reference: same inputs -> same outputs.
 *   - Self-contained: no imports, no snapshot fetch.
 *
 * Port notes: the TS lib imports `listModels` from src/lib/ai/models.ts and
 * lets `models` default to the bundled pricing snapshot (src/data/ai-models.json).
 * A dependency-free port cannot load that file, so the filter step is inlined
 * below and `models` is an explicit parameter (defaulting to an empty list).
 *
 * Every sort places null/missing values last and breaks ties on id ascending,
 * so output order is fully deterministic for a given model list.
 */

/** Curated entry points: a filter + the sort that makes that filter useful. */
export const PRESETS = {
  'long-context': { filter: { minContext: 500_000 }, sort: 'context' },
  'cheap-bulk': { filter: { maxInputPerM: 1 }, sort: 'price' },
  flagship: { filter: { tier: 'flagship' }, sort: 'tokensPerDollar' },
};

/** id ascending - the shared stable tie-break for every sort. */
const byId = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);

/** Sort comparator per key: value order first, null last, id asc on ties. */
const COMPARATORS = {
  price: (a, b) => {
    if (a.inputPerM === null && b.inputPerM === null) return byId(a, b);
    if (a.inputPerM === null) return 1;
    if (b.inputPerM === null) return -1;
    return a.inputPerM - b.inputPerM || byId(a, b);
  },
  context: (a, b) => b.contextWindow - a.contextWindow || byId(a, b),
  tokensPerDollar: (a, b) => {
    const ta = a.outputPerM === null ? null : 1_000_000 / a.outputPerM;
    const tb = b.outputPerM === null ? null : 1_000_000 / b.outputPerM;
    if (ta === null && tb === null) return byId(a, b);
    if (ta === null) return 1;
    if (tb === null) return -1;
    return tb - ta || byId(a, b);
  },
  released: (a, b) => {
    if (a.released === null && b.released === null) return byId(a, b);
    if (a.released === null) return 1;
    if (b.released === null) return -1;
    if (a.released === b.released) return byId(a, b);
    return a.released > b.released ? -1 : 1;
  },
};

/** Filter the model list. Inline port of listModels() from
 *  src/lib/ai/models.ts: `null` prices never satisfy maxInputPerM, search is
 *  a case-insensitive substring match over id + name + vendor. */
export function listModels(filter = {}, models = []) {
  const q = filter.search?.toLowerCase();
  return models.filter((m) => {
    if (filter.vendor !== undefined && m.vendor !== filter.vendor) return false;
    if (filter.tier !== undefined && m.tier !== filter.tier) return false;
    if (filter.minContext !== undefined && m.contextWindow < filter.minContext) return false;
    if (
      filter.maxInputPerM !== undefined &&
      (m.inputPerM === null || m.inputPerM > filter.maxInputPerM)
    )
      return false;
    if (filter.modality !== undefined && !m.modalities.includes(filter.modality)) return false;
    if (q !== undefined && !(m.id + ' ' + m.name + ' ' + m.vendor).toLowerCase().includes(q))
      return false;
    return true;
  });
}

/** Filter the model list, then sort it. See COMPARATORS for the exact order. */
export function pickModels(filter = {}, sort = 'price', models = []) {
  return listModels(filter, models).sort(COMPARATORS[sort]);
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →