@@ -166,6 +166,7 @@ export function deriveEntry(
166166 priority : number ,
167167 model ?: CatalogModel ,
168168 exactComboSlugs : ReadonlySet < string > = new Set ( ) ,
169+ fallbackDisplayName ?: string ,
169170) : RawEntry {
170171 const preserveExact = isExactComboCatalogModel ( model , exactComboSlugs ) ;
171172 const isRouted = model !== undefined ;
@@ -202,7 +203,7 @@ export function deriveEntry(
202203 applyReasoningLevels ( e , model ?. reasoningEfforts , model ?. defaultReasoningEffort , preserveExact ) ;
203204 normalizeRoutedCatalogEntry ( e , model ?. parallelToolCalls === true ) ;
204205 if ( model ) applyJawcodeCatalogMetadata ( e , model . provider , model . id , model . contextCap ) ;
205- applyCatalogModelMetadata ( e , model ) ;
206+ applyCatalogModelMetadata ( e , model , fallbackDisplayName ) ;
206207 } else {
207208 applyNativeOpenAiContextOverride ( e ) ;
208209 if ( isGpt56NativeSlug ( slug ) ) ensureGpt56ReasoningLevels ( e ) ;
@@ -239,14 +240,58 @@ export function deriveEntry(
239240 if ( isGpt56NativeSlug ( slug ) ) ensureGpt56ReasoningLevels ( entry ) ;
240241 }
241242 if ( model && isRouted ) applyJawcodeCatalogMetadata ( entry , model . provider , model . id , model . contextCap ) ;
242- applyCatalogModelMetadata ( entry , model ) ;
243+ applyCatalogModelMetadata ( entry , model , fallbackDisplayName ) ;
243244 if ( ! isRouted ) applyNativeOpenAiContextOverride ( entry ) ;
244245 return ensureStrictCatalogFields ( normalizeServiceTiers ( entry ) , {
245246 preserveExactInputModalities : preserveExact ,
246247 isRouted,
247248 } ) ;
248249}
249250
251+ function configuredDisplayName ( model : CatalogModel ) : string {
252+ return typeof model . displayName === "string" ? model . displayName . trim ( ) : "" ;
253+ }
254+
255+ function defaultPickerLabel ( model : CatalogModel ) : string {
256+ const nativeId = model . id . trim ( ) ;
257+ return nativeId . slice ( nativeId . lastIndexOf ( "/" ) + 1 ) . trim ( ) ;
258+ }
259+
260+ /**
261+ * Keep the common picker path compact, but retain only as much route context as needed when two
262+ * emitted rows would otherwise have the same label. Explicit display names and combo aliases are
263+ * author-owned and never rewritten. If distinct native ids share a basename, the native id is
264+ * enough; if the same native id is exposed by multiple providers, append the provider as well.
265+ */
266+ function routedFallbackDisplayNames ( models : readonly CatalogModel [ ] ) : Map < CatalogModel , string > {
267+ const intendedLabel = ( model : CatalogModel ) : string =>
268+ configuredDisplayName ( model ) || model . alias ?. trim ( ) || defaultPickerLabel ( model ) ;
269+ const labelCounts = new Map < string , number > ( ) ;
270+ const nativeIdCounts = new Map < string , number > ( ) ;
271+ for ( const model of models ) {
272+ const label = intendedLabel ( model ) ;
273+ if ( ! label ) continue ;
274+ labelCounts . set ( label , ( labelCounts . get ( label ) ?? 0 ) + 1 ) ;
275+ const nativeKey = `${ label } \u0000${ model . id . trim ( ) } ` ;
276+ nativeIdCounts . set ( nativeKey , ( nativeIdCounts . get ( nativeKey ) ?? 0 ) + 1 ) ;
277+ }
278+
279+ const out = new Map < CatalogModel , string > ( ) ;
280+ for ( const model of models ) {
281+ if ( configuredDisplayName ( model ) || model . alias ) continue ;
282+ const label = defaultPickerLabel ( model ) ;
283+ if ( ! label ) continue ;
284+ if ( ( labelCounts . get ( label ) ?? 0 ) <= 1 ) {
285+ out . set ( model , label ) ;
286+ continue ;
287+ }
288+ const nativeId = model . id . trim ( ) ;
289+ const sameNativeIdCount = nativeIdCounts . get ( `${ label } \u0000${ nativeId } ` ) ?? 0 ;
290+ out . set ( model , sameNativeIdCount > 1 ? `${ nativeId } (${ model . provider } )` : nativeId ) ;
291+ }
292+ return out ;
293+ }
294+
250295export function buildCatalogEntries (
251296 template : RawEntry | null ,
252297 gptSlugs : string [ ] ,
@@ -271,22 +316,28 @@ export function buildCatalogEntries(
271316 if ( rank . has ( slug ) ) e . priority = rank . get ( slug ) ! ;
272317 out . push ( e ) ;
273318 }
274- for ( const m of goModels ) {
275- if ( collisionSkipped . has ( m ) ) continue ;
319+ const routedModels = goModels . filter ( m => {
320+ if ( collisionSkipped . has ( m ) ) return false ;
276321 const slug = catalogModelSlug ( m ) ;
277322 if ( m . provider !== COMBO_NAMESPACE && comboPublicSlugs . has ( slug ) ) {
278323 warnComboMasqueradeCollisionOnce ( slug ) ;
279- continue ;
324+ return false ;
280325 }
326+ return true ;
327+ } ) ;
328+ const fallbackDisplayNames = routedFallbackDisplayNames ( routedModels ) ;
329+ for ( const m of routedModels ) {
330+ const slug = catalogModelSlug ( m ) ;
281331 // Provider rows use the one-slash slug codec; combo aliases intentionally override that
282332 // public slug and may be bare.
283333 const e = deriveEntry (
284334 template ,
285335 slug ,
286- `Routed via opencodex → ${ m . provider } (${ m . owned_by ?? m . provider } ).` ,
336+ `Routed via opencodex → ${ slug } (${ m . owned_by ?? m . provider } ).` ,
287337 5 ,
288338 m ,
289339 exactComboSlugs ,
340+ fallbackDisplayNames . get ( m ) ,
290341 ) ;
291342 // Featured picks may be stored raw (legacy) or encoded — honor both.
292343 const rankHit = rank . get ( slug ) ?? rank . get ( `${ m . provider } /${ m . id } ` ) ;
0 commit comments