@@ -145,6 +145,7 @@ export function deriveEntry(
145145 priority : number ,
146146 model ?: CatalogModel ,
147147 exactComboSlugs : ReadonlySet < string > = new Set ( ) ,
148+ fallbackDisplayName ?: string ,
148149) : RawEntry {
149150 const preserveExact = isExactComboCatalogModel ( model , exactComboSlugs ) ;
150151 const isRouted = model !== undefined ;
@@ -181,7 +182,7 @@ export function deriveEntry(
181182 applyReasoningLevels ( e , model ?. reasoningEfforts , model ?. defaultReasoningEffort , preserveExact ) ;
182183 normalizeRoutedCatalogEntry ( e , model ?. parallelToolCalls === true ) ;
183184 if ( model ) applyJawcodeCatalogMetadata ( e , model . provider , model . id , model . contextCap ) ;
184- applyCatalogModelMetadata ( e , model ) ;
185+ applyCatalogModelMetadata ( e , model , fallbackDisplayName ) ;
185186 } else {
186187 applyNativeOpenAiContextOverride ( e ) ;
187188 if ( isGpt56NativeSlug ( slug ) ) ensureGpt56ReasoningLevels ( e ) ;
@@ -218,14 +219,58 @@ export function deriveEntry(
218219 if ( isGpt56NativeSlug ( slug ) ) ensureGpt56ReasoningLevels ( entry ) ;
219220 }
220221 if ( model && isRouted ) applyJawcodeCatalogMetadata ( entry , model . provider , model . id , model . contextCap ) ;
221- applyCatalogModelMetadata ( entry , model ) ;
222+ applyCatalogModelMetadata ( entry , model , fallbackDisplayName ) ;
222223 if ( ! isRouted ) applyNativeOpenAiContextOverride ( entry ) ;
223224 return ensureStrictCatalogFields ( normalizeServiceTiers ( entry ) , {
224225 preserveExactInputModalities : preserveExact ,
225226 isRouted,
226227 } ) ;
227228}
228229
230+ function configuredDisplayName ( model : CatalogModel ) : string {
231+ return typeof model . displayName === "string" ? model . displayName . trim ( ) : "" ;
232+ }
233+
234+ function defaultPickerLabel ( model : CatalogModel ) : string {
235+ const nativeId = model . id . trim ( ) ;
236+ return nativeId . slice ( nativeId . lastIndexOf ( "/" ) + 1 ) . trim ( ) ;
237+ }
238+
239+ /**
240+ * Keep the common picker path compact, but retain only as much route context as needed when two
241+ * emitted rows would otherwise have the same label. Explicit display names and combo aliases are
242+ * author-owned and never rewritten. If distinct native ids share a basename, the native id is
243+ * enough; if the same native id is exposed by multiple providers, append the provider as well.
244+ */
245+ function routedFallbackDisplayNames ( models : readonly CatalogModel [ ] ) : Map < CatalogModel , string > {
246+ const intendedLabel = ( model : CatalogModel ) : string =>
247+ configuredDisplayName ( model ) || model . alias ?. trim ( ) || defaultPickerLabel ( model ) ;
248+ const labelCounts = new Map < string , number > ( ) ;
249+ const nativeIdCounts = new Map < string , number > ( ) ;
250+ for ( const model of models ) {
251+ const label = intendedLabel ( model ) ;
252+ if ( ! label ) continue ;
253+ labelCounts . set ( label , ( labelCounts . get ( label ) ?? 0 ) + 1 ) ;
254+ const nativeKey = `${ label } \u0000${ model . id . trim ( ) } ` ;
255+ nativeIdCounts . set ( nativeKey , ( nativeIdCounts . get ( nativeKey ) ?? 0 ) + 1 ) ;
256+ }
257+
258+ const out = new Map < CatalogModel , string > ( ) ;
259+ for ( const model of models ) {
260+ if ( configuredDisplayName ( model ) || model . alias ) continue ;
261+ const label = defaultPickerLabel ( model ) ;
262+ if ( ! label ) continue ;
263+ if ( ( labelCounts . get ( label ) ?? 0 ) <= 1 ) {
264+ out . set ( model , label ) ;
265+ continue ;
266+ }
267+ const nativeId = model . id . trim ( ) ;
268+ const sameNativeIdCount = nativeIdCounts . get ( `${ label } \u0000${ nativeId } ` ) ?? 0 ;
269+ out . set ( model , sameNativeIdCount > 1 ? `${ nativeId } (${ model . provider } )` : nativeId ) ;
270+ }
271+ return out ;
272+ }
273+
229274export function buildCatalogEntries (
230275 template : RawEntry | null ,
231276 gptSlugs : string [ ] ,
@@ -250,22 +295,28 @@ export function buildCatalogEntries(
250295 if ( rank . has ( slug ) ) e . priority = rank . get ( slug ) ! ;
251296 out . push ( e ) ;
252297 }
253- for ( const m of goModels ) {
254- if ( collisionSkipped . has ( m ) ) continue ;
298+ const routedModels = goModels . filter ( m => {
299+ if ( collisionSkipped . has ( m ) ) return false ;
255300 const slug = catalogModelSlug ( m ) ;
256301 if ( m . provider !== COMBO_NAMESPACE && comboPublicSlugs . has ( slug ) ) {
257302 warnComboMasqueradeCollisionOnce ( slug ) ;
258- continue ;
303+ return false ;
259304 }
305+ return true ;
306+ } ) ;
307+ const fallbackDisplayNames = routedFallbackDisplayNames ( routedModels ) ;
308+ for ( const m of routedModels ) {
309+ const slug = catalogModelSlug ( m ) ;
260310 // Provider rows use the one-slash slug codec; combo aliases intentionally override that
261311 // public slug and may be bare.
262312 const e = deriveEntry (
263313 template ,
264314 slug ,
265- `Routed via opencodex → ${ m . provider } (${ m . owned_by ?? m . provider } ).` ,
315+ `Routed via opencodex → ${ slug } (${ m . owned_by ?? m . provider } ).` ,
266316 5 ,
267317 m ,
268318 exactComboSlugs ,
319+ fallbackDisplayNames . get ( m ) ,
269320 ) ;
270321 // Featured picks may be stored raw (legacy) or encoded — honor both.
271322 const rankHit = rank . get ( slug ) ?? rank . get ( `${ m . provider } /${ m . id } ` ) ;
0 commit comments