@@ -258,16 +258,8 @@ export function getProviderName(ownedBy: string): string {
258258 return PROVIDER_NAMES [ ownedBy ] ?? ownedBy ;
259259}
260260
261- // Sort key for ordering models oldest-to-newest in pickers. The model menu
262- // opens upward (side="top"), so the last item sits closest to the trigger —
263- // sorting ascending by this key puts the newest model right under the user's
264- // cursor. The key is the version embedded in the model id, e.g.
265- // "claude-sonnet-4-6" -> 4006, "claude-opus-4-8" -> 4008, "claude-fable-5" ->
266- // 5000; a higher number means a newer model. An id with no recognisable
267- // version (a brand-new or unexpected release) ranks as newest so it still
268- // surfaces at the end rather than at an arbitrary gateway-determined position.
269- // Only the first version group is read, so a trailing date suffix (e.g.
270- // "-20251001") is ignored; the minor component is assumed to be < 1000.
261+ // Version embedded in the model id, e.g. "claude-opus-4-8" -> 4008. Ids with no
262+ // recognisable version rank newest. A trailing date suffix is ignored.
271263export function getClaudeModelRecency ( modelId : string ) : number {
272264 const match = modelId . toLowerCase ( ) . match ( / - ( \d + ) (?: [ - . ] ( \d + ) ) ? / ) ;
273265 if ( ! match ) return Number . MAX_SAFE_INTEGER ;
@@ -276,43 +268,44 @@ export function getClaudeModelRecency(modelId: string): number {
276268 return major * 1000 + minor ;
277269}
278270
279- // Anthropic model families ordered by tier, most capable first.
271+ // Families ordered most- capable first; unknown families sort last .
280272const MODEL_FAMILY_ORDER = [ "fable" , "opus" , "sonnet" , "haiku" ] ;
281273
282274function getModelFamilyRank ( modelId : string ) : number {
283275 const id = modelId . toLowerCase ( ) ;
284276 const index = MODEL_FAMILY_ORDER . findIndex ( ( family ) => id . includes ( family ) ) ;
285- // Non-Anthropic-family models (e.g. Cloudflare `@cf/...`) group after the
286- // known families.
287277 return index === - 1 ? MODEL_FAMILY_ORDER . length : index ;
288278}
289279
290- // Comparator for the model picker. Groups models by family (tier) first, then
291- // orders each family's versions oldest-to-newest. Sorting by version alone
292- // interleaves families (e.g. a Sonnet 5 lands between Opus versions), which
293- // reads as an arbitrary order; grouping keeps every family contiguous.
280+ // Group by family, then newest version first within each family.
294281export function compareModelsForPicker ( a : string , b : string ) : number {
295282 const familyDiff = getModelFamilyRank ( a ) - getModelFamilyRank ( b ) ;
296283 if ( familyDiff !== 0 ) return familyDiff ;
297- return getClaudeModelRecency ( a ) - getClaudeModelRecency ( b ) ;
284+ return getClaudeModelRecency ( b ) - getClaudeModelRecency ( a ) ;
298285}
299286
300287const PROVIDER_PREFIXES = [ "anthropic/" , "openai/" , "google-vertex/" ] ;
301288
302- // Uppercase the leading acronym in a model id so provider prefixes read as
303- // their brand (e.g. "gpt-5.5" -> "GPT-5.5", "glm-5.2" -> "GLM-5.2").
304- function uppercaseLeadingAcronym ( name : string ) : string {
305- return name . replace ( / ^ [ a - z ] + / , ( prefix ) => prefix . toUpperCase ( ) ) ;
289+ // Uppercase the acronym, keep the version attached, title-case any suffix:
290+ // "gpt-5.6-sol" -> "GPT-5.6 Sol", "glm-5.2" -> "GLM-5.2".
291+ function formatProviderModelName ( modelId : string ) : string {
292+ const [ acronym , version , ...suffix ] = modelId . split ( "-" ) ;
293+ const head = version
294+ ? `${ acronym . toUpperCase ( ) } -${ version } `
295+ : acronym . toUpperCase ( ) ;
296+ const tail = suffix . map (
297+ ( word ) => word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) . toLowerCase ( ) ,
298+ ) ;
299+ return [ head , ...tail ] . join ( " " ) ;
306300}
307301
308302export function formatGatewayModelName ( model : GatewayModel ) : string {
309303 if ( isCloudflareModel ( model ) ) {
310- const name = ( model . id . split ( "/" ) . pop ( ) ?? model . id ) . toLowerCase ( ) ;
311- return uppercaseLeadingAcronym ( name ) ;
304+ return formatProviderModelName ( model . id . split ( "/" ) . pop ( ) ?? model . id ) ;
312305 }
313306
314307 if ( isOpenAIModel ( model ) ) {
315- return uppercaseLeadingAcronym ( stripProviderPrefix ( model . id ) . toLowerCase ( ) ) ;
308+ return formatProviderModelName ( stripProviderPrefix ( model . id ) ) ;
316309 }
317310
318311 return formatModelId ( model . id ) ;
0 commit comments