Skip to content

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

// model-picker — C# port: filter + rank the AI model catalog.
using System;
using System.Collections.Generic;
using System.Linq;

// Pricing projection of the TS AiModel record — nullables play the role of
// TS null (unpriced model / unknown release date).
record Model(string Id, string Tier, long ContextWindow,
             double? InputPerM, double? OutputPerM, string? Released);

// Projection of the TS ModelFilter — the three constraints the presets use.
record ModelFilter(string? Tier = null, long? MinContext = null, double? MaxInputPerM = null);

static class ModelPicker
{
    // Curated entry points: a filter + the sort that makes that filter useful.
    public static readonly Dictionary<string, (ModelFilter Filter, string Sort)> Presets = new()
    {
        ["long-context"] = (new(MinContext: 500_000), "context"),
        ["cheap-bulk"] = (new(MaxInputPerM: 1), "price"),
        ["flagship"] = (new(Tier: "flagship"), "tokensPerDollar"),
    };

    // Output tokens per USD: 1e6 / OutputPerM, or null when unpriced.
    static double? TokensPerDollar(Model m) => m.OutputPerM is { } o ? 1e6 / o : null;

    // Filter step, inlined from listModels in src/lib/ai/models.ts (the
    // pricing snapshot accessor): null prices never satisfy MaxInputPerM.
    public static IEnumerable<Model> ListModels(IEnumerable<Model> models, ModelFilter? f = null) =>
        models.Where(m =>
            (f?.Tier == null || m.Tier == f.Tier) &&
            (f?.MinContext == null || m.ContextWindow >= f.MinContext) &&
            (f?.MaxInputPerM == null ||
                (m.InputPerM != null && m.InputPerM <= f.MaxInputPerM)));

    // Sort keys — the TS SortKey union. Nulls always sort last, id asc on
    // ties: nullsLast on an ascending key, nullsSmallest inverted by
    // OrderByDescending (null/negative infinity sinks to the end naturally).
    public static IEnumerable<Model> PickModels(IEnumerable<Model> models,
                                               ModelFilter? f = null, string sort = "price") =>
        sort switch
        {
            "context" => ListModels(models, f)
                .OrderByDescending(m => m.ContextWindow).ThenBy(m => m.Id),
            "tokensPerDollar" => ListModels(models, f)
                .OrderByDescending(m => TokensPerDollar(m) ?? double.NegativeInfinity)
                .ThenBy(m => m.Id),
            "released" => ListModels(models, f)
                .OrderByDescending(m => m.Released).ThenBy(m => m.Id),
            _ => ListModels(models, f)
                .OrderBy(m => m.InputPerM ?? double.MaxValue).ThenBy(m => m.Id),
        };

    public static void Main()
    {
        var models = new[]
        {
            new Model("atlas-flagship", "flagship", 1_000_000, 3.0, 15.0, "2025-03-01"),
            new Model("atlas-mini", "fast", 200_000, 0.5, 2.0, "2025-01-15"),
            new Model("nova-open", "balanced", 1_000_000, null, null, null), // unpriced
            new Model("zeta-budget", "budget", 128_000, 0.5, 1.0, "2024-12-01"),
        };

        var (filter, sort) = Presets["long-context"];
        Console.WriteLine($"long-context (minContext >= {filter.MinContext}, sort {sort}):");
        foreach (var m in PickModels(models, filter, sort))
            Console.WriteLine($"  {m.Id,-14}ctx {m.ContextWindow}");

        Console.WriteLine("price sort (all models, unpriced last, ties by id):");
        foreach (var m in PickModels(models))
            Console.WriteLine(m.InputPerM is { } p
                ? $"  {m.Id,-14}${p:F2}/M in" : $"  {m.Id,-14}unpriced");
    }
}

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 →