Skip to content

Commit 9b5cef6

Browse files
committed
Adding state Management to stop the response for new user input prompt
1 parent 640ccb6 commit 9b5cef6

File tree

3 files changed

+90
-56
lines changed

3 files changed

+90
-56
lines changed

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ export class ChatController {
368368
private async processStopResponseMessage(message: StopResponseMessage) {
369369
const session = this.sessionStorage.getSession(message.tabID)
370370
session.tokenSource.cancel()
371+
this.chatHistoryStorage.getTabHistory(message.tabID).clearRecentHistory()
371372
}
372373

373374
private async processTriggerTabIDReceived(message: TriggerTabIDReceived) {
@@ -650,6 +651,8 @@ export class ChatController {
650651
const session = this.sessionStorage.getSession(tabID)
651652
const toolUse = session.toolUse
652653
if (!toolUse || !toolUse.input) {
654+
// Turn off AgentLoop flag if there's no tool use
655+
this.sessionStorage.setAgentLoopInProgress(tabID, false)
653656
return
654657
}
655658
session.setToolUse(undefined)
@@ -711,7 +714,6 @@ export class ChatController {
711714
customization: getSelectedCustomization(),
712715
toolResults: toolResults,
713716
origin: Origin.IDE,
714-
chatHistory: this.chatHistoryStorage.getTabHistory(tabID).getHistory(),
715717
context: session.context ?? [],
716718
relevantTextDocuments: [],
717719
additionalContents: [],
@@ -887,10 +889,16 @@ export class ChatController {
887889
errorMessage = e.message
888890
}
889891

892+
// Turn off AgentLoop flag in case of exception
893+
if (tabID) {
894+
this.sessionStorage.setAgentLoopInProgress(tabID, false)
895+
}
896+
890897
this.messenger.sendErrorMessage(errorMessage, tabID, requestID)
891898
getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`)
892899

893900
this.sessionStorage.deleteSession(tabID)
901+
this.chatHistoryStorage.getTabHistory(tabID).clearRecentHistory()
894902
}
895903

896904
private async processContextMenuCommand(command: EditorContextCommand) {
@@ -1050,7 +1058,6 @@ export class ChatController {
10501058
codeQuery: lastTriggerEvent.context?.focusAreaContext?.names,
10511059
userIntent: message.userIntent,
10521060
customization: getSelectedCustomization(),
1053-
chatHistory: this.chatHistoryStorage.getTabHistory(message.tabID).getHistory(),
10541061
contextLengths: {
10551062
...defaultContextLengths,
10561063
},
@@ -1099,7 +1106,6 @@ export class ChatController {
10991106
codeQuery: context?.focusAreaContext?.names,
11001107
userIntent: this.userIntentRecognizer.getFromPromptChatMessage(message),
11011108
customization: getSelectedCustomization(),
1102-
chatHistory: this.chatHistoryStorage.getTabHistory(message.tabID).getHistory(),
11031109
origin: Origin.IDE,
11041110
context: message.context ?? [],
11051111
relevantTextDocuments: [],
@@ -1281,6 +1287,16 @@ export class ChatController {
12811287
}
12821288

12831289
const tabID = triggerEvent.tabID
1290+
if (this.sessionStorage.isAgentLoopInProgress(tabID)) {
1291+
// If a response is already in progress, stop it first
1292+
const stopResponseMessage: StopResponseMessage = {
1293+
tabID: tabID,
1294+
}
1295+
await this.processStopResponseMessage(stopResponseMessage)
1296+
}
1297+
1298+
// Ensure AgentLoop flag is set to true during response generation
1299+
this.sessionStorage.setAgentLoopInProgress(tabID, true)
12841300

12851301
const credentialsState = await AuthUtil.instance.getChatAuthState()
12861302

@@ -1343,6 +1359,7 @@ export class ChatController {
13431359
if (fixedHistoryMessage.userInputMessage?.userInputMessageContext) {
13441360
triggerPayload.toolResults = fixedHistoryMessage.userInputMessage.userInputMessageContext.toolResults
13451361
}
1362+
triggerPayload.chatHistory = chatHistory.getHistory()
13461363
const request = triggerPayloadToChatRequest(triggerPayload)
13471364
const conversationId = chatHistory.getConversationId() || randomUUID()
13481365
chatHistory.setConversationId(conversationId)
@@ -1405,8 +1422,13 @@ export class ChatController {
14051422
} metadata: ${inspect(response.$metadata, { depth: 12 })}`
14061423
)
14071424
await this.messenger.sendAIResponse(response, session, tabID, triggerID, triggerPayload, chatHistory)
1425+
1426+
// Turn off AgentLoop flag after sending the AI response
1427+
this.sessionStorage.setAgentLoopInProgress(tabID, false)
14081428
} catch (e: any) {
14091429
this.telemetryHelper.recordMessageResponseError(triggerPayload, tabID, getHttpStatusCode(e) ?? 0)
1430+
// Turn off AgentLoop flag in case of exception
1431+
this.sessionStorage.setAgentLoopInProgress(tabID, false)
14101432
// clears session, record telemetry before this call
14111433
this.processException(e, tabID)
14121434
}

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

Lines changed: 45 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,37 @@ export class ChatHistoryManager {
188188
return newUserMessage
189189
}
190190
}
191+
} else {
192+
if (
193+
newUserMessage.userInputMessage?.userInputMessageContext?.toolResults &&
194+
newUserMessage.userInputMessage?.userInputMessageContext?.toolResults.length > 0
195+
) {
196+
// correct toolUse section of lastAssistantResponse in case of empty toolUse for user message with tool Results.
197+
const toolResults = newUserMessage.userInputMessage.userInputMessageContext.toolResults
198+
const updatedToolUses = toolResults.map((toolResult) => ({
199+
toolUseId: toolResult.toolUseId,
200+
name: lastHistoryMessage.assistantResponseMessage.toolUses?.find(
201+
(tu) => tu.toolUseId === toolResult.toolUseId
202+
)?.name,
203+
input: lastHistoryMessage.assistantResponseMessage.toolUses?.find(
204+
(tu) => tu.toolUseId === toolResult.toolUseId
205+
)?.input,
206+
}))
207+
208+
// Create a new assistant response message with updated toolUses
209+
const updatedAssistantResponseMessage = {
210+
...lastHistoryMessage.assistantResponseMessage,
211+
toolUses: updatedToolUses,
212+
}
213+
214+
// Create a new chat message with the updated assistant response
215+
const updatedChatMessage: ChatMessage = {
216+
assistantResponseMessage: updatedAssistantResponseMessage,
217+
}
218+
219+
// Replace the last message in history
220+
this.history[this.history.length - 1] = updatedChatMessage
221+
}
191222
}
192223
}
193224

