Skip to content

Commit 68dacc4

Browse files
committed
fix(chat): Prune history if it's too large, populate statusCode in error message correctly
1 parent db64637 commit 68dacc4

File tree

3 files changed

+85
-18
lines changed

3 files changed

+85
-18
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ export class ChatController {
987987
private processException(e: any, tabID: string) {
988988
let errorMessage = ''
989989
let requestID = undefined
990+
let statusCode = undefined
990991
const defaultMessage = 'Failed to get response'
991992
if (typeof e === 'string') {
992993
errorMessage = e.toUpperCase()
@@ -996,6 +997,7 @@ export class ChatController {
996997
} else if (e instanceof CodeWhispererStreamingServiceException) {
997998
errorMessage = e.message
998999
requestID = e.$metadata.requestId
1000+
statusCode = e.$metadata.httpStatusCode
9991001
} else if (e instanceof Error) {
10001002
errorMessage = e.message
10011003
}
@@ -1005,7 +1007,7 @@ export class ChatController {
10051007
this.sessionStorage.setAgentLoopInProgress(tabID, false)
10061008
}
10071009

1008-
this.messenger.sendErrorMessage(errorMessage, tabID, requestID)
1010+
this.messenger.sendErrorMessage(errorMessage, tabID, requestID, statusCode)
10091011
getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`)
10101012

10111013
this.sessionStorage.deleteSession(tabID)

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,12 +566,17 @@ export class Messenger {
566566
)
567567
}
568568

569-
public sendErrorMessage(errorMessage: string | undefined, tabID: string, requestID: string | undefined) {
569+
public sendErrorMessage(
570+
errorMessage: string | undefined,
571+
tabID: string,
572+
requestID: string | undefined,
573+
statusCode?: number
574+
) {
570575
this.showChatExceptionMessage(
571576
{
572577
errorMessage: errorMessage,
573578
sessionID: undefined,
574-
statusCode: undefined,
579+
statusCode: statusCode?.toString(),
575580
},
576581
tabID,
577582
requestID

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

Lines changed: 75 additions & 15 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,81 @@ 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+
count += message.userInputMessage.userInputMessageContext?.editorState?.document?.text?.length ?? 0
152+
if (message.userInputMessage.userInputMessageContext?.editorState?.relevantDocuments) {
153+
for (const document of message.userInputMessage.userInputMessageContext.editorState
154+
.relevantDocuments) {
155+
count += document.text?.length ?? 0
156+
}
157+
}
158+
if (message.userInputMessage.userInputMessageContext?.additionalContext) {
159+
for (const document of message.userInputMessage.userInputMessageContext.additionalContext) {
160+
count += document.innerContext?.length ?? 0
161+
}
162+
}
163+
}
164+
165+
// Count characters in assistant messages
166+
if (message.assistantResponseMessage?.content) {
167+
count += message.assistantResponseMessage.content.length
168+
}
169+
170+
try {
171+
// Count characters in tool uses and results
172+
if (message.assistantResponseMessage?.toolUses) {
173+
for (const toolUse of message.assistantResponseMessage.toolUses) {
174+
count += JSON.stringify(toolUse).length
175+
}
176+
}
177+
178+
if (message.userInputMessage?.userInputMessageContext?.toolResults) {
179+
for (const toolResult of message.userInputMessage.userInputMessageContext.toolResults) {
180+
count += JSON.stringify(toolResult).length
181+
}
182+
}
183+
} catch (error: any) {
184+
this.logger.error(`Error calculating character count for tool uses/results: ${error.message}`)
185+
}
132186
}
187+
this.logger.debug(`Current history characters: ${count}`)
188+
return count
133189
}
134190

135191
private findIndexToTrim(): number | undefined {
136-
for (let i = 1; i < this.history.length; i++) {
192+
for (let i = 2; i < this.history.length; i++) {
137193
const message = this.history[i]
138194
if (this.isValidUserMessageWithoutToolResults(message)) {
139195
return i
@@ -162,6 +218,10 @@ export class ChatHistoryManager {
162218
private ensureCurrentMessageIsValid(newUserMessage: ChatMessage): void {
163219
const lastHistoryMessage = this.history[this.history.length - 1]
164220
if (!lastHistoryMessage) {
221+
if (newUserMessage.userInputMessage?.userInputMessageContext?.toolResults) {
222+
this.logger.debug('No history message found, but new user message has tool results.')
223+
newUserMessage.userInputMessage.userInputMessageContext.toolResults = undefined
224+
}
165225
return
166226
}
167227

0 commit comments

Comments
 (0)