@@ -35,11 +35,26 @@ var specialisedCoderRegex = regexp.MustCompile(`-coder(-|$|:)`)
3535
3636var specialisedVisionRegex = regexp .MustCompile (`-vl-` )
3737
38+ var unstableVariantRegex = regexp .MustCompile (
39+ `(^|[-/:])(preview|beta|exp|experimental|customtools)([-/:]|$)` ,
40+ )
41+
42+ // lightweightDefaultRegex catches models that are good L1/classifier
43+ // candidates but too small or latency-oriented for the default workhorse role.
44+ // Do not match every "flash" model: Gemini Flash-class models can still be a
45+ // reasonable default, while "flash-lite"/"mini"/small-B variants usually are
46+ // not.
47+ var lightweightDefaultRegex = regexp .MustCompile (
48+ `(^|[-/:])(flash-lite|lite|mini|small|nano)([-/:]|$)|` +
49+ `(^|[-/:])([1-9]|1[0-4])b([-/:]|$)|` +
50+ `^qwen/qwen3(\.[0-9]+)?-flash([-/:]|$)` ,
51+ )
52+
3853// thinkingRegex matches model ids that are actually frontier reasoners — the
3954// `reasoning: true` capability flag alone is not enough (8B models also set
4055// it just because the API accepts a reasoning parameter).
4156var thinkingRegex = regexp .MustCompile (
42- `(-thinking|:thinking|/qwq|/deepseek-r[0-9]|-r1(-|$)|-reasoner)` ,
57+ `(-thinking|:thinking|/qwq|/deepseek-r[0-9]|-r1(-|$)|-reasoner|^x-ai/grok-(3-mini|4) )` ,
4358)
4459
4560// paretoAxes returns (quality, price) for a model under a given role.
@@ -79,6 +94,10 @@ func effectivePromptOf(m uiModel) float64 {
7994 return m .PromptPrice
8095}
8196
97+ func effectiveBlendedPriceOf (m uiModel ) float64 {
98+ return orBlendedPrice (effectivePromptOf (m ), m .CompletionPrice )
99+ }
100+
82101// usesVisionFallback returns true for roles where non-vision traffic routes
83102// image messages to the multimodal slot (so the role's candidates should be
84103// penalised for missing vision).
@@ -102,62 +121,68 @@ func inverseTTFT(m uiModel) float64 {
102121
103122var rolePresets = map [string ]rolePreset {
104123 "simple" : {
105- Description : "tools + multilingual, ≤ $0.2/M prompt, ctx ≥ 32k. Pareto frontier on (AA Agentic Index, prompt price )." ,
124+ Description : "tools + multilingual, ≤ $0.2/M prompt, ctx ≥ 32k. Pareto frontier on (1/TTFT, blended cost) when speed data is available, else ( AA Agentic Index, blended cost )." ,
106125 Filter : func (c llm.Capabilities , id string , aa llm.AAModelInfo ) bool {
107126 return multilingualRegex .MatchString (id ) &&
108127 ! excludedVendorsRegex .MatchString (id ) &&
109128 ! specialisedCoderRegex .MatchString (id ) &&
110129 ! specialisedVisionRegex .MatchString (id ) &&
130+ ! isUnstableVariant (id ) &&
111131 ! isFreeVariant (id ) &&
112132 c .Tools &&
113133 c .ContextLength >= 32000 &&
114134 c .PromptPrice > 0 && c .PromptPrice <= 0.2
115135 },
116- Axes : func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectivePromptOf (m ) },
136+ // Axes selected dynamically in applyPreset (see simpleAxes).
137+ Axes : nil ,
117138 },
118139
119140 "default" : {
120- Description : "tools + multilingual, ≤ $2/M prompt, ctx ≥ 32k. Pareto frontier on (AA Agentic Index, prompt price )." ,
141+ Description : "workhorse tools + multilingual, excludes L1/classifier-sized models, ≤ $2/M prompt, ctx ≥ 32k. Pareto frontier on (AA Agentic Index, blended cost )." ,
121142 Filter : func (c llm.Capabilities , id string , aa llm.AAModelInfo ) bool {
122143 return multilingualRegex .MatchString (id ) &&
123144 ! excludedVendorsRegex .MatchString (id ) &&
124145 ! specialisedCoderRegex .MatchString (id ) &&
125146 ! specialisedVisionRegex .MatchString (id ) &&
147+ ! lightweightDefaultRegex .MatchString (id ) &&
148+ ! isUnstableVariant (id ) &&
126149 ! isFreeVariant (id ) &&
127150 c .Tools &&
128151 c .ContextLength >= 32000 &&
129152 c .PromptPrice > 0 && c .PromptPrice <= 2.0
130153 },
131- Axes : func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectivePromptOf (m ) },
154+ Axes : func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectiveBlendedPriceOf (m ) },
132155 },
133156
134157 "complex" : {
135- Description : "frontier reasoners (thinking/r1/qwq) with tools + multilingual, ≤ $5/M prompt, ctx ≥ 64k. Pareto frontier on (AA Agentic Index, prompt price ). Claude via bridge is preferred when configured." ,
158+ Description : "frontier reasoners (thinking/r1/qwq/grok ) with tools + multilingual, ≤ $5/M prompt, ctx ≥ 64k. Pareto frontier on (AA Agentic Index, blended cost ). Claude via bridge is preferred when configured." ,
136159 Filter : func (c llm.Capabilities , id string , aa llm.AAModelInfo ) bool {
137160 return multilingualRegex .MatchString (id ) &&
138161 ! excludedVendorsRegex .MatchString (id ) &&
139162 ! specialisedCoderRegex .MatchString (id ) &&
140163 ! specialisedVisionRegex .MatchString (id ) &&
164+ ! isUnstableVariant (id ) &&
141165 ! isFreeVariant (id ) &&
142166 thinkingRegex .MatchString (id ) &&
143167 c .Tools && c .Reasoning &&
144168 c .ContextLength >= 64000 &&
145169 c .PromptPrice > 0 && c .PromptPrice <= 5.0
146170 },
147- Axes : func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectivePromptOf (m ) },
171+ Axes : func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectiveBlendedPriceOf (m ) },
148172 },
149173
150174 "multimodal" : {
151- Description : "vision + tools + multilingual, ≤ $2/M prompt, ctx ≥ 32k. Pareto frontier on (AA Intelligence Index, prompt price )." ,
175+ Description : "vision + tools + multilingual, ≤ $2/M prompt, ctx ≥ 32k. Pareto frontier on (AA Intelligence Index, blended cost )." ,
152176 Filter : func (c llm.Capabilities , id string , aa llm.AAModelInfo ) bool {
153177 return multilingualRegex .MatchString (id ) &&
154178 ! excludedVendorsRegex .MatchString (id ) &&
179+ ! isUnstableVariant (id ) &&
155180 ! isFreeVariant (id ) &&
156181 c .Vision && c .Tools &&
157182 c .ContextLength >= 32000 &&
158183 c .PromptPrice > 0 && c .PromptPrice <= 2.0
159184 },
160- Axes : func (m uiModel ) (float64 , float64 ) { return m .Score , m . PromptPrice },
185+ Axes : func (m uiModel ) (float64 , float64 ) { return m .Score , effectiveBlendedPriceOf ( m ) },
161186 },
162187
163188 "compaction" : {
@@ -167,6 +192,7 @@ var rolePresets = map[string]rolePreset{
167192 ! excludedVendorsRegex .MatchString (id ) &&
168193 ! specialisedCoderRegex .MatchString (id ) &&
169194 ! specialisedVisionRegex .MatchString (id ) &&
195+ ! isUnstableVariant (id ) &&
170196 ! isFreeVariant (id ) &&
171197 c .ContextLength >= 64000 &&
172198 c .CompletionPrice > 0 && c .CompletionPrice <= 2.0
@@ -181,6 +207,7 @@ var rolePresets = map[string]rolePreset{
181207 ! excludedVendorsRegex .MatchString (id ) &&
182208 ! specialisedCoderRegex .MatchString (id ) &&
183209 ! specialisedVisionRegex .MatchString (id ) &&
210+ ! isUnstableVariant (id ) &&
184211 ! isFreeVariant (id ) &&
185212 c .PromptPrice > 0 && c .PromptPrice <= 0.1
186213 },
@@ -196,6 +223,10 @@ func isFreeVariant(modelID string) bool {
196223 return len (modelID ) > 5 && modelID [len (modelID )- 5 :] == ":free"
197224}
198225
226+ func isUnstableVariant (modelID string ) bool {
227+ return unstableVariantRegex .MatchString (modelID )
228+ }
229+
199230// classifierAxes picks (1/TTFT, price) when at least one candidate has TTFT
200231// data, otherwise falls back to (Score, price). Mixing two quality scales in
201232// the same Pareto frontier would be meaningless, so we pick one globally.
@@ -208,6 +239,32 @@ func classifierAxes(candidates []uiModel) paretoAxes {
208239 return func (m uiModel ) (float64 , float64 ) { return m .Score , m .PromptPrice }
209240}
210241
242+ // simpleAxes keeps L1 recommendations distinct from default: when AA speed
243+ // data exists, startup latency is the quality axis. Without TTFT data, fall
244+ // back to agentic quality so new/untested models are not promoted blindly.
245+ func simpleAxes (candidates []uiModel ) paretoAxes {
246+ for _ , m := range candidates {
247+ if m .TTFT > 0 {
248+ return func (m uiModel ) (float64 , float64 ) { return inverseTTFT (m ), effectiveBlendedPriceOf (m ) }
249+ }
250+ }
251+ return func (m uiModel ) (float64 , float64 ) { return bestAgentic (m ), effectiveBlendedPriceOf (m ) }
252+ }
253+
254+ func axesForPreset (role string , preset rolePreset , candidates []uiModel ) paretoAxes {
255+ if preset .Axes != nil {
256+ return preset .Axes
257+ }
258+ switch role {
259+ case "simple" :
260+ return simpleAxes (candidates )
261+ case "classifier" :
262+ return classifierAxes (candidates )
263+ default :
264+ return nil
265+ }
266+ }
267+
211268// paretoFrontier keeps only non-dominated models. A model is also excluded
212269// if its quality is 0 — Pareto would otherwise keep untested models at the
213270// price floor just because no one beats them on both axes. Requiring quality
@@ -241,6 +298,94 @@ func paretoFrontier(models []uiModel, axes paretoAxes) []uiModel {
241298 return out
242299}
243300
301+ func appendNearFrontierAlternatives (frontier , candidates []uiModel , axes paretoAxes , role string , limit int ) []uiModel {
302+ if len (candidates ) == 0 || axes == nil || limit <= 0 {
303+ return frontier
304+ }
305+ seen := make (map [string ]bool , len (frontier ))
306+ topQuality := 0.0
307+ for _ , m := range candidates {
308+ q , _ := axes (m )
309+ if q > topQuality {
310+ topQuality = q
311+ }
312+ }
313+ if topQuality <= 0 {
314+ return frontier
315+ }
316+ for _ , m := range frontier {
317+ seen [m .ID ] = true
318+ }
319+ alts := make ([]uiModel , 0 , len (candidates ))
320+ for _ , m := range candidates {
321+ if seen [m .ID ] {
322+ continue
323+ }
324+ q , p := axes (m )
325+ if q <= 0 || p <= 0 || q < 0.50 * topQuality {
326+ continue
327+ }
328+ m .Recommended = false
329+ if p > 0 {
330+ m .ValuePerDollar = q / p
331+ }
332+ annotateModelForRole (& m , role , "near_frontier" )
333+ alts = append (alts , m )
334+ }
335+ sort .Slice (alts , func (i , j int ) bool {
336+ qi , pi := axes (alts [i ])
337+ qj , pj := axes (alts [j ])
338+ if qi != qj {
339+ return qi > qj
340+ }
341+ if pi != pj {
342+ return pi < pj
343+ }
344+ return alts [i ].ID < alts [j ].ID
345+ })
346+ if len (alts ) > limit {
347+ alts = alts [:limit ]
348+ }
349+ out := append (frontier , alts ... )
350+ return appendUntestedAlternatives (out , candidates , axes , role , 2 )
351+ }
352+
353+ func appendUntestedAlternatives (models , candidates []uiModel , axes paretoAxes , role string , limit int ) []uiModel {
354+ if limit <= 0 {
355+ return models
356+ }
357+ seen := make (map [string ]bool , len (models ))
358+ for _ , m := range models {
359+ seen [m .ID ] = true
360+ }
361+ untested := make ([]uiModel , 0 , len (candidates ))
362+ for _ , m := range candidates {
363+ if seen [m .ID ] {
364+ continue
365+ }
366+ q , _ := axes (m )
367+ if q > 0 {
368+ continue
369+ }
370+ m .Recommended = false
371+ annotateModelForRole (& m , role , "untested" )
372+ untested = append (untested , m )
373+ }
374+ sort .Slice (untested , func (i , j int ) bool {
375+ if untested [i ].ContextLength != untested [j ].ContextLength {
376+ return untested [i ].ContextLength > untested [j ].ContextLength
377+ }
378+ if untested [i ].PromptPrice != untested [j ].PromptPrice {
379+ return untested [i ].PromptPrice < untested [j ].PromptPrice
380+ }
381+ return untested [i ].ID < untested [j ].ID
382+ })
383+ if len (untested ) > limit {
384+ untested = untested [:limit ]
385+ }
386+ return append (models , untested ... )
387+ }
388+
244389// applyPreset returns the Pareto-optimal models for the role, sorted by
245390// quality descending (best first). If the role has no preset, returns nil.
246391// Each returned model has ValuePerDollar populated using the role's axes.
@@ -283,9 +428,9 @@ func applyPreset(all map[string]llm.Capabilities, aaModels map[string]llm.AAMode
283428 }
284429 candidates = append (candidates , m )
285430 }
286- axes := preset . Axes
287- if axes == nil && role == "classifier" {
288- axes = classifierAxes ( candidates )
431+ axes := axesForPreset ( role , preset , candidates )
432+ if axes == nil {
433+ return nil
289434 }
290435 frontier := paretoFrontier (candidates , axes )
291436 sort .Slice (frontier , func (i , j int ) bool {
@@ -307,7 +452,7 @@ func applyPreset(all map[string]llm.Capabilities, aaModels map[string]llm.AAMode
307452 frontier [i ].Recommended = true
308453 annotateModelForRole (& frontier [i ], role , "preset" )
309454 }
310- return frontier
455+ return appendNearFrontierAlternatives ( frontier , candidates , axes , role , 4 )
311456}
312457
313458func annotateModelForRole (m * uiModel , role , source string ) {
0 commit comments