in packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts [130:377]
public async sendAIResponse(
response: MessengerResponseType,
session: ChatSession,
tabID: string,
triggerID: string,
triggerPayload: TriggerPayload,
cancelToken: vscode.CancellationToken
) {
let message = ''
const messageID = response.$metadata.requestId ?? ''
let codeReference: CodeReference[] = []
let followUps: FollowUp[] = []
let relatedSuggestions: Suggestion[] = []
let codeBlockLanguage: string = 'plaintext'
if (response.message === undefined) {
throw new ToolkitError(
`Empty response from CodeWhisperer Streaming service. Request ID: ${response.$metadata.requestId}`
)
}
this.telemetryHelper.setResponseStreamStartTime(tabID)
let cwsprChatHasProjectContext = false
if (
triggerPayload.relevantTextDocuments &&
triggerPayload.relevantTextDocuments.length > 0 &&
triggerPayload.useRelevantDocuments === true
) {
cwsprChatHasProjectContext = true
}
const additionalCounts = this.telemetryHelper.getAdditionalContextCounts(triggerPayload)
this.telemetryHelper.setResponseFromAdditionalContext(messageID, {
cwsprChatHasProjectContext,
cwsprChatRuleContextCount: triggerPayload.workspaceRulesCount,
cwsprChatFileContextCount: additionalCounts.fileContextCount,
cwsprChatFolderContextCount: additionalCounts.folderContextCount,
cwsprChatPromptContextCount: additionalCounts.promptContextCount,
})
const eventCounts = new Map<string, number>()
waitUntil(
async () => {
for await (const chatEvent of response.message!) {
if (cancelToken.isCancellationRequested) {
return
}
for (const key of keys(chatEvent)) {
if ((chatEvent[key] as any) !== undefined) {
eventCounts.set(key, (eventCounts.get(key) ?? 0) + 1)
}
}
if (session.tokenSource.token.isCancellationRequested) {
return true
}
if (
chatEvent.codeReferenceEvent?.references !== undefined &&
chatEvent.codeReferenceEvent.references.length > 0
) {
codeReference = [
...codeReference,
...chatEvent.codeReferenceEvent.references.map((reference) => ({
...reference,
recommendationContentSpan: {
start: reference.recommendationContentSpan?.start ?? 0,
end: reference.recommendationContentSpan?.end ?? 0,
},
information: `Reference code under **${reference.licenseName}** license from repository \`${reference.repository}\``,
})),
]
}
if (
chatEvent.assistantResponseEvent?.content !== undefined &&
chatEvent.assistantResponseEvent.content.length > 0
) {
message += chatEvent.assistantResponseEvent.content
if (codeBlockLanguage === 'plaintext') {
codeBlockLanguage = extractCodeBlockLanguage(message)
}
this.dispatcher.sendChatMessage(
new ChatMessage(
{
message: message,
messageType: 'answer-part',
followUps: undefined,
followUpsHeader: undefined,
relatedSuggestions: undefined,
codeReference,
triggerID,
messageID,
userIntent: triggerPayload.userIntent,
codeBlockLanguage: codeBlockLanguage,
contextList: undefined,
},
tabID
)
)
this.telemetryHelper.setResponseStreamTimeForChunks(tabID)
}
if (chatEvent.supplementaryWebLinksEvent?.supplementaryWebLinks !== undefined) {
let suggestionIndex = 0
const newSuggestions: Suggestion[] =
chatEvent.supplementaryWebLinksEvent.supplementaryWebLinks.map(
(s: SupplementaryWebLink) =>
new Suggestion({
title: s.title ?? '',
url: s.url ?? '',
body: s.snippet ?? '',
id: suggestionIndex++,
context: [],
})
)
relatedSuggestions.push(...newSuggestions)
}
if (chatEvent.followupPromptEvent?.followupPrompt !== undefined) {
const followUp = chatEvent.followupPromptEvent.followupPrompt
followUps.push({
type: followUp.userIntent ?? '',
pillText: followUp.content ?? '',
prompt: followUp.content ?? '',
})
}
}
return true
},
{ timeout: 60000, truthy: true }
)
.catch((error: any) => {
let errorMessage = 'Error reading chat stream.'
let statusCode = undefined
let requestID = undefined
if (error instanceof CodeWhispererStreamingServiceException) {
errorMessage = error.message
statusCode = getHttpStatusCode(error) ?? 0
requestID = getRequestId(error)
}
this.showChatExceptionMessage(
{ errorMessage, statusCode: statusCode?.toString(), sessionID: undefined },
tabID,
requestID
)
getLogger().error(`error: ${errorMessage} tabID: ${tabID} requestID: ${requestID}`)
followUps = []
relatedSuggestions = []
this.telemetryHelper.recordMessageResponseError(triggerPayload, tabID, statusCode ?? 0)
})
.finally(async () => {
if (session.sessionIdentifier) {
this.chatHistoryDb.addMessage(tabID, 'cwc', session.sessionIdentifier, {
body: message,
type: 'answer' as any,
codeReference: codeReference as any,
relatedContent: { title: 'Sources', content: relatedSuggestions as any },
})
}
if (
triggerPayload.relevantTextDocuments &&
triggerPayload.relevantTextDocuments.length > 0 &&
LspController.instance.isIndexingInProgress()
) {
this.dispatcher.sendChatMessage(
new ChatMessage(
{
message:
message +
` \n\nBy the way, I'm still indexing this project for full context from your workspace. I may have a better response in a few minutes when it's complete if you'd like to try again then.`,
messageType: 'answer-part',
followUps: undefined,
followUpsHeader: undefined,
relatedSuggestions: undefined,
triggerID,
messageID,
userIntent: triggerPayload.userIntent,
codeBlockLanguage: codeBlockLanguage,
contextList: undefined,
},
tabID
)
)
}
if (relatedSuggestions.length !== 0) {
this.dispatcher.sendChatMessage(
new ChatMessage(
{
message: undefined,
messageType: 'answer-part',
followUpsHeader: undefined,
followUps: undefined,
relatedSuggestions,
triggerID,
messageID,
userIntent: triggerPayload.userIntent,
codeBlockLanguage: undefined,
contextList: undefined,
},
tabID
)
)
}
this.dispatcher.sendChatMessage(
new ChatMessage(
{
message: undefined,
messageType: 'answer',
followUps: followUps,
followUpsHeader: undefined,
relatedSuggestions: undefined,
triggerID,
messageID,
userIntent: triggerPayload.userIntent,
codeBlockLanguage: undefined,
contextList: undefined,
},
tabID
)
)
getLogger().info(
`All events received. requestId=%s counts=%s`,
response.$metadata.requestId,
Object.fromEntries(eventCounts)
)
this.telemetryHelper.setResponseStreamTotalTime(tabID)
const responseCode = response?.$metadata.httpStatusCode ?? 0
this.telemetryHelper.recordAddMessage(triggerPayload, {
followUpCount: followUps.length,
suggestionCount: relatedSuggestions.length,
tabID: tabID,
messageLength: message.length,
messageID,
responseCode,
codeReferenceCount: codeReference.length,
totalNumberOfCodeBlocksInResponse: await this.countTotalNumberOfCodeBlocks(message),
})
})
}