Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Will be deleted or merged.
*/

import * as path from 'path'

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "path"

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "path"
import {
ChatTriggerType,
GenerateAssistantResponseCommandInput,
Expand Down Expand Up @@ -691,34 +691,33 @@
}

#processReadOrList(toolUse: ToolUse, chatResultStream: AgenticChatResultStream): ChatMessage | undefined {
if (toolUse.name !== 'fsRead') {
//TODO: Implement list directory UX in next PR.
return {}
}
let messageId = toolUse.toolUseId || ''
if (chatResultStream.getMessageIdToUpdate()) {
messageId = chatResultStream.getMessageIdToUpdate()!
} else if (messageId) {
chatResultStream.setMessageIdToUpdate(messageId)
let messageIdToUpdate = toolUse.toolUseId!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this always going to be defined? what happens if toolUseId isn't?

Copy link
Contributor Author

@laileni-aws laileni-aws Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We come to this part of code processReadOrList once toolName and toolId is available!
We has existing check here So don't want to duplicate by adding one more check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah so really the ToolUse type should really be something like Required<ToolUse> as an input to this function since the optional fields are actually defined? Not a blocker, but worth a consideration

const currentId = chatResultStream.getMessageIdToUpdateForTool(toolUse.name!)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will toolUse name always be defined? what happens if name isn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied here


if (currentId) {
messageIdToUpdate = currentId
} else {
chatResultStream.setMessageIdToUpdateForTool(toolUse.name!, messageIdToUpdate)
}

const currentPath = (toolUse.input as unknown as FsReadParams | ListDirectoryParams)?.path
if (!currentPath) return
const existingPaths = chatResultStream.getMessageOperation(messageId)?.filePaths || []
const existingPaths = chatResultStream.getMessageOperation(messageIdToUpdate)?.filePaths || []
// Check if path already exists in the list
const isPathAlreadyProcessed = existingPaths.some(path => path.relativeFilePath === currentPath)
if (!isPathAlreadyProcessed) {
const currentFileDetail = {
relativeFilePath: currentPath,
lineRanges: [{ first: -1, second: -1 }],
}
const operationType = toolUse.name === 'fsRead' ? 'read' : 'listDir'
if (operationType === 'read') {
chatResultStream.addMessageOperation(messageId, operationType, [...existingPaths, currentFileDetail])
}
chatResultStream.addMessageOperation(messageIdToUpdate, toolUse.name!, [
...existingPaths,
currentFileDetail,
])
}
let title: string
const itemCount = chatResultStream.getMessageOperation(messageId)?.filePaths.length
const filePathsPushed = chatResultStream.getMessageOperation(messageId)?.filePaths ?? []
const itemCount = chatResultStream.getMessageOperation(messageIdToUpdate)?.filePaths.length
const filePathsPushed = chatResultStream.getMessageOperation(messageIdToUpdate)?.filePaths ?? []
if (!itemCount) {
title = 'Gathering context'
} else {
Expand All @@ -742,7 +741,7 @@
return {
type: 'tool',
contextList,
messageId,
messageId: messageIdToUpdate,
body: '',
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChatResult, FileDetails, ChatMessage } from '@aws/language-server-runtimes/protocol'
import { randomUUID } from 'crypto'

Check warning on line 2 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatResultStream.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "crypto"

Check warning on line 2 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatResultStream.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "crypto"

interface ResultStreamWriter {
write(chunk: ChatResult, final?: boolean): Promise<void>
Expand Down Expand Up @@ -29,7 +29,7 @@
isLocked: false,
uuid: randomUUID(),
messageId: undefined as string | undefined,
messageIdToUpdate: undefined as string | undefined,
messageIdToUpdateForTool: new Map<OperationType, string>(),
messageOperations: new Map<string, FileOperation>(),
}
readonly #sendProgress: (newChatResult: ChatResult | string) => Promise<void>
Expand All @@ -41,22 +41,23 @@
getResult(only?: string): ChatResult {
return this.#joinResults(this.#state.chatResultBlocks, only)
}
getMessageIdToUpdate(): string | undefined {
return this.#state.messageIdToUpdate

setMessageIdToUpdateForTool(toolName: string, messageId: string) {
this.#state.messageIdToUpdateForTool.set(toolName as OperationType, messageId)
}

setMessageIdToUpdate(messageId: string) {
this.#state.messageIdToUpdate = messageId
getMessageIdToUpdateForTool(toolName: string): string | undefined {
return this.#state.messageIdToUpdateForTool.get(toolName as OperationType)
}

/**
* Adds a file operation for a specific message
* @param messageId The ID of the message
* @param type The type of operation ('read' or 'listDir' or 'write')
* @param type The type of operation ('fsRead' or 'listDirectory' or 'fsWrite')
* @param filePaths Array of FileDetailsWithPath involved in the operation
*/
addMessageOperation(messageId: string, type: OperationType, filePaths: FileDetailsWithPath[]) {
this.#state.messageOperations.set(messageId, { type, filePaths })
addMessageOperation(messageId: string, type: string, filePaths: FileDetailsWithPath[]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make a type here instead of converting to string? then you wouldn't need to cast to an operation type on the next line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this approach,
OperationType can not be imported to agenticChatController.ts so explicitly mentioning here!

this.#state.messageOperations.set(messageId, { type: type as OperationType, filePaths })
}

/**
Expand Down
Loading