Skip to content

Model Picker — TypeScript 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 TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

/**
 * Model Picker — filter + rank the AI model snapshot.
 *
 * A thin sorting/ranking layer over `listModels` (src/lib/ai/models.ts): the
 * filter contract, fixtures, and pricing fields all come from the accessor,
 * which stays the single source of truth. Derived values are computed here,
 * never stored.
 *
 * Every sort places null/missing values last and breaks ties on id ascending,
 * so output order is fully deterministic for a given snapshot.
 */
import { listModels, type AiModel, type ModelFilter } from './ai/models';

export type SortKey = 'price' | 'context' | 'tokensPerDollar' | 'released';

/** Curated entry points: a filter + the sort that makes that filter useful. */
export const PRESETS: Record<
  'long-context' | 'cheap-bulk' | 'flagship',
  { filter: ModelFilter; sort: SortKey }
> = {
  '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: AiModel, b: AiModel): number => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);

/** Sort comparator per key: value order first, nulls last, id asc on ties. */
const COMPARATORS: Record<SortKey, (a: AiModel, b: AiModel) => number> = {
  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, then sort it. See COMPARATORS for the exact order. */
export function pickModels(
  filter: ModelFilter,
  sort: SortKey = 'price',
  models?: readonly AiModel[],
): AiModel[] {
  return listModels(filter, models).slice().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 →