-
Notifications
You must be signed in to change notification settings - Fork 83
fix: ux polish for list directory tool messages. #1075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
|
||
| import { | ||
| ChatTriggerType, | ||
| GenerateAssistantResponseCommandInput, | ||
|
|
@@ -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! | ||
| const currentId = chatResultStream.getMessageIdToUpdateForTool(toolUse.name!) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will toolUse name always be defined? what happens if name isn't?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
|
@@ -742,7 +741,7 @@ | |
| return { | ||
| type: 'tool', | ||
| contextList, | ||
| messageId, | ||
| messageId: messageIdToUpdate, | ||
| body: '', | ||
| } | ||
| } | ||
|
|
||
| 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
|
||
|
|
||
| interface ResultStreamWriter { | ||
| write(chunk: ChatResult, final?: boolean): Promise<void> | ||
|
|
@@ -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> | ||
|
|
@@ -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[]) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried this approach, |
||
| this.#state.messageOperations.set(messageId, { type: type as OperationType, filePaths }) | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
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?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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
processReadOrListonce toolName and toolId is available!We has existing check here So don't want to duplicate by adding one more check.
There was a problem hiding this comment.
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