Skip to content

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

// model-picker — Zig port: filter + rank the AI model catalog.
const std = @import("std");
/// Pricing projection of the TS AiModel record; optionals play TS null.
pub const Model = struct {
    id: []const u8,
    tier: []const u8,
    context_window: i64, // context window in tokens.
    input_per_m: ?f64,  // USD per 1M input tokens; null = unpriced.
    output_per_m: ?f64, // USD per 1M output tokens; null = unpriced.
};
/// Projection of the TS ModelFilter — the three constraints the presets use.
/// Null prices never satisfy max_input_per_m.
pub const ModelFilter = struct {
    tier: ?[]const u8 = null,
    min_context: ?i64 = null,
    max_input_per_m: ?f64 = null,
};
/// Sort keys: the TS SortKey union. tokensPerDollar and released follow the
/// same pattern (rank 1e6 / output_per_m, or the date, descending).
pub const SortKey = enum { price, context };

/// Order of two optional values: nulls last, real values compared directly.
fn optOrder(comptime T: type, a: ?T, b: ?T) std.math.Order {
    if (a == null) return if (b == null) .eq else .gt;
    if (b == null) return .lt;
    return std.math.order(a.?, b.?);
}

/// Strict "a before b" test — value order first, null last, id asc on ties.
/// Full ordering keeps std.mem.sort (unstable) deterministic — the TS COMPARATORS map.
fn lessThan(key: SortKey, a: Model, b: Model) bool {
    const ord: std.math.Order = if (key == .price)
        optOrder(f64, a.input_per_m, b.input_per_m)
    else
        std.math.order(a.context_window, b.context_window).invert();
    if (ord == .eq) return std.mem.lessThan(u8, a.id, b.id);
    return ord == .lt;
}

/// Filter step, inlined from listModels in src/lib/ai/models.ts (`out` must fit `models`).
pub fn listModels(models: []const Model, filter: ModelFilter, out: []Model) []Model {
    var n: usize = 0;
    outer: for (models) |m| {
        if (filter.tier) |t| if (!std.mem.eql(u8, m.tier, t)) continue :outer;
        if (filter.min_context) |c| if (m.context_window < c) continue :outer;
        if (filter.max_input_per_m) |max| {
            if (m.input_per_m == null or m.input_per_m.? > max) continue :outer;
        }
        out[n] = m;
        n += 1;
    }
    return out[0..n];
}

/// Filter the model list, then sort it (see lessThan for the exact order).
pub fn pickModels(models: []const Model, filter: ModelFilter, key: SortKey, out: []Model) []Model {
    const rows = listModels(models, filter, out);
    std.mem.sort(Model, rows, key, lessThan);
    return rows;
}

pub fn main() !void {
    // Rates flow from the model snapshot in the TS lib; one row is unpriced.
    const models = [_]Model{
        .{ .id = "atlas-flagship", .tier = "flagship", .context_window = 1_000_000, .input_per_m = 3.0, .output_per_m = 15.0 },
        .{ .id = "atlas-mini", .tier = "fast", .context_window = 200_000, .input_per_m = 0.5, .output_per_m = 2.0 },
        .{ .id = "nova-open", .tier = "balanced", .context_window = 1_000_000, .input_per_m = null, .output_per_m = null },
        .{ .id = "zeta-budget", .tier = "budget", .context_window = 128_000, .input_per_m = 0.5, .output_per_m = 1.0 },
    };
    const stdout = std.io.getStdOut().writer();
    try stdout.print("long-context (minContext >= 500000, sort context):\n", .{});
    var rows: [models.len]Model = undefined;
    for (pickModels(&models, .{ .min_context = 500_000 }, .context, &rows)) |m|
        try stdout.print("  {s:<14}ctx {d}\n", .{ m.id, m.context_window });
    try stdout.print("price sort (all models, unpriced last, ties by id):\n", .{});
    for (pickModels(&models, .{}, .price, &rows)) |m| {
        if (m.input_per_m) |p| try stdout.print("  {s:<14}${d:.2}/M in\n", .{ m.id, p })
        else try stdout.print("  {s:<14}unpriced\n", .{m.id});
    }
}

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 →