@@ -94,10 +94,11 @@ class AutoModeTokenBank extends Disposable {
9494 : 'copilotchat.autoModelHint' ;
9595
9696 const autoModeHint = this . _expService . getTreatmentVariable < string > ( expName ) || 'auto' ;
97+ console . log ( `AutoModeService: Using auto mode hint '${ autoModeHint } ' for location '${ this . _location } '.` ) ;
9798
9899 const response = await this . _capiClientService . makeRequest < Response > ( {
99100 json : {
100- 'auto_mode' : { 'model_hints' : [ autoModeHint ] }
101+ 'auto_mode' : { 'model_hints' : [ 'grok-code-fast-1' ] }
101102 } ,
102103 headers,
103104 method : 'POST'
@@ -229,6 +230,8 @@ export class AutomodeService extends Disposable implements IAutomodeService {
229230 throw new Error ( errorMsg ) ;
230231 }
231232 }
233+ selectedModel = this . _applyVisionFallback ( chatRequest , selectedModel , reserveToken . available_models , knownEndpoints ) ;
234+
232235 const existingEndpoints = entry ?. endpoints || [ ] ;
233236 let autoEndpoint = existingEndpoints . find ( e => e . model === selectedModel . model ) ;
234237 if ( ! autoEndpoint ) {
@@ -259,7 +262,15 @@ export class AutomodeService extends Disposable implements IAutomodeService {
259262 }
260263 entry . endpoints = [ this . _instantiationService . createInstance ( AutoChatEndpoint , newModel , entryToken . session_token , entryToken . discounted_costs ?. [ newModel . model ] || 0 , this . _calculateDiscountRange ( entryToken . discounted_costs ) ) ] ;
261264 }
262- return entry . endpoints [ 0 ] ;
265+ // Apply vision fallback even on cached entries, since the cached model may not support images
266+ const cachedEndpoint = entry . endpoints [ 0 ] ;
267+ const fallbackEndpoint = this . _applyVisionFallback ( chatRequest , cachedEndpoint , entryToken . available_models , knownEndpoints ) ;
268+ if ( fallbackEndpoint !== cachedEndpoint ) {
269+ const autoEndpoint = this . _instantiationService . createInstance ( AutoChatEndpoint , fallbackEndpoint , entryToken . session_token , entryToken . discounted_costs ?. [ fallbackEndpoint . model ] || 0 , this . _calculateDiscountRange ( entryToken . discounted_costs ) ) ;
270+ entry . endpoints [ 0 ] = autoEndpoint ;
271+ return autoEndpoint ;
272+ }
273+ return cachedEndpoint ;
263274 }
264275
265276 // No cached entry, use the reserve token
@@ -269,18 +280,38 @@ export class AutomodeService extends Disposable implements IAutomodeService {
269280 reserveTokenBank . debugName = conversationId ;
270281
271282 const reserveToken = await reserveTokenBank . getToken ( ) ;
272- const selectedModel = knownEndpoints . find ( e => e . model === reserveToken . selected_model ) ;
283+ let selectedModel = knownEndpoints . find ( e => e . model === reserveToken . selected_model ) ;
273284 if ( ! selectedModel ) {
274285 const errorMsg = `Auto mode failed: selected model '${ reserveToken . selected_model } ' not found in known endpoints.` ;
275286 this . _logService . error ( errorMsg ) ;
276287 throw new Error ( errorMsg ) ;
277288 }
289+ selectedModel = this . _applyVisionFallback ( chatRequest , selectedModel , reserveToken . available_models , knownEndpoints ) ;
278290 const autoEndpoint = this . _instantiationService . createInstance ( AutoChatEndpoint , selectedModel , reserveToken . session_token , reserveToken . discounted_costs ?. [ selectedModel . model ] || 0 , this . _calculateDiscountRange ( reserveToken . discounted_costs ) ) ;
279291
280292 this . _autoModelCache . set ( conversationId , { endpoints : [ autoEndpoint ] , tokenBank : reserveTokenBank } ) ;
281293 return autoEndpoint ;
282294 }
283295
296+ /**
297+ * If the request contains an image and the selected model doesn't support vision,
298+ * fall back to the first vision-capable model from the available models.
299+ */
300+ private _applyVisionFallback ( chatRequest : ChatRequest | undefined , selectedModel : IChatEndpoint , availableModels : string [ ] , knownEndpoints : IChatEndpoint [ ] ) : IChatEndpoint {
301+ if ( ! hasImage ( chatRequest ) || selectedModel . supportsVision ) {
302+ return selectedModel ;
303+ }
304+ const visionModel = availableModels
305+ . map ( model => knownEndpoints . find ( e => e . model === model ) )
306+ . find ( endpoint => endpoint ?. supportsVision ) ;
307+ if ( visionModel ) {
308+ this . _logService . trace ( `Selected model '${ selectedModel . model } ' does not support vision, falling back to '${ visionModel . model } '.` ) ;
309+ return visionModel ;
310+ }
311+ this . _logService . warn ( `Request contains an image but no vision-capable model is available.` ) ;
312+ return selectedModel ;
313+ }
314+
284315 private _calculateDiscountRange ( discounts : Record < string , number > | undefined ) : { low : number ; high : number } {
285316 if ( ! discounts ) {
286317 return { low : 0 , high : 0 } ;
@@ -311,5 +342,19 @@ function getConversationId(chatRequest: ChatRequest | undefined): string {
311342 if ( ! chatRequest ) {
312343 return 'unknown' ;
313344 }
314- return ( chatRequest ?. toolInvocationToken as { sessionId : string } ) ?. sessionId || 'unknown' ;
345+ return chatRequest ?. sessionId || 'unknown' ;
346+ }
347+
348+ function hasImage ( chatRequest : ChatRequest | undefined ) : boolean {
349+ if ( ! chatRequest || ! chatRequest . references ) {
350+ return false ;
351+ }
352+ return chatRequest . references . some ( ref => {
353+ const value = ref . value ;
354+ return typeof value === 'object' &&
355+ value !== null &&
356+ 'mimeType' in value &&
357+ typeof value . mimeType === 'string'
358+ && value . mimeType . startsWith ( 'image/' ) ;
359+ } ) ;
315360}
0 commit comments