@@ -6,8 +6,8 @@ import { ChatMessage, ToolResult, ToolResultStatus, ToolUse } from '@amzn/codewh
66import { randomUUID } from '../../shared/crypto'
77import { 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 }
0 commit comments