Skip to content

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

# model-picker — Ruby port: filter + rank the AI model catalog.

# Pricing projection of the TS AiModel record; nil plays TS null (unpriced).
Model = Struct.new(:id, :tier, :context_window, :input_per_m, :output_per_m,
                   :released, keyword_init: true)

# Curated entry points: a filter + the sort that makes that filter useful.
PRESETS = {
  'long-context' => [{ min_context: 500_000 }, 'context'],
  'cheap-bulk'   => [{ max_input_per_m: 1 }, 'price'],
  'flagship'     => [{ tier: 'flagship' }, 'tokens_per_dollar'],
}.freeze

# id ascending — the shared stable tie-break for every sort.
def by_id(a, b) = a.id <=> b.id

# Output tokens per USD: 1e6 / output_per_m, or nil when unpriced.
def tokens_per_dollar(m) = m.output_per_m && 1_000_000.0 / m.output_per_m

# Comparator core: value order (desc when asked), nil last, id asc on ties.
# `key` is an attribute Symbol or a derivation lambda.
def rank(key, desc = false)
  lambda do |a, b|
    va = key.respond_to?(:call) ? key.call(a) : a.public_send(key)
    vb = key.respond_to?(:call) ? key.call(b) : b.public_send(key)
    return by_id(a, b) if va.nil? && vb.nil?
    return 1 if va.nil?
    return -1 if vb.nil?
    c = (va <=> vb)
    c = -c if desc
    c.zero? ? by_id(a, b) : c
  end
end

# Sort comparator per key: value order first, nil last, id asc on ties.
# Every comparator fully orders, so the unstable Array#sort stays deterministic.
COMPARATORS = {
  'price'            => rank(:input_per_m),
  'context'          => rank(:context_window, true),
  'tokens_per_dollar' => rank(->(m) { tokens_per_dollar(m) }, true),
  'released'         => rank(:released, true),
}.freeze

# Filter step, inlined from listModels in src/lib/ai/models.ts: nil prices
# never satisfy max_input_per_m.
def list_models(models, filter = {})
  models.select do |m|
    (!filter.key?(:tier) || m.tier == filter[:tier]) &&
      (!filter.key?(:min_context) || m.context_window >= filter[:min_context]) &&
      (!filter.key?(:max_input_per_m) ||
        !m.input_per_m.nil? && m.input_per_m <= filter[:max_input_per_m])
  end
end

# Filter the model list, then sort it. See COMPARATORS for the exact order.
def pick_models(models, filter = {}, sort = 'price')
  list_models(models, filter).sort(&COMPARATORS.fetch(sort))
end

models = [
  Model.new(id: 'atlas-flagship', tier: 'flagship', context_window: 1_000_000, input_per_m: 3.0, output_per_m: 15.0, released: '2025-03-01'),
  Model.new(id: 'atlas-mini', tier: 'fast', context_window: 200_000, input_per_m: 0.5, output_per_m: 2.0, released: '2025-01-15'),
  Model.new(id: 'nova-open', tier: 'balanced', context_window: 1_000_000, input_per_m: nil, output_per_m: nil, released: nil),
  Model.new(id: 'zeta-budget', tier: 'budget', context_window: 128_000, input_per_m: 0.5, output_per_m: 1.0, released: '2024-12-01'),
]

filter, sort = PRESETS['long-context']
puts "long-context (minContext >= #{filter[:min_context]}, sort #{sort}):"
pick_models(models, filter, sort).each { |m| puts format('  %-14s ctx %d', m.id, m.context_window) }

puts 'price sort (all models, unpriced last, ties by id):'
pick_models(models, {}, 'price').each do |m|
  puts m.input_per_m ? format('  %-14s $%.2f/M in', m.id, m.input_per_m) : format('  %-14s unpriced', m.id)
end

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 →