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 →