@@ -216,59 +247,6 @@ export class ChatHistoryManager {
216247
}
217248
}
218249

219-
/**
220-
* Checks if the latest message in history is an Assistant Message.
221-
* If it is and doesn't have toolUse, it will be removed.
222-
* If it has toolUse, an assistantResponse message with cancelled tool status will be added.
223-
*/
224-
public checkLatestAssistantMessage(): void {
225-
if (this.history.length === 0) {
226-
return
227-
}
228-
229-
const lastMessage = this.history[this.history.length - 1]
230-
231-
if (lastMessage.assistantResponseMessage) {
232-
const toolUses = lastMessage.assistantResponseMessage.toolUses
233-
234-
if (!toolUses || toolUses.length === 0) {
235-
// If there are no tool uses, remove the assistant message
236-
this.logger.debug('Removing assistant message without tool uses')
237-
this.history.pop()
238-
} else {
239-
// If there are tool uses, add cancelled tool results
240-
const toolResults = toolUses.map((toolUse) => ({
241-
toolUseId: toolUse.toolUseId,
242-
content: [
243-
{
244-
type: 'Text',
245-
text: 'Tool use was cancelled by the user',
246-
},
247-
],
248-
status: ToolResultStatus.ERROR,
249-
}))
250-
251-
// Create a new user message with cancelled tool results
252-
const userInputMessageContext: UserInputMessageContext = {
253-
shellState: undefined,
254-
envState: undefined,
255-
toolResults: toolResults,
256-
tools: this.tools.length === 0 ? undefined : [...this.tools],
257-
}
258-
259-
const userMessage: ChatMessage = {
260-
userInputMessage: {
261-
content: '',
262-
userInputMessageContext: userInputMessageContext,
263-
},
264-
}
265-
266-
this.history.push(this.formatChatHistoryMessage(userMessage))
267-
this.logger.debug('Added user message with cancelled tool results')
268-
}
269-
}
270-
}
271-
272250
private formatChatHistoryMessage(message: ChatMessage): ChatMessage {
273251
if (message.userInputMessage !== undefined) {
274252
return {
@@ -283,4 +261,18 @@ export class ChatHistoryManager {
283261
}
284262
return message
285263
}
264+
265+
public clearRecentHistory(): void {
266+
if (this.history.length === 0) {
267+
return
268+
}
269+
270+
const lastHistoryMessage = this.history[this.history.length - 1]
271+
272+
if (lastHistoryMessage.userInputMessage?.userInputMessageContext) {
273+
this.history.pop()
274+
} else if (lastHistoryMessage.assistantResponseMessage) {
275+
this.history.splice(-2)
276+
}
277+
}
286278
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ChatSession } from '../clients/chat/v0/chat'
77

88
export class ChatSessionStorage {
99
private sessions: Map<string, ChatSession> = new Map()
10+
private agentLoopInProgress: Map<string, boolean> = new Map()
1011

1112
public getSession(tabID: string): ChatSession {
1213
const sessionFromStorage = this.sessions.get(tabID)
@@ -22,5 +23,24 @@ export class ChatSessionStorage {
2223

2324
public deleteSession(tabID: string) {
2425
this.sessions.delete(tabID)
26+
this.agentLoopInProgress.delete(tabID)
27+
}
28+
29+
/**
30+
* Check if agent loop is in progress for a specific tab
31+
* @param tabID The tab ID to check
32+
* @returns True if agent loop is in progress, false otherwise
33+
*/
34+
public isAgentLoopInProgress(tabID: string): boolean {
35+
return this.agentLoopInProgress.get(tabID) === true
36+
}
37+
38+
/**
39+
* Set agent loop in progress state for a specific tab
40+
* @param tabID The tab ID to set state for
41+
* @param inProgress Whether the agent loop is in progress
42+
*/
43+
public setAgentLoopInProgress(tabID: string, inProgress: boolean): void {
44+
this.agentLoopInProgress.set(tabID, inProgress)
2545
}
2646
}

0 commit comments

Comments
 (0)