Skip to content

Commit 946c4d0

Browse files
committed
fix(chat): Prune history if it's too large, populate statusCode in error msg, ignore log window as activeFile
1 parent db64637 commit 946c4d0

File tree

5 files changed

+107
-31
lines changed

5 files changed

+107
-31
lines changed

packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): { c
145145
},
146146
}
147147
}
148+
149+
if (document.relativeFilePath === 'amazonwebservices.amazon-q-vscode.Amazon Q Logs') {
150+
getLogger().debug('Active file is Amazon Q Logs, filter it out in the chat request')
151+
document = undefined
152+
cursorState = undefined
153+
}
148154
}
149155

150156
// service will throw validation exception if string is empty

packages/core/src/codewhispererChat/controllers/chat/controller.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,10 @@ export class ChatController {
690690
this.messenger.sendAsyncEventProgress(tabID, true, '')
691691
const session = this.sessionStorage.getSession(tabID)
692692
const toolUseWithError = session.toolUseWithError
693-
if (!toolUseWithError || !toolUseWithError.toolUse || !toolUseWithError.toolUse.input) {
693+
getLogger().debug(
694+
`processToolUseMessage: ${toolUseWithError?.toolUse.name}:${toolUseWithError?.toolUse.toolUseId} with error: ${toolUseWithError?.error}`
695+
)
696+
if (!toolUseWithError || !toolUseWithError.toolUse) {
694697
// Turn off AgentLoop flag if there's no tool use
695698
this.sessionStorage.setAgentLoopInProgress(tabID, false)
696699
return
@@ -771,7 +774,7 @@ export class ChatController {
771774
customization: getSelectedCustomization(),
772775
toolResults: toolResults,
773776
origin: Origin.IDE,
774-
context: session.context ?? [],
777+
context: [],
775778
relevantTextDocuments: [],
776779
additionalContents: [],
777780
documentReferences: [],
@@ -987,6 +990,7 @@ export class ChatController {
987990
private processException(e: any, tabID: string) {
988991
let errorMessage = ''
989992
let requestID = undefined
993+
let statusCode = undefined
990994
const defaultMessage = 'Failed to get response'
991995
if (typeof e === 'string') {
992996
errorMessage = e.toUpperCase()
@@ -996,6 +1000,7 @@ export class ChatController {
9961000
} else if (e instanceof CodeWhispererStreamingServiceException) {
9971001
errorMessage = e.message
9981002
requestID = e.$metadata.requestId
1003+
statusCode = e.$metadata.httpStatusCode
9991004
} else if (e instanceof Error) {
10001005
errorMessage = e.message
10011006
}
@@ -1005,11 +1010,11 @@ export class ChatController {
10051010
this.sessionStorage.setAgentLoopInProgress(tabID, false)
10061011
}
10071012

1008-
this.messenger.sendErrorMessage(errorMessage, tabID, requestID)
1013+
this.messenger.sendErrorMessage(errorMessage, tabID, requestID, statusCode)
10091014
getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`)
10101015

10111016
this.sessionStorage.deleteSession(tabID)
1012-
this.chatHistoryStorage.getTabHistory(tabID).clearRecentHistory()
1017+
this.chatHistoryStorage.getTabHistory(tabID).clear()
10131018
}
10141019

10151020
private async processContextMenuCommand(command: EditorContextCommand) {

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,9 @@ export class Messenger {
264264
}
265265

266266
if (cwChatEvent.toolUseEvent?.stop) {
267-
let toolError = undefined
267+
toolUse.toolUseId = cwChatEvent.toolUseEvent.toolUseId ?? ''
268+
toolUse.name = cwChatEvent.toolUseEvent.name ?? ''
268269
try {
269-
toolUse.toolUseId = cwChatEvent.toolUseEvent.toolUseId ?? ''
270-
toolUse.name = cwChatEvent.toolUseEvent.name ?? ''
271270
toolUse.input = JSON.parse(toolUseInput)
272271
const availableToolsNames = (session.pairProgrammingModeOn ? tools : noWriteTools).map(
273272
(item) => item.toolSpecification?.name
@@ -327,6 +326,10 @@ export class Messenger {
327326
) {
328327
session.setMessageIdToUpdateListDirectory(toolUse.toolUseId)
329328
}
329+
getLogger().debug(
330+
`SetToolUseWithError: ${toolUse.name}:${toolUse.toolUseId} with no error`
331+
)
332+
session.setToolUseWithError({ toolUse, error: undefined })
330333

331334
if (!validation.requiresAcceptance) {
332335
// Need separate id for read tool and safe bash command execution as 'run-shell-command' id is required to state in cwChatConnector.ts which will impact generic tool execution.
@@ -345,12 +348,19 @@ export class Messenger {
345348
}
346349
}
347350
} else {
348-
toolError = new Error('Tool not found')
351+
throw new Error('Tool not found')
349352
}
350353
} catch (error: any) {
351-
toolError = error
352-
} finally {
353-
session.setToolUseWithError({ toolUse, error: toolError })
354+
getLogger().error(
355+
`toolUseId: ${toolUse.toolUseId}, toolUseName: ${toolUse.name}, error: ${error}`
356+
)
357+
session.setToolUseWithError({ toolUse, error })
358+
// trigger processToolUseMessage to handle the error
359+
this.dispatcher.sendCustomFormActionMessage(
360+
new CustomFormActionMessage(tabID, {
361+
id: 'generic-tool-execution',
362+
})
363+
)
354364
}
355365
// TODO: Add a spinner component for fsWrite, previous implementation is causing lag in mynah UX.
356366
}
@@ -566,12 +576,17 @@ export class Messenger {
566576
)
567577
}
568578

569-
public sendErrorMessage(errorMessage: string | undefined, tabID: string, requestID: string | undefined) {
579+
public sendErrorMessage(
580+
errorMessage: string | undefined,
581+
tabID: string,
582+
requestID: string | undefined,
583+
statusCode?: number
584+
) {
570585
this.showChatExceptionMessage(
571586
{
572587
errorMessage: errorMessage,
573588
sessionID: undefined,
574-
statusCode: undefined,
589+
statusCode: statusCode?.toString(),
575590
},
576591
tabID,
577592
requestID

packages/core/src/codewhispererChat/storages/chatHistory.ts

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { ChatMessage, ToolResult, ToolResultStatus, ToolUse } from '@amzn/codewh
66
import { randomUUID } from '../../shared/crypto'
77
import { getLogger } from '../../shared/logger/logger'
88

9-
// Maximum number of messages to keep in history
10-
const MaxConversationHistoryLength = 100
9+
// Maximum number of characters to keep in history
10+
const MaxConversationHistoryCharacters = 600_000
1111

1212
/**
1313
* ChatHistoryManager handles the storage and manipulation of chat history
@@ -70,9 +70,6 @@ export class ChatHistoryManager {
7070
*/
7171
public appendUserMessage(newMessage: ChatMessage): void {
7272
this.lastUserMessage = newMessage
73-
if (!newMessage.userInputMessage?.content || newMessage.userInputMessage?.content.trim() === '') {
74-
this.logger.warn('input must not be empty when adding new messages')
75-
}
7673
this.history.push(this.formatChatHistoryMessage(this.lastUserMessage))
7774
}
7875

@@ -118,22 +115,69 @@ export class ChatHistoryManager {
118115
this.clearRecentHistory()
119116
}
120117

121-
if (this.history.length <= MaxConversationHistoryLength) {
122-
return
118+
// Check if we need to trim based on character count
119+
const totalCharacters = this.calculateHistoryCharacterCount()
120+
if (totalCharacters > MaxConversationHistoryCharacters) {
121+
this.logger.debug(
122+
`History size (${totalCharacters} chars) exceeds limit of ${MaxConversationHistoryCharacters} chars`
123+
)
124+
// Keep removing messages from the beginning until we're under the limit
125+
do {
126+
// Find the next valid user message to start from
127+
const indexToTrim = this.findIndexToTrim()
128+
if (indexToTrim !== undefined && indexToTrim > 0) {
129+
this.logger.debug(
130+
`Removing the first ${indexToTrim} elements in the history due to character count limit`
131+
)
132+
this.history.splice(0, indexToTrim)
133+
} else {
134+
// If we can't find a valid starting point, reset it
135+
this.logger.debug('Could not find a valid point to trim, reset history to reduce character count')
136+
this.history = []
137+
}
138+
} while (
139+
this.calculateHistoryCharacterCount() > MaxConversationHistoryCharacters &&
140+
this.history.length > 2
141+
)
123142
}
143+
}
124144

125-
const indexToTrim = this.findIndexToTrim()
126-
if (indexToTrim !== undefined) {
127-
this.logger.debug(`Removing the first ${indexToTrim} elements in the history`)
128-
this.history.splice(0, indexToTrim)
129-
} else {
130-
this.logger.debug('No valid starting user message found in the history, clearing')
131-
this.history = []
145+
private calculateHistoryCharacterCount(): number {
146+
let count = 0
147+
for (const message of this.history) {
148+
// Count characters in user messages
149+
if (message.userInputMessage?.content) {
150+
count += message.userInputMessage.content.length
151+
}
152+
153+
// Count characters in assistant messages
154+
if (message.assistantResponseMessage?.content) {
155+
count += message.assistantResponseMessage.content.length
156+
}
157+
158+
try {
159+
// Count characters in tool uses and results
160+
if (message.assistantResponseMessage?.toolUses) {
161+
for (const toolUse of message.assistantResponseMessage.toolUses) {
162+
count += JSON.stringify(toolUse).length
163+
}
164+
}
165+
166+
if (message.userInputMessage?.userInputMessageContext?.toolResults) {
167+
for (const toolResult of message.userInputMessage.userInputMessageContext.toolResults) {
168+
count += JSON.stringify(toolResult).length
169+
}
170+
}
171+
} catch (error: any) {
172+
this.logger.error(`Error calculating character count for tool uses/results: ${error.message}`)
173+
}
132174
}
175+
this.logger.debug(`Current history characters: ${count}`)
176+
return count
133177
}
134178

135179
private findIndexToTrim(): number | undefined {
136-
for (let i = 1; i < this.history.length; i++) {
180+
for (let i = 2; i < this.history.length; i++) {
137181
const message = this.history[i]
138182
if (this.isValidUserMessageWithoutToolResults(message)) {
139183
return i
@@ -162,6 +206,12 @@ export class ChatHistoryManager {
162206
private ensureCurrentMessageIsValid(newUserMessage: ChatMessage): void {
163207
const lastHistoryMessage = this.history[this.history.length - 1]
164208
if (!lastHistoryMessage) {
209+
if (newUserMessage.userInputMessage?.userInputMessageContext?.toolResults) {
210+
this.logger.debug('No history message found, but new user message has tool results.')
211+
newUserMessage.userInputMessage.userInputMessageContext.toolResults = undefined
212+
// tool results are empty, so content must not be empty
213+
newUserMessage.userInputMessage.content = 'Conversation history was too large, so it was cleared.'
214+
}
165215
return
166216
}
167217

@@ -198,8 +248,8 @@ export class ChatHistoryManager {
198248
userInputMessage: {
199249
...message.userInputMessage,
200250
userInputMessageContext: {
201-
...message.userInputMessage.userInputMessageContext,
202-
tools: undefined,
251+
// Only keep toolResults in history
252+
toolResults: message.userInputMessage.userInputMessageContext?.toolResults,
203253
},
204254
},
205255
}

packages/core/src/codewhispererChat/tools/chatStream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class ChatStream extends Writable {
3333
) {
3434
super()
3535
this.logger.debug(
36-
`ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}, session: ${session.readFiles}, emitEvent to mynahUI: ${emitEvent}`
36+
`ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}, emitEvent to mynahUI: ${emitEvent}`
3737
)
3838
if (!emitEvent) {
3939
return

0 commit comments

Comments
 (0)