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
36 changes: 18 additions & 18 deletions ui/desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

172 changes: 146 additions & 26 deletions ui/desktop/src/components/ToolCallWithResponse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,49 +163,169 @@ function ToolCallView({
const isRenderingProgress =
loadingStatus === 'loading' && (progressEntries.length > 0 || (logs || []).length > 0);

const isShouldExpand = isExpandToolDetails || toolResults.some((v) => v.isExpandToolResults);
// Only expand if there are actual results that need to be shown, not just for tool details
const isShouldExpand = toolResults.some((v) => v.isExpandToolResults);

// Function to create a compact representation of arguments
const getCompactArguments = () => {
// Function to create a descriptive representation of what the tool is doing
const getToolDescription = () => {
const args = toolCall.arguments as Record<string, ToolCallArgumentValue>;
const entries = Object.entries(args);
const toolName = toolCall.name.substring(toolCall.name.lastIndexOf('__') + 2);

// Helper function to get string value safely
const getStringValue = (value: ToolCallArgumentValue): string => {
return typeof value === 'string' ? value : JSON.stringify(value);
};

// Helper function to truncate long values
const truncate = (str: string, maxLength: number = 50): string => {
return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
};

// Generate descriptive text based on tool type
switch (toolName) {
case 'text_editor':
if (args.command === 'write' && args.path) {
return `writing ${truncate(getStringValue(args.path))}`;
}
if (args.command === 'view' && args.path) {
return `reading ${truncate(getStringValue(args.path))}`;
}
if (args.command === 'str_replace' && args.path) {
return `editing ${truncate(getStringValue(args.path))}`;
}
if (args.command && args.path) {
return `${getStringValue(args.command)} ${truncate(getStringValue(args.path))}`;
}
break;

case 'shell':
if (args.command) {
return `running ${truncate(getStringValue(args.command))}`;
}
break;

case 'search':
if (args.name) {
return `searching for "${truncate(getStringValue(args.name))}"`;
}
if (args.mimeType) {
return `searching for ${getStringValue(args.mimeType)} files`;
}
break;

case 'read': {
if (args.uri) {
const uri = getStringValue(args.uri);
const fileId = uri.replace('gdrive:///', '');
return `reading file ${truncate(fileId)}`;
}
if (args.url) {
return `reading ${truncate(getStringValue(args.url))}`;
}
break;
}

if (entries.length === 0) return null;
case 'create_file':
if (args.name) {
return `creating ${truncate(getStringValue(args.name))}`;
}
break;

case 'update_file':
if (args.fileId) {
return `updating file ${truncate(getStringValue(args.fileId))}`;
}
break;

case 'sheets_tool': {
if (args.operation && args.spreadsheetId) {
const operation = getStringValue(args.operation);
const sheetId = truncate(getStringValue(args.spreadsheetId));
return `${operation} in sheet ${sheetId}`;
}
break;
}

// For a single parameter, show key and truncated value
if (entries.length === 1) {
const [key, value] = entries[0];
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
const truncatedValue =
stringValue.length > 30 ? stringValue.substring(0, 30) + '...' : stringValue;
case 'docs_tool': {
if (args.operation && args.documentId) {
const operation = getStringValue(args.operation);
const docId = truncate(getStringValue(args.documentId));
return `${operation} in document ${docId}`;
}
break;
}

return (
<span className="ml-2 text-textSubtle truncate text-xs opacity-70">
{key}: {truncatedValue}
</span>
);
case 'web_scrape':
if (args.url) {
return `scraping ${truncate(getStringValue(args.url))}`;
}
break;

case 'remember_memory':
if (args.category && args.data) {
return `storing ${getStringValue(args.category)}: ${truncate(getStringValue(args.data))}`;
}
break;

case 'retrieve_memories':
if (args.category) {
return `retrieving ${getStringValue(args.category)} memories`;
}
break;

case 'screen_capture':
if (args.window_title) {
return `capturing window "${truncate(getStringValue(args.window_title))}"`;
}
return 'capturing screen';

case 'automation_script':
if (args.language) {
return `running ${getStringValue(args.language)} script`;
}
break;

case 'computer_control':
return 'poking around...';

default: {
// Fallback to showing key parameters for unknown tools
const entries = Object.entries(args);
if (entries.length === 0) return null;

// For a single parameter, show key and truncated value
if (entries.length === 1) {
const [key, value] = entries[0];
const stringValue = getStringValue(value);
const truncatedValue = truncate(stringValue, 30);
return `${key}: ${truncatedValue}`;
}

// For multiple parameters, just show the keys
return entries.map(([key]) => key).join(', ');
}
}

// For multiple parameters, just show the keys
return (
<span className="ml-2 text-textSubtle truncate text-xs opacity-70">
{entries.map(([key]) => key).join(', ')}
</span>
);
return null;
};

return (
<ToolCallExpandable
isStartExpanded={isShouldExpand || isRenderingProgress}
isStartExpanded={isRenderingProgress}
isForceExpand={isShouldExpand}
label={
<>
<Dot size={2} loadingStatus={loadingStatus} />
<span className="ml-[10px]">
{snakeToTitleCase(toolCall.name.substring(toolCall.name.lastIndexOf('__') + 2))}
{(() => {
const description = getToolDescription();
if (description) {
return description;
}
// Fallback to the original tool name formatting
return snakeToTitleCase(toolCall.name.substring(toolCall.name.lastIndexOf('__') + 2));
})()}
</span>
{/* Display compact arguments inline */}
{isToolDetails && getCompactArguments()}
</>
}
>
Expand Down
Loading