@@ -130,6 +130,7 @@ export function buildModelPickerItems(
130130 manageSettingsUrl : string | undefined ,
131131 commandService : ICommandService ,
132132 chatEntitlementService : IChatEntitlementService ,
133+ showCuratedModels : boolean = true ,
133134) : IActionListItem < IActionWidgetDropdownAction > [ ] {
134135 const isPro = isProUser ( chatEntitlementService . entitlement ) ;
135136 const items : IActionListItem < IActionWidgetDropdownAction > [ ] = [ ] ;
@@ -145,167 +146,185 @@ export function buildModelPickerItems(
145146 run : ( ) => { }
146147 } ) ) ;
147148 } else {
148- // Collect all available models into lookup maps
149- const allModelsMap = new Map < string , ILanguageModelChatMetadataAndIdentifier > ( ) ;
150- const modelsByMetadataId = new Map < string , ILanguageModelChatMetadataAndIdentifier > ( ) ;
151- for ( const model of models ) {
152- allModelsMap . set ( model . identifier , model ) ;
153- modelsByMetadataId . set ( model . metadata . id , model ) ;
154- }
155-
156- const placed = new Set < string > ( ) ;
157-
158- const markPlaced = ( identifierOrId : string , metadataId ?: string ) => {
159- placed . add ( identifierOrId ) ;
160- if ( metadataId ) {
161- placed . add ( metadataId ) ;
149+ if ( ! showCuratedModels ) {
150+ // Flat list: auto first, then all models sorted alphabetically
151+ const autoModel = models . find ( m => m . metadata . id === 'auto' && m . metadata . vendor === 'copilot' ) ;
152+ if ( autoModel ) {
153+ items . push ( createModelItem ( createModelAction ( autoModel , selectedModelId , onSelect ) , autoModel ) ) ;
162154 }
163- } ;
164-
165- const resolveModel = ( id : string ) => allModelsMap . get ( id ) ?? modelsByMetadataId . get ( id ) ;
166-
167- const getUnavailableReason = ( entry : IModelControlEntry ) : 'upgrade' | 'update' | 'admin' => {
168- if ( ! isPro ) {
169- return 'upgrade' ;
155+ const sortedModels = models
156+ . filter ( m => m !== autoModel )
157+ . sort ( ( a , b ) => {
158+ const vendorCmp = a . metadata . vendor . localeCompare ( b . metadata . vendor ) ;
159+ return vendorCmp !== 0 ? vendorCmp : a . metadata . name . localeCompare ( b . metadata . name ) ;
160+ } ) ;
161+ for ( const model of sortedModels ) {
162+ items . push ( createModelItem ( createModelAction ( model , selectedModelId , onSelect ) , model ) ) ;
170163 }
171- if ( entry . minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
172- return 'update' ;
164+ } else {
165+
166+ // Collect all available models into lookup maps
167+ const allModelsMap = new Map < string , ILanguageModelChatMetadataAndIdentifier > ( ) ;
168+ const modelsByMetadataId = new Map < string , ILanguageModelChatMetadataAndIdentifier > ( ) ;
169+ for ( const model of models ) {
170+ allModelsMap . set ( model . identifier , model ) ;
171+ modelsByMetadataId . set ( model . metadata . id , model ) ;
173172 }
174- return 'admin' ;
175- } ;
176173
177- // --- 1. Auto ---
178- const autoModel = models . find ( m => m . metadata . id === 'auto' && m . metadata . vendor === 'copilot' ) ;
179- if ( autoModel ) {
180- markPlaced ( autoModel . identifier , autoModel . metadata . id ) ;
181- items . push ( createModelItem ( createModelAction ( autoModel , selectedModelId , onSelect ) , autoModel ) ) ;
182- }
174+ const placed = new Set < string > ( ) ;
183175
184- // --- 2. Promoted section (selected + recently used + featured) ---
185- type PromotedItem =
186- | { kind : 'available' ; model : ILanguageModelChatMetadataAndIdentifier }
187- | { kind : 'unavailable' ; id : string ; entry : IModelControlEntry ; reason : 'upgrade' | 'update' | 'admin' } ;
176+ const markPlaced = ( identifierOrId : string , metadataId ?: string ) => {
177+ placed . add ( identifierOrId ) ;
178+ if ( metadataId ) {
179+ placed . add ( metadataId ) ;
180+ }
181+ } ;
188182
189- const promotedItems : PromotedItem [ ] = [ ] ;
183+ const resolveModel = ( id : string ) => allModelsMap . get ( id ) ?? modelsByMetadataId . get ( id ) ;
190184
191- // Try to place a model by id. Returns true if handled.
192- const tryPlaceModel = ( id : string ) : boolean => {
193- if ( placed . has ( id ) ) {
194- return false ;
195- }
196- const model = resolveModel ( id ) ;
197- if ( model && ! placed . has ( model . identifier ) ) {
198- markPlaced ( model . identifier , model . metadata . id ) ;
199- const entry = controlModels [ model . metadata . id ] ;
200- if ( entry ?. minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
201- promotedItems . push ( { kind : 'unavailable' , id : model . metadata . id , entry, reason : 'update' } ) ;
202- } else {
203- promotedItems . push ( { kind : 'available' , model } ) ;
185+ const getUnavailableReason = ( entry : IModelControlEntry ) : 'upgrade' | 'update' | 'admin' => {
186+ if ( ! isPro ) {
187+ return 'upgrade' ;
204188 }
205- return true ;
206- }
207- if ( ! model ) {
208- const entry = controlModels [ id ] ;
209- if ( entry && ! entry . exists ) {
210- markPlaced ( id ) ;
211- promotedItems . push ( { kind : 'unavailable' , id, entry, reason : getUnavailableReason ( entry ) } ) ;
212- return true ;
189+ if ( entry . minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
190+ return 'update' ;
213191 }
192+ return 'admin' ;
193+ } ;
194+
195+ // --- 1. Auto ---
196+ const autoModel = models . find ( m => m . metadata . id === 'auto' && m . metadata . vendor === 'copilot' ) ;
197+ if ( autoModel ) {
198+ markPlaced ( autoModel . identifier , autoModel . metadata . id ) ;
199+ items . push ( createModelItem ( createModelAction ( autoModel , selectedModelId , onSelect ) , autoModel ) ) ;
214200 }
215- return false ;
216- } ;
217201
218- // Selected model
219- if ( selectedModelId && selectedModelId !== autoModel ?. identifier ) {
220- tryPlaceModel ( selectedModelId ) ;
221- }
202+ // --- 2. Promoted section (selected + recently used + featured) ---
203+ type PromotedItem =
204+ | { kind : 'available' ; model : ILanguageModelChatMetadataAndIdentifier }
205+ | { kind : 'unavailable' ; id : string ; entry : IModelControlEntry ; reason : 'upgrade' | 'update' | 'admin' } ;
222206
223- // Recently used models
224- for ( const id of recentModelIds ) {
225- tryPlaceModel ( id ) ;
226- }
207+ const promotedItems : PromotedItem [ ] = [ ] ;
227208
228- // Featured models from control manifest
229- for ( const [ entryId , entry ] of Object . entries ( controlModels ) ) {
230- if ( ! entry . featured || placed . has ( entryId ) ) {
231- continue ;
232- }
233- const model = resolveModel ( entryId ) ;
234- if ( model && ! placed . has ( model . identifier ) ) {
235- markPlaced ( model . identifier , model . metadata . id ) ;
236- if ( entry . minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
237- promotedItems . push ( { kind : 'unavailable' , id : entryId , entry, reason : 'update' } ) ;
238- } else {
239- promotedItems . push ( { kind : 'available' , model } ) ;
209+ // Try to place a model by id. Returns true if handled.
210+ const tryPlaceModel = ( id : string ) : boolean => {
211+ if ( placed . has ( id ) ) {
212+ return false ;
240213 }
241- } else if ( ! model && ! entry . exists ) {
242- markPlaced ( entryId ) ;
243- promotedItems . push ( { kind : 'unavailable' , id : entryId , entry, reason : getUnavailableReason ( entry ) } ) ;
214+ const model = resolveModel ( id ) ;
215+ if ( model && ! placed . has ( model . identifier ) ) {
216+ markPlaced ( model . identifier , model . metadata . id ) ;
217+ const entry = controlModels [ model . metadata . id ] ;
218+ if ( entry ?. minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
219+ promotedItems . push ( { kind : 'unavailable' , id : model . metadata . id , entry, reason : 'update' } ) ;
220+ } else {
221+ promotedItems . push ( { kind : 'available' , model } ) ;
222+ }
223+ return true ;
224+ }
225+ if ( ! model ) {
226+ const entry = controlModels [ id ] ;
227+ if ( entry && ! entry . exists ) {
228+ markPlaced ( id ) ;
229+ promotedItems . push ( { kind : 'unavailable' , id, entry, reason : getUnavailableReason ( entry ) } ) ;
230+ return true ;
231+ }
232+ }
233+ return false ;
234+ } ;
235+
236+ // Selected model
237+ if ( selectedModelId && selectedModelId !== autoModel ?. identifier ) {
238+ tryPlaceModel ( selectedModelId ) ;
244239 }
245- }
246240
247- // Render promoted section: sorted alphabetically by name
248- let hasShownActionLink = false ;
249- if ( promotedItems . length > 0 ) {
250- promotedItems . sort ( ( a , b ) => {
251- const aName = a . kind === 'available' ? a . model . metadata . name : a . entry . label ;
252- const bName = b . kind === 'available' ? b . model . metadata . name : b . entry . label ;
253- return aName . localeCompare ( bName ) ;
254- } ) ;
241+ // Recently used models
242+ for ( const id of recentModelIds ) {
243+ tryPlaceModel ( id ) ;
244+ }
255245
256- for ( const item of promotedItems ) {
257- if ( item . kind === 'available' ) {
258- items . push ( createModelItem ( createModelAction ( item . model , selectedModelId , onSelect ) , item . model ) ) ;
259- } else {
260- const showActionLink = item . reason === 'upgrade' ? ! hasShownActionLink : true ;
261- if ( showActionLink && item . reason === 'upgrade' ) {
262- hasShownActionLink = true ;
246+ // Featured models from control manifest
247+ for ( const [ entryId , entry ] of Object . entries ( controlModels ) ) {
248+ if ( ! entry . featured || placed . has ( entryId ) ) {
249+ continue ;
250+ }
251+ const model = resolveModel ( entryId ) ;
252+ if ( model && ! placed . has ( model . identifier ) ) {
253+ markPlaced ( model . identifier , model . metadata . id ) ;
254+ if ( entry . minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
255+ promotedItems . push ( { kind : 'unavailable' , id : entryId , entry, reason : 'update' } ) ;
256+ } else {
257+ promotedItems . push ( { kind : 'available' , model } ) ;
263258 }
264- items . push ( createUnavailableModelItem ( item . id , item . entry , item . reason , manageSettingsUrl , updateStateType , undefined , showActionLink ) ) ;
259+ } else if ( ! model && ! entry . exists ) {
260+ markPlaced ( entryId ) ;
261+ promotedItems . push ( { kind : 'unavailable' , id : entryId , entry, reason : getUnavailableReason ( entry ) } ) ;
265262 }
266263 }
267- }
268264
269- // --- 3. Other Models (collapsible) ---
270- otherModels = models
271- . filter ( m => ! placed . has ( m . identifier ) && ! placed . has ( m . metadata . id ) )
272- . sort ( ( a , b ) => {
273- const aCopilot = a . metadata . vendor === 'copilot' ? 0 : 1 ;
274- const bCopilot = b . metadata . vendor === 'copilot' ? 0 : 1 ;
275- if ( aCopilot !== bCopilot ) {
276- return aCopilot - bCopilot ;
265+ // Render promoted section: sorted alphabetically by name
266+ let hasShownActionLink = false ;
267+ if ( promotedItems . length > 0 ) {
268+ promotedItems . sort ( ( a , b ) => {
269+ const aName = a . kind === 'available' ? a . model . metadata . name : a . entry . label ;
270+ const bName = b . kind === 'available' ? b . model . metadata . name : b . entry . label ;
271+ return aName . localeCompare ( bName ) ;
272+ } ) ;
273+
274+ for ( const item of promotedItems ) {
275+ if ( item . kind === 'available' ) {
276+ items . push ( createModelItem ( createModelAction ( item . model , selectedModelId , onSelect ) , item . model ) ) ;
277+ } else {
278+ const showActionLink = item . reason === 'upgrade' ? ! hasShownActionLink : true ;
279+ if ( showActionLink && item . reason === 'upgrade' ) {
280+ hasShownActionLink = true ;
281+ }
282+ items . push ( createUnavailableModelItem ( item . id , item . entry , item . reason , manageSettingsUrl , updateStateType , undefined , showActionLink ) ) ;
283+ }
277284 }
278- const vendorCmp = a . metadata . vendor . localeCompare ( b . metadata . vendor ) ;
279- return vendorCmp !== 0 ? vendorCmp : a . metadata . name . localeCompare ( b . metadata . name ) ;
280- } ) ;
281-
282- if ( otherModels . length > 0 ) {
283- if ( items . length > 0 ) {
284- items . push ( { kind : ActionListItemKind . Separator } ) ;
285285 }
286- items . push ( {
287- item : {
288- id : 'otherModels' ,
289- enabled : true ,
290- checked : false ,
291- class : undefined ,
292- tooltip : localize ( 'chat.modelPicker.otherModels' , "Other Models" ) ,
286+
287+ // --- 3. Other Models (collapsible) ---
288+ otherModels = models
289+ . filter ( m => ! placed . has ( m . identifier ) && ! placed . has ( m . metadata . id ) )
290+ . sort ( ( a , b ) => {
291+ const aCopilot = a . metadata . vendor === 'copilot' ? 0 : 1 ;
292+ const bCopilot = b . metadata . vendor === 'copilot' ? 0 : 1 ;
293+ if ( aCopilot !== bCopilot ) {
294+ return aCopilot - bCopilot ;
295+ }
296+ const vendorCmp = a . metadata . vendor . localeCompare ( b . metadata . vendor ) ;
297+ return vendorCmp !== 0 ? vendorCmp : a . metadata . name . localeCompare ( b . metadata . name ) ;
298+ } ) ;
299+
300+ if ( otherModels . length > 0 ) {
301+ if ( items . length > 0 ) {
302+ items . push ( { kind : ActionListItemKind . Separator } ) ;
303+ }
304+ items . push ( {
305+ item : {
306+ id : 'otherModels' ,
307+ enabled : true ,
308+ checked : false ,
309+ class : undefined ,
310+ tooltip : localize ( 'chat.modelPicker.otherModels' , "Other Models" ) ,
311+ label : localize ( 'chat.modelPicker.otherModels' , "Other Models" ) ,
312+ run : ( ) => { /* toggle handled by isSectionToggle */ }
313+ } ,
314+ kind : ActionListItemKind . Action ,
293315 label : localize ( 'chat.modelPicker.otherModels' , "Other Models" ) ,
294- run : ( ) => { /* toggle handled by isSectionToggle */ }
295- } ,
296- kind : ActionListItemKind . Action ,
297- label : localize ( 'chat.modelPicker.otherModels' , "Other Models" ) ,
298- group : { title : '' , icon : Codicon . chevronDown } ,
299- hideIcon : false ,
300- section : ModelPickerSection . Other ,
301- isSectionToggle : true ,
302- } ) ;
303- for ( const model of otherModels ) {
304- const entry = controlModels [ model . metadata . id ] ?? controlModels [ model . identifier ] ;
305- if ( entry ?. minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
306- items . push ( createUnavailableModelItem ( model . metadata . id , entry , 'update' , manageSettingsUrl , updateStateType , ModelPickerSection . Other , true ) ) ;
307- } else {
308- items . push ( createModelItem ( createModelAction ( model , selectedModelId , onSelect , ModelPickerSection . Other ) , model ) ) ;
316+ group : { title : '' , icon : Codicon . chevronDown } ,
317+ hideIcon : false ,
318+ section : ModelPickerSection . Other ,
319+ isSectionToggle : true ,
320+ } ) ;
321+ for ( const model of otherModels ) {
322+ const entry = controlModels [ model . metadata . id ] ?? controlModels [ model . identifier ] ;
323+ if ( entry ?. minVSCodeVersion && ! isVersionAtLeast ( currentVSCodeVersion , entry . minVSCodeVersion ) ) {
324+ items . push ( createUnavailableModelItem ( model . metadata . id , entry , 'update' , manageSettingsUrl , updateStateType , ModelPickerSection . Other , true ) ) ;
325+ } else {
326+ items . push ( createModelItem ( createModelAction ( model , selectedModelId , onSelect , ModelPickerSection . Other ) , model ) ) ;
327+ }
309328 }
310329 }
311330 }
@@ -513,21 +532,22 @@ export class ModelPickerWidget extends Disposable {
513532 const items = buildModelPickerItems (
514533 models ,
515534 this . _selectedModel ?. identifier ,
516- this . _languageModelsService . getRecentlyUsedModelIds ( ) ,
535+ showCuratedModels ? this . _languageModelsService . getRecentlyUsedModelIds ( ) : [ ] ,
517536 controlModelsForTier ,
518537 this . _productService . version ,
519538 this . _updateService . state . type ,
520539 onSelect ,
521540 this . _productService . defaultChatAgent ?. manageSettingsUrl ,
522541 this . _commandService ,
523- this . _entitlementService
542+ this . _entitlementService ,
543+ showCuratedModels
524544 ) ;
525545
526546 const listOptions = {
527547 showFilter : models . length >= 10 ,
528548 filterPlaceholder : localize ( 'chat.modelPicker.search' , "Search models" ) ,
529549 focusFilterOnOpen : true ,
530- collapsedByDefault : new Set ( [ ModelPickerSection . Other ] ) ,
550+ collapsedByDefault : showCuratedModels ? new Set ( [ ModelPickerSection . Other ] ) : new Set < string > ( ) ,
531551 minWidth : 300 ,
532552 } ;
533553 const previouslyFocusedElement = dom . getActiveElement ( ) ;
0 commit comments