@@ -210,7 +210,11 @@ import {
210
210
PaidTierMode ,
211
211
qProName ,
212
212
} from '../paidTier/paidTier'
213
- import { Message as DbMessage , messageToStreamingMessage } from './tools/chatDb/util'
213
+ import {
214
+ estimateCharacterCountFromImageBlock ,
215
+ Message as DbMessage ,
216
+ messageToStreamingMessage ,
217
+ } from './tools/chatDb/util'
214
218
import { MODEL_OPTIONS , MODEL_OPTIONS_FOR_REGION } from './constants/modelSelection'
215
219
import { DEFAULT_IMAGE_VERIFICATION_OPTIONS , verifyServerImage } from '../../shared/imageVerification'
216
220
import { sanitize } from '@aws/lsp-core/out/util/path'
@@ -813,12 +817,6 @@ export class AgenticChatController implements ChatHandlers {
813
817
params . context ,
814
818
params . tabId
815
819
)
816
- // Add image context to triggerContext.documentReference for transparency
817
- await this . #additionalContextProvider. appendCustomContextToTriggerContext (
818
- triggerContext ,
819
- params . context ,
820
- params . tabId
821
- )
822
820
823
821
let finalResult
824
822
if ( params . prompt . command === QuickAction . Compact ) {
@@ -866,7 +864,7 @@ export class AgenticChatController implements ChatHandlers {
866
864
session . conversationId ,
867
865
token ,
868
866
triggerContext . documentReference ,
869
- additionalContext . filter ( item => item . pinned )
867
+ additionalContext
870
868
)
871
869
}
872
870
@@ -927,7 +925,7 @@ export class AgenticChatController implements ChatHandlers {
927
925
triggerContext : TriggerContext ,
928
926
additionalContext : AdditionalContentEntryAddition [ ] ,
929
927
chatResultStream : AgenticChatResultStream ,
930
- customContext : ImageBlock [ ]
928
+ images : ImageBlock [ ]
931
929
) : Promise < ChatCommandInput > {
932
930
this . #debug( 'Preparing request input' )
933
931
// Get profileArn from the service manager if available
@@ -943,7 +941,7 @@ export class AgenticChatController implements ChatHandlers {
943
941
additionalContext ,
944
942
session . modelId ,
945
943
this . #origin,
946
- customContext
944
+ images
947
945
)
948
946
return requestInput
949
947
}
@@ -1141,13 +1139,15 @@ export class AgenticChatController implements ChatHandlers {
1141
1139
conversationIdentifier ?: string ,
1142
1140
token ?: CancellationToken ,
1143
1141
documentReference ?: FileList ,
1144
- pinnedContext ?: AdditionalContentEntryAddition [ ]
1142
+ additionalContext ?: AdditionalContentEntryAddition [ ]
1145
1143
) : Promise < Result < AgenticChatResultWithMetadata , string > > {
1146
1144
let currentRequestInput = { ...initialRequestInput }
1147
1145
let finalResult : Result < AgenticChatResultWithMetadata , string > | null = null
1148
1146
let iterationCount = 0
1149
1147
let shouldDisplayMessage = true
1150
1148
let currentRequestCount = 0
1149
+ const pinnedContext = additionalContext ?. filter ( item => item . pinned )
1150
+
1151
1151
metric . recordStart ( )
1152
1152
this . logSystemInformation ( )
1153
1153
while ( true ) {
@@ -1164,7 +1164,7 @@ export class AgenticChatController implements ChatHandlers {
1164
1164
throw new CancellationError ( 'user' )
1165
1165
}
1166
1166
1167
- this . truncateRequest ( currentRequestInput , pinnedContext )
1167
+ this . truncateRequest ( currentRequestInput , additionalContext )
1168
1168
const currentMessage = currentRequestInput . conversationState ?. currentMessage
1169
1169
const conversationId = conversationIdentifier ?? ''
1170
1170
if ( ! currentMessage || ! conversationId ) {
@@ -1248,6 +1248,7 @@ export class AgenticChatController implements ChatHandlers {
1248
1248
shouldDisplayMessage &&
1249
1249
! currentMessage . userInputMessage ?. content ?. startsWith ( 'You are Amazon Q' ) ,
1250
1250
timestamp : new Date ( ) ,
1251
+ images : currentMessage . userInputMessage ?. images ,
1251
1252
} )
1252
1253
}
1253
1254
}
@@ -1473,7 +1474,7 @@ export class AgenticChatController implements ChatHandlers {
1473
1474
* Returns the remaining character budget for chat history.
1474
1475
* @param request
1475
1476
*/
1476
- truncateRequest ( request : ChatCommandInput , pinnedContext ?: AdditionalContentEntryAddition [ ] ) : number {
1477
+ truncateRequest ( request : ChatCommandInput , additionalContext ?: AdditionalContentEntryAddition [ ] ) : number {
1477
1478
// TODO: Confirm if this limit applies to SendMessage and rename this constant
1478
1479
let remainingCharacterBudget = GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT
1479
1480
if ( ! request ?. conversationState ?. currentMessage ?. userInputMessage ) {
@@ -1494,22 +1495,69 @@ export class AgenticChatController implements ChatHandlers {
1494
1495
}
1495
1496
}
1496
1497
1497
- // 2. try to fit @context into budget
1498
- let truncatedRelevantDocuments = [ ]
1498
+ // 2. try to fit @context and images into budget together
1499
+ const docs =
1500
+ request . conversationState . currentMessage . userInputMessage . userInputMessageContext ?. editorState
1501
+ ?. relevantDocuments ?? [ ]
1502
+ const images = request . conversationState . currentMessage . userInputMessage . images ?? [ ]
1503
+
1504
+ // Combine docs and images, preserving the order from additionalContext
1505
+ let combined
1506
+ if ( additionalContext && additionalContext . length > 0 ) {
1507
+ let docIdx = 0
1508
+ let imageIdx = 0
1509
+ combined = additionalContext
1510
+ . map ( entry => {
1511
+ if ( entry . type === 'image' ) {
1512
+ return { type : 'image' , value : images [ imageIdx ++ ] }
1513
+ } else {
1514
+ return { type : 'doc' , value : docs [ docIdx ++ ] }
1515
+ }
1516
+ } )
1517
+ . filter ( item => item . value !== undefined )
1518
+ } else {
1519
+ combined = [
1520
+ ...docs . map ( d => ( { type : 'doc' , value : d } ) ) ,
1521
+ ...images . map ( i => ( { type : 'image' , value : i } ) ) ,
1522
+ ]
1523
+ }
1524
+
1525
+ const truncatedDocs : typeof docs = [ ]
1526
+ const truncatedImages : typeof images = [ ]
1527
+ for ( const item of combined ) {
1528
+ let itemLength = 0
1529
+ if ( item . type === 'doc' ) {
1530
+ itemLength = ( item . value as any ) ?. text ?. length || 0
1531
+ if ( remainingCharacterBudget >= itemLength ) {
1532
+ truncatedDocs . push ( item . value as ( typeof docs ) [ number ] )
1533
+ remainingCharacterBudget -= itemLength
1534
+ }
1535
+ } else if ( item . type === 'image' ) {
1536
+ // Type guard: only call on ImageBlock
1537
+ if ( item . value && typeof item . value === 'object' && 'format' in item . value && 'source' in item . value ) {
1538
+ itemLength = estimateCharacterCountFromImageBlock ( item . value )
1539
+ if ( remainingCharacterBudget >= itemLength ) {
1540
+ truncatedImages . push ( item . value as ( typeof images ) [ number ] )
1541
+ remainingCharacterBudget -= itemLength
1542
+ }
1543
+ }
1544
+ }
1545
+ }
1546
+
1547
+ // Assign truncated lists back to request
1499
1548
if (
1500
1549
request . conversationState . currentMessage . userInputMessage . userInputMessageContext ?. editorState
1501
1550
?. relevantDocuments
1502
1551
) {
1503
- for ( const relevantDoc of request . conversationState . currentMessage . userInputMessage . userInputMessageContext
1504
- ?. editorState ?. relevantDocuments ) {
1505
- const docLength = relevantDoc ?. text ?. length || 0
1506
- if ( remainingCharacterBudget > docLength ) {
1507
- truncatedRelevantDocuments . push ( relevantDoc )
1508
- remainingCharacterBudget = remainingCharacterBudget - docLength
1509
- }
1510
- }
1511
1552
request . conversationState . currentMessage . userInputMessage . userInputMessageContext . editorState . relevantDocuments =
1512
- truncatedRelevantDocuments
1553
+ truncatedDocs
1554
+ }
1555
+
1556
+ if (
1557
+ request . conversationState . currentMessage . userInputMessage . images !== undefined &&
1558
+ request . conversationState . currentMessage . userInputMessage . images . length > 0
1559
+ ) {
1560
+ request . conversationState . currentMessage . userInputMessage . images = truncatedImages
1513
1561
}
1514
1562
1515
1563
// 3. try to fit current file context
@@ -1528,6 +1576,8 @@ export class AgenticChatController implements ChatHandlers {
1528
1576
truncatedCurrentDocument
1529
1577
}
1530
1578
1579
+ const pinnedContext = additionalContext ?. filter ( item => item . pinned )
1580
+
1531
1581
// 4. try to fit pinned context into budget
1532
1582
if ( pinnedContext && pinnedContext . length > 0 ) {
1533
1583
remainingCharacterBudget = this . truncatePinnedContext ( remainingCharacterBudget , pinnedContext )
@@ -2901,6 +2951,9 @@ export class AgenticChatController implements ChatHandlers {
2901
2951
useRelevantDocuments : false ,
2902
2952
}
2903
2953
2954
+ // Clear images to avoid passing them again in follow-up toolUse/toolResult loops, as it is may confuse the model
2955
+ updatedRequestInput . conversationState ! . currentMessage ! . userInputMessage ! . images = [ ]
2956
+
2904
2957
for ( const toolResult of toolResults ) {
2905
2958
this . #debug( `ToolResult: ${ JSON . stringify ( toolResult ) } ` )
2906
2959
updatedRequestInput . conversationState ! . currentMessage ! . userInputMessage ! . userInputMessageContext ! . toolResults . push (
0 commit comments