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 →