Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

anypick

npm version npm downloads PyPI version Python versions

Select the best LLM across providers using filters over capabilities, pricing, and benchmarks, then pick a winner with a strategy. anypick only selects — you wire the chosen model into your chat client.

Two implementations share the same design, fixtures, and behavior:

  • anypick-python/
  • anypick-ts/

Example

Python

import os
from anypick import anypick, ModelFilters

sel = anypick(
    filters=ModelFilters(
        max_prompt_price=2e-6,
        min_context_length=128_000,
        requires_tools=True,
    ),
    strategy="cheapest",
    openrouter_api_key=os.environ["OPENROUTER_API_KEY"],
)
print(sel.model.id)

TypeScript

import { anypick, ModelFilters } from "anypick";

const sel = await anypick({
  filters: new ModelFilters({
    maxPromptPrice: 2e-6,
    minContextLength: 128_000,
    requiresTools: true,
  }),
  strategy: "cheapest",
  openrouterApiKey: process.env.OPENROUTER_API_KEY,
});

console.log(sel.model.id);

Providers

anypick v1 ships two model providers; wiring differs by capability:

provider source models benchmarks auth docs
openrouter OpenRouter API ✅ GET /api/v1/models ✅ GET /api/v1/benchmarks optional OPENROUTER_API_KEY (required for benchmarks, 30 req/min · 500 req/day) docs/providers/openrouter.md
vercel Vercel AI Gateway ✅ GET /v1/models ❌ none (no benchmark feed) optional VERCEL_AI_GATEWAY_API_KEY (raises rate ceiling) docs/providers/vercel.md

openrouter (the default) is the only provider with both a model catalog and benchmarks, so it supports every strategy. vercel is models-only: pair it with NoopBenchmarkObtainer, and only price-only strategies (cheapest) are meaningful — cheapest_with_floor, best_score, and best_value find no scores and behave as if every model's score is unknown.

Select a provider with the obtainer argument (or pass obtainers directly):

from anypick import anypick, ModelFilters, VercelModelObtainer, NoopBenchmarkObtainer

sel = anypick(
    filters=ModelFilters(requires_tools=True),
    strategy="cheapest",
    obtainer="vercel",   # or (VercelModelObtainer(), NoopBenchmarkObtainer())
)

Prefer fine-grained control? Pass a custom model and/or benchmark obtainer individually with model_obtainer / benchmark_obtainer — they override the corresponding side of obtainer:

from anypick import anypick, MyModelObtainer, MyBenchmarkObtainer

sel = anypick(
    filters=ModelFilters(requires_tools=True),
    model_obtainer=MyModelObtainer(),      # custom model catalog
    benchmark_obtainer=MyBenchmarkObtainer(),  # custom benchmark feed
)

Filters

Filters reduce (models, scores) to a subset before a strategy picks a winner. Build them ergonomically with ModelFilters, or as composable predicates with pred.* — both compile to the same engine.

ModelFilters fields

Everything is optional; None means "no constraint on this dimension". Clauses apply in the order listed (the order reported by NoModelsFound.survivors_by_clause).

category field semantics
ids / makers include_ids whitelist — keep only ids in the list
exclude_ids blacklist — drop ids in the list
include_makers whitelist — keep only makers in the list
exclude_makers blacklist — drop makers in the list
price (USD/token) min_prompt_price prompt_price ≥ value
max_prompt_price prompt_price ≤ value
min_completion_price completion_price ≥ value
max_completion_price completion_price ≤ value
min_expected_cost α·prompt + β·completion ≥ value
max_expected_cost α·prompt + β·completion ≤ value
expected_cost_weights (α, β) for expected-cost bounds & strategies
max_cache_read_price cache_read_price ≤ value (0.0 if n/a)
context min_context_length context_length ≥ value
max_context_length context_length ≤ value
modalities modalities_in inputs ⊇ given set
modalities_exactly inputs == given set
excludes_modalities inputs ∩ given set = ∅
output_modalities_in outputs ⊇ given set
output_modalities_exactly outputs == given set
excludes_output_modalities outputs ∩ given set = ∅
capabilities (tri-state) requires_tools None=ignore · True=must support · False=must not
requires_reasoning as above
requires_structured_outputs as above
benchmarks min_benchmarks list of BenchmarkThreshold; pass all (AND)
max_benchmarks list of BenchmarkThreshold; pass all (AND)

A maker is the id prefix before the first / (e.g. "openai" for "openai/gpt-4o"). Makers without a / have no maker: a maker whitelist drops them, a blacklist leaves them alone. Sigils are preserved verbatim (OpenRouter's ~deepseek/... has maker ~deepseek).

BenchmarkThreshold narrows by source ("artificial-analysis", "openrouter", "design-arena"), task_type (coding | intelligence | agentic), and benchmark_type (a specific OpenRouter benchmark field, e.g. gpqa_diamond), with min/max on the source's native scale.

Benchmark matching: a model passes a threshold iff at least one of its scores matching the threshold's source/task_type/benchmark_type (wildcards) satisfies the bound. Unknown ≠ zero — a threshold with only source/task_type and no min/max keeps scoreless models.

Predicate form — pred.*

For negation, alternation, or custom logic, compose predicates with & (and), | (or), and ~ (not); negate a capability to forbid it:

from anypick import pred

f = (pred.maker_in(["openai", "anthropic"])
     & pred.price_below(prompt=2e-6, completion=8e-6)
     & pred.price_above(prompt=1e-7)          # skip free tier
     & pred.context_at_least(128_000)
     & pred.context_at_most(1_000_000)
     & pred.modalities_exactly(["text", "image"])
     & pred.supports_tools()
     & ~pred.supports_reasoning()              # forbid reasoning
     & pred.benchmark_above(task_type="coding", min=60))
predicate keeps model if
pred.id_in([...]) / id_not_in([...]) id is / isn't in the list
pred.maker_in([...]) / maker_not_in([...]) maker is / isn't in the list
pred.price_below(*, prompt=None, completion=None) each given price ≤ bound
pred.price_above(*, prompt=None, completion=None) each given price ≥ bound
pred.expected_cost_below(max, weights=(α,β)) α·prompt+β·completion ≤ max
pred.expected_cost_above(min, weights=(α,β)) α·prompt+β·completion ≥ min
pred.cache_read_price_below(max) cache_read_price ≤ max
pred.context_at_least(n) / context_at_most(n) context_length ≥ / ≤ n
pred.modalities_in([...]) / modalities_exactly([...]) / modalities_not_in([...]) inputs ⊇ / == / ∩ ∅
pred.output_modalities_in([...]) / output_modalities_exactly([...]) / output_modalities_not_in([...]) outputs ⊇ / == / ∩ ∅
pred.supports_tools() / supports_reasoning() / supports_structured_outputs() flag true (use ~ to forbid)
pred.benchmark_above(*, source=None, task_type=None, benchmark_type=None, min=None) ≥1 matching score ≥ min
pred.benchmark_below(*, ..., max=None) ≥1 matching score ≤ max

Apply filters directly with apply_filters(models, scores, filters); pick_best and anypick call it internally. See docs/filters.md for full semantics, and docs/strategies.md for the pickers that run on the filtered set.

More

About

Select across all LLMs using filters across capabilities, pricing and benchmarks

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages