diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 7ee42b36..62720353 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -423,7 +423,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { toolParams = params } catch (error) { const errorMessage = getErrorMessage(error) - this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) + this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', params: undefined, value: errorMessage }, }) res_() return } @@ -441,7 +441,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { // TODO!!! test rejection // if (Math.random() > 0) throw new Error('TESTING') const errorMessage = 'Tool call was rejected by the user.' - this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) + this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', params: toolParams, value: errorMessage }, }) res_() return } @@ -453,7 +453,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { toolResult = await this._toolsService.callTool[toolName](toolParams as any) // typescript is so bad it doesn't even couple the type of ToolResult with the type of the function being called here } catch (error) { const errorMessage = getErrorMessage(error) - this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) + this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', params: toolParams, value: errorMessage }, }) res_() return } @@ -464,7 +464,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) } catch (error) { const errorMessage = `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` - this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', value: errorMessage }, }) + this._addMessageToThread(threadId, { role: 'tool', name: toolName, paramsStr: tool.paramsStr, id: tool.id, content: errorMessage, result: { type: 'error', params: toolParams, value: errorMessage }, }) res_() return } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index c5f8ff42..1b46bef1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -10,7 +10,6 @@ import { nameToVscodeLanguage } from '../../../../common/helpers/detectLanguage. import { ApplyBlockHoverButtons } from './ApplyBlockHoverButtons.js' import { useAccessor, useChatThreadsState } from '../util/services.js' import { Range } from '../../../../../../services/search/common/searchExtTypes.js' -import { CodespanLocationLink } from '../../../chatThreadService.js' import { IRange } from '../../../../../../../base/common/range.js' import { ScrollType } from '../../../../../../../editor/common/editorCommon.js' diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 8f26dd49..19af982f 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -29,7 +29,7 @@ import { filenameToVscodeLanguage } from '../../../../common/helpers/detectLangu import { getModelSelectionState, getModelCapabilities } from '../../../../common/modelCapabilities.js'; import { AlertTriangle, ChevronRight, Dot, Pencil, X } from 'lucide-react'; import { ChatMessage, StagingSelectionItem, ToolMessage, ToolRequestApproval } from '../../../../common/chatThreadServiceTypes.js'; -import { ToolName } from '../../../../common/toolsServiceTypes.js'; +import { ToolCallParams, ToolName } from '../../../../common/toolsServiceTypes.js'; @@ -274,7 +274,7 @@ interface VoidChatAreaProps { onAbort: () => void; isStreaming: boolean; isDisabled?: boolean; - divRef?: React.RefObject; + divRef?: React.RefObject; // UI customization className?: string; @@ -659,11 +659,10 @@ export const SelectedFiles = ( interface DropdownComponentProps { title: string; desc1: string; - desc2?: string; + desc2?: React.ReactNode; numResults?: number; children?: React.ReactNode; onClick?: () => void; - icon?: React.ReactNode; } const DropdownComponent = ({ @@ -673,7 +672,6 @@ const DropdownComponent = ({ numResults, children, onClick, - icon, }: DropdownComponentProps) => { const [isExpanded, setIsExpanded] = useState(false); @@ -695,18 +693,21 @@ const DropdownComponent = ({ className={`text-void-fg-3 mr-0.5 h-5 w-5 flex-shrink-0 transition-transform duration-100 ease-[cubic-bezier(0.4,0,0.2,1)] ${isExpanded ? 'rotate-90' : ''}`} /> )} -
- {icon} - {title} - {desc1} - {desc2 && - {desc2} - } - {numResults !== undefined && ( - - {`(`}{numResults}{` result`}{numResults !== 1 ? 's' : ''}{`)`} - - )} +
+
+ {title} + {desc1} +
+
+ {desc2 && + {desc2} + } + {numResults !== undefined && ( + + {`(`}{numResults}{` result`}{numResults !== 1 ? 's' : ''}{`)`} + + )} +
} -
- - } @@ -968,7 +966,6 @@ const AssistantMessageComponent = ({ chatMessage, isLoading, messageIdx }: ChatB {hasReasoning && } > { +const ToolError = ({ title, desc1, errorMessage }: { title: string, desc1: string, errorMessage: string }) => { return ( -
- -
- {title} -
{'Error: ' + errorMessage}
-
-
+ // px-2 py-1 + //
+ // + //
+ // {title + ' error'} + //
{errorMessage}
+ //
+ //
+ + + Error + + } + > +
{errorMessage}
+
+ ) } const toolNameToTitle: Record = { 'read_file': 'Read file', - 'list_dir': 'Inspect folder', - 'pathname_search': 'Search (path only)', - 'search': 'Search (file contents)', + 'list_dir': 'Inspected folder', + 'pathname_search': 'Searched by file name', + 'search': 'Searched files', 'create_uri': 'Create file', 'delete_uri': 'Delete file', 'edit': 'Edit file', 'terminal_command': 'Ran terminal command' } +const toolNameToDesc = (toolName: ToolName, _toolParams: ToolCallParams[ToolName] | undefined): string => { + + if (_toolParams === undefined) { + return ''; + } + + if (toolName === 'read_file') { + const toolParams = _toolParams as ToolCallParams['read_file'] + return toolParams ? getBasename(toolParams.uri.fsPath) : ''; + } else if (toolName === 'list_dir') { + const toolParams = _toolParams as ToolCallParams['list_dir'] + return toolParams ? `${getBasename(toolParams.rootURI.fsPath)}/` : ''; + } else if (toolName === 'pathname_search') { + const toolParams = _toolParams as ToolCallParams['pathname_search'] + return toolParams ? `"${toolParams.queryStr}"` : ''; + } else if (toolName === 'search') { + const toolParams = _toolParams as ToolCallParams['search'] + return toolParams ? `"${toolParams.queryStr}"` : ''; + } else if (toolName === 'create_uri') { + const toolParams = _toolParams as ToolCallParams['create_uri'] + return toolParams ? getBasename(toolParams.uri.fsPath) : ''; + } else if (toolName === 'delete_uri') { + const toolParams = _toolParams as ToolCallParams['delete_uri'] + return toolParams ? getBasename(toolParams.uri.fsPath) + ' (deleted)' : ''; + } else if (toolName === 'edit') { + const toolParams = _toolParams as ToolCallParams['edit'] + return toolParams ? getBasename(toolParams.uri.fsPath) : ''; + } else if (toolName === 'terminal_command') { + const toolParams = _toolParams as ToolCallParams['terminal_command'] + return toolParams ? `"${toolParams.command}"` : ''; + } else { + return '' + } +} @@ -1028,24 +1073,31 @@ const ToolRequestAcceptRejectButtons = ({ toolRequest }: { toolRequest: ToolRequ const toolNameToComponent: { [T in ToolName]: { requestWrapper: (props: { toolRequest: ToolRequestApproval }) => React.ReactNode, - resultWrapper: (props: { toolMessage: ToolMessage & { result: { type: 'success' } } }) => React.ReactNode, + resultWrapper: (props: { toolMessage: ToolMessage }) => React.ReactNode, } } = { 'read_file': { requestWrapper: ({ toolRequest }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } - onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return { commandService.executeCommand('vscode.open', toolRequest.params.uri, { preview: true }) }} /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } + const { value, params } = toolMessage.result - return }> + + return
{ commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} @@ -1053,7 +1105,7 @@ const toolNameToComponent: { [T in ToolName]: {
{params.uri.fsPath}
- {value.hasNextPage && (
AI can scroll for more content...
)} + {toolMessage.result.value.hasNextPage && (
AI can scroll for more content...
)}
}, @@ -1061,23 +1113,26 @@ const toolNameToComponent: { [T in ToolName]: { 'list_dir': { requestWrapper: ({ toolRequest }) => { const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } /> + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const explorerService = accessor.get('IExplorerService') const title = toolNameToTitle[toolMessage.name] - // message.result.hasNextPage = true - // message.result.itemsRemaining = 400 + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } const { value, params } = toolMessage.result + return } > {value.children?.map((child, i) => (
)} - } }, 'pathname_search': { requestWrapper: ({ toolRequest }) => { const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } /> + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return }, resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } const { value, params } = toolMessage.result + return ( } > - { - value.uris.map((uri, i) => ( -
{ - commandService.executeCommand('vscode.open', uri, { preview: true }) - }} - > -
- {uri.fsPath.split('/').pop()} -
- )) - } - { - value.hasNextPage && ( -
- More results available... -
- ) - } -
+ {value.uris.map((uri, i) => ( +
{ + commandService.executeCommand('vscode.open', uri, { preview: true }) + }} + > +
+ {uri.fsPath.split('/').pop()} +
+ ))} + {value.hasNextPage && ( +
+ More results available... +
+ )} +
) } }, 'search': { requestWrapper: ({ toolRequest }) => { const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } /> + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } const { value, params } = toolMessage.result + return ( } > {value.uris.map((uri, i) => (
{ const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } /> + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } + const { params } = toolMessage.result + return ( { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} - icon={} /> ) } @@ -1206,20 +1270,27 @@ const toolNameToComponent: { [T in ToolName]: { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return { commandService.executeCommand('vscode.open', toolRequest.params.uri, { preview: true }) }} /> }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } + const { params } = toolMessage.result + return ( { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} /> ) @@ -1230,25 +1301,30 @@ const toolNameToComponent: { [T in ToolName]: { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } - onClick={() => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return { commandService.executeCommand('vscode.open', toolRequest.params.uri, { preview: true }) }} > - - + + }, resultWrapper: ({ toolMessage }) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } const { params } = toolMessage.result + return ( { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }} - icon={} /> ) } @@ -1258,8 +1334,8 @@ const toolNameToComponent: { [T in ToolName]: { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolRequest.name] - const { params } = toolRequest - return } + const desc1 = toolNameToDesc(toolRequest.name, toolRequest.params) + return }, @@ -1267,13 +1343,18 @@ const toolNameToComponent: { [T in ToolName]: { const accessor = useAccessor() const commandService = accessor.get('ICommandService') const title = toolNameToTitle[toolMessage.name] + const desc1 = toolNameToDesc(toolMessage.name, toolMessage.result.params) + + if (toolMessage.result.type === 'error') { + return + } const { params } = toolMessage.result + return ( } + desc1={desc1} >
} else if (role === 'tool') { + const title = toolNameToTitle[chatMessage.name] - if (chatMessage.result.type === 'error') return + // if (chatMessage.result.type === 'error') return + const ToolResultComponent = toolNameToComponent[chatMessage.name].resultWrapper as React.FC<{ toolMessage: any }> // ts isnt smart enough... return } diff --git a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts b/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts index 19398801..0819f242 100644 --- a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts +++ b/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts @@ -9,7 +9,7 @@ export type ToolMessage = { paramsStr: string; // internal use id: string; // apis require this tool use id content: string; // give this result to LLM - result: { type: 'success'; params: ToolCallParams[T]; value: ToolResultType[T], } | { type: 'error'; value: string }; // give this result to user + result: { type: 'success'; params: ToolCallParams[T]; value: ToolResultType[T], } | { type: 'error'; params: ToolCallParams[T] | undefined; value: string }; // give this result to user } export type ToolRequestApproval = { role: 'tool_request';