Constraints
Scoring produces a ranked list, but the top 5 might all be from the same provider or the same model family. Constraints let you enforce diversity by spreading results across providers, families, or any rule you define.
Constraints don’t change scores. They shape the final selection by controlling which scored models make it into recommend() results. pickai ships two built-in constraints: perProvider() and perFamily().
How Constraints Work
Section titled “How Constraints Work”When recommend() selects the top N models, it uses a two-pass algorithm:
- First pass: Walk the scored list in order. Add a model only if it passes all constraints.
- Second pass: If fewer than
limitmodels were selected, fill remaining slots from the scored list, ignoring constraints.
This means constraints are preferences, not hard limits. You always get limit results (if enough candidates exist), but constraint-satisfying models come first.
Built-in Constraints
Section titled “Built-in Constraints”perProvider(max)
Section titled “perProvider(max)”Limits how many models sharing the same model.provider value appear in results. Default: 1 per provider.
import { fromModelsDev, recommend, Purpose, perProvider, DIRECT_PROVIDERS } from "pickai";
const models = await fromModelsDev();
// At most 2 models per providerconst results = recommend(models, Purpose.Coding, { filter: { providers: [...DIRECT_PROVIDERS] }, constraints: [perProvider(2)], limit: 10,});perFamily(max)
Section titled “perFamily(max)”Limits how many models sharing the same model.family value appear in results. This field comes from models.dev and represents model sub-lines (e.g., claude-sonnet, gpt-mini, gemini-flash). Models without a family field always pass and aren’t counted against any group.
import { perFamily, DIRECT_PROVIDERS } from "pickai";
// At most 1 per model.family (e.g., one claude-opus, one claude-sonnet, one gpt-mini)const results = recommend(models, Purpose.Balanced, { filter: { providers: [...DIRECT_PROVIDERS] }, constraints: [perFamily(1)], limit: 5,});Writing Custom Constraints
Section titled “Writing Custom Constraints”A Constraint is a function that receives the models already selected and the candidate being considered:
type Constraint = (selected: Model[], candidate: Model) => boolean;Return true to allow the candidate, false to skip it (in the first pass).
// Custom: at most 2 models over $3/M inputconst limitCostly: Constraint = (selected, candidate) => { const isCostly = (m: Model) => (m.cost?.input ?? 0) > 3; if (!isCostly(candidate)) return true; return selected.filter(isCostly).length < 2;};Full Example
Section titled “Full Example”import { fromModelsDev, recommend, perProvider, perFamily, Purpose, DIRECT_PROVIDERS, type Model, type Constraint,} from "pickai";
const models = await fromModelsDev();
// Without constraints: top 5 might all be from the same providerconst unconstrained = recommend(models, Purpose.Coding, { filter: { providers: [...DIRECT_PROVIDERS] }, limit: 5,});
console.log("--- Without constraints ---");for (const m of unconstrained) { console.log(` ${m.score.toFixed(3)} | ${m.name} (${m.provider})`);}
// With perProvider(1): one model per providerconst byProvider = recommend(models, Purpose.Coding, { filter: { providers: [...DIRECT_PROVIDERS] }, constraints: [perProvider(1)], limit: 5,});
console.log("\n--- With perProvider(1) ---");for (const m of byProvider) { console.log(` ${m.score.toFixed(3)} | ${m.name} (${m.provider})`);}
// With perFamily(1): one model per familyconst byFamily = recommend(models, Purpose.Coding, { filter: { providers: [...DIRECT_PROVIDERS] }, constraints: [perFamily(1)], limit: 10,});
console.log("\n--- With perFamily(1) ---");for (const m of byFamily) { console.log(` ${m.score.toFixed(3)} | ${m.name} (${m.provider}, ${m.family ?? "unknown"})`);}
// Custom constraint: at most 2 models over $3/M inputconst limitCostly: Constraint = (selected, candidate) => { const isCostly = (m: Model) => (m.cost?.input ?? 0) > 3; if (!isCostly(candidate)) return true; return selected.filter(isCostly).length < 2;};
// Combine all three: built-in + custom constraints togetherconst everything = recommend(models, Purpose.Coding, { filter: { providers: [...DIRECT_PROVIDERS] }, constraints: [perProvider(2), perFamily(1), limitCostly], limit: 10,});
console.log("\n--- With perProvider(2) + perFamily(1) + limitCostly ---");for (const m of everything) { const cost = m.cost?.input != null ? `$${m.cost.input}/M` : "unknown"; console.log(` ${m.score.toFixed(3)} | ${m.name} (${m.provider}) | ${cost}`);}