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
26 changes: 17 additions & 9 deletions packages/core/src/codewhispererChat/controllers/chat/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import { amazonQTabSuffix } from '../../../shared/constants'
import { FsRead, FsReadParams } from '../../tools/fsRead'
import { InvokeOutput, OutputKind } from '../../tools/toolShared'
import { FsWrite, FsWriteCommand } from '../../tools/fsWrite'
import { ExecuteBash, ExecuteBashParams } from '../../tools/executeBash'

export interface ChatControllerMessagePublishers {
readonly processPromptChatMessage: MessagePublisher<PromptMessage>
Expand Down Expand Up @@ -947,12 +948,12 @@ export class ChatController {
const toolResults: ToolResult[] = []
try {
switch (toolUse.name) {
// case 'execute_bash': {
// const executeBash = new ExecuteBash(toolUse.input as unknown as ExecuteBashParams)
// await executeBash.validate()
// result = await executeBash.invoke(process.stdout)
// break
// }
case 'executeBash': {
const executeBash = new ExecuteBash(toolUse.input as unknown as ExecuteBashParams)
await executeBash.validate()
result = await executeBash.invoke(process.stdout)
break
}
case 'fsRead': {
const fsRead = new FsRead(toolUse.input as unknown as FsReadParams)
await fsRead.validate()
Expand All @@ -966,12 +967,12 @@ export class ChatController {
break
}
default:
getLogger().warn('Received invalid tool: %s', toolUse.name)
break
throw new ToolkitError(`Unsupported tool: ${toolUse.name}`)
}
if (!result) {
throw new ToolkitError('Failed to execute tool and get results')
}

toolResults.push({
content: [
result.output.kind === OutputKind.Text
Expand All @@ -982,7 +983,14 @@ export class ChatController {
status: 'success',
})
} catch (e: any) {
toolResults.push({ content: [{ text: e.message }], toolUseId: toolUse.toolUseId, status: 'error' })
const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred during tool execution'
getLogger().debug(`Tool execution failed: ${toolUse.name} - ${errorMessage}`)

toolResults.push({
content: [{ text: errorMessage }],
toolUseId: toolUse.toolUseId,
status: 'error',
})
}

await this.generateResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import { extractAuthFollowUp } from '../../../../amazonq/util/authUtils'
import { helpMessage } from '../../../../amazonq/webview/ui/texts/constants'
import { ChatItemButton, ChatItemFormItem, MynahUIDataModel } from '@aws/mynah-ui'
import { ChatHistoryManager } from '../../../storages/chatHistory'
import { ExecuteBashParams } from '../../../tools/executeBash'
import { FsWriteCommand } from '../../../tools/fsWrite'

export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help'

Expand Down Expand Up @@ -604,57 +606,57 @@ export class Messenger {
// return toolUse.name === 'fs_write'
// }
private getToolUseMessage(toolUse: ToolUse) {
if (toolUse.name === 'fs_read') {
return `Reading the file at \`${(toolUse.input as any)?.path}\` using the \`fs_read\` tool.`
if (toolUse.name === 'fsRead') {
return `Reading the file at \`${(toolUse.input as any)?.path}\` using the \`fsRead\` tool.`
}
if (toolUse.name === 'executeBash') {
const input = toolUse.input as unknown as ExecuteBashParams
return `Executing the bash command
\`\`\`bash
${input.command}
\`\`\`
using the \`executeBash\` tool.`
}
if (toolUse.name === 'fsWrite') {
const input = toolUse.input as unknown as FsWriteCommand
switch (input.command) {
case 'create': {
return `Writing
\`\`\`
${input.fileText}
\`\`\`
into the file at \`${input.path}\` using the \`fsWrite\` tool.`
}
case 'strReplace': {
return `Replacing
\`\`\`
${input.oldStr}
\`\`\`
with
\`\`\`
${input.newStr}
\`\`\`
at \`${input.path}\` using the \`fsWrite\` tool.`
}
case 'insert': {
return `Inserting
\`\`\`
${input.newStr}
\`\`\`
at line
\`\`\`
${input.insertLine}
\`\`\`
at \`${input.path}\` using the \`fsWrite\` tool.`
}
case 'append': {
return `Appending
\`\`\`
${input.newStr}
\`\`\`
at \`${input.path}\` using the \`fsWrite\` tool.`
}
}
}
// if (toolUse.name === 'execute_bash') {
// const input = toolUse.input as unknown as ExecuteBashParams
// return `Executing the bash command
// \`\`\`bash
// ${input.command}
// \`\`\`
// using the \`execute_bash\` tool.`
// }
// if (toolUse.name === 'fs_write') {
// const input = toolUse.input as unknown as FsWriteParams
// switch (input.command) {
// case 'create': {
// return `Writing
// \`\`\`
// ${input.file_text}
// \`\`\`
// into the file at \`${input.path}\` using the \`fs_write\` tool.`
// }
// case 'str_replace': {
// return `Replacing
// \`\`\`
// ${input.old_str}
// \`\`\`
// with
// \`\`\`
// ${input.new_str}
// \`\`\`
// at \`${input.path}\` using the \`fs_write\` tool.`
// }
// case 'insert': {
// return `Inserting
// \`\`\`
// ${input.new_str}
// \`\`\`
// at line
// \`\`\`
// ${input.insert_line}
// \`\`\`
// at \`${input.path}\` using the \`fs_write\` tool.`
// }
// case 'append': {
// return `Appending
// \`\`\`
// ${input.new_str}
// \`\`\`
// at \`${input.path}\` using the \`fs_write\` tool.`
// }
// }
// }
}
}
23 changes: 14 additions & 9 deletions packages/core/src/codewhispererChat/tools/executeBash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,32 @@ export class ExecuteBash {
stderr: stderrTrunc + (stderrSuffix ? ' ... truncated' : ''),
}

return {
resolve({
output: {
kind: OutputKind.Json,
content: outputJson,
},
}
})
} catch (err: any) {
this.logger.error(`Failed to execute bash command '${this.command}': ${err.message}`)
throw new Error(`Failed to execute command: ${err.message}`)
reject(new Error(`Failed to execute command: ${err.message}`))
}
})
}

private static handleChunk(chunk: string, buffer: string[], updates: Writable) {
const lines = chunk.split(/\r?\n/)
for (const line of lines) {
updates.write(`${line}\n`)
buffer.push(line)
if (buffer.length > lineCount) {
buffer.shift()
try {
const lines = chunk.split(/\r?\n/)
for (const line of lines) {
updates.write(`${line}\n`)
buffer.push(line)
if (buffer.length > lineCount) {
buffer.shift()
}
}
} catch (error) {
// Log the error but don't let it crash the process
throw new Error('Error handling output chunk')
Copy link

Choose a reason for hiding this comment

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

For debugging you might want to log the chunk

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will add in followup

}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/codewhispererChat/tools/toolShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export enum OutputKind {
export interface InvokeOutput {
output: {
kind: OutputKind
content: string
content: string | any
}
}

Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/codewhispererChat/tools/tool_index.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,23 @@
},
"required": ["command", "path"]
}
},
"executeBash": {
"name": "executeBash",
"description": "Execute the specified bash command.",
"inputSchema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "Bash command to execute"
},
"cwd": {
"type": "string",
"description": "Parameter to set the current working directory for the bash command."
}
},
"required": ["command", "cwd"]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ describe('FsRead Tool', () => {
const result = await fsRead.invoke()

const lines = result.output.content.split('\n')
const hasFileA = lines.some((line) => line.includes('- ') && line.includes('fileA.txt'))
const hasSubfolder = lines.some((line) => line.includes('d ') && line.includes('subfolder'))
const hasFileA = lines.some((line: string | string[]) => line.includes('- ') && line.includes('fileA.txt'))
Copy link

Choose a reason for hiding this comment

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

Wait, why did you need to change the type here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

once i updated output type in tool shared file to accomodate to json type output...this threw failures.

const hasSubfolder = lines.some((line: string | string[]) => line.includes('d ') && line.includes('subfolder'))

assert.ok(hasFileA, 'Should list fileA.txt in the directory output')
assert.ok(hasSubfolder, 'Should list the subfolder in the directory output')
Expand Down
Loading