optimized ChatBox component.

This commit is contained in:
Xiaohan-Tian
2025-08-16 17:35:40 -07:00
parent 429efaf629
commit 8b0ddae58d
6 changed files with 351 additions and 294 deletions
+42
View File
@@ -0,0 +1,42 @@
import { processUserMessage } from '../util/messageFilter/UserMessageFilter';
import type { ChatMessage } from '../types/projectTypes';
export const generateMessageId = (): string => {
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
};
export const createMessage = (role: 'user' | 'assistant', content: string): ChatMessage => {
return {
id: generateMessageId(),
role,
content,
};
};
export const createStreamingMessage = (content: string = '<span class="processing-wave">Processing...</span> 0 tokens received. click here to abort.'): ChatMessage => {
return {
id: generateMessageId(),
role: 'assistant',
content,
isStreaming: true,
tokenCount: 0,
};
};
export const createToolResultMessage = (toolName: string, success: boolean, result: string): ChatMessage => {
const friendlyDisplay = `${success ? '✅' : '❌'} __**${toolName}**__ \n\n └── ${result}`;
return createMessage('user', friendlyDisplay);
};
export const addWelcomeMessage = async (): Promise<ChatMessage | null> => {
try {
const result = await processUserMessage('/welcome');
if (result.pseudoAssistantResponse) {
return createMessage('assistant', result.pseudoAssistantResponse);
}
return null;
} catch {
// ignore errors, just don't show welcome if it fails
return null;
}
};
+102
View File
@@ -0,0 +1,102 @@
import { XMLToolExecutor } from '../agent/core/XMLToolExecutor';
import { extractXMLFromString } from '../util/xmlUtil';
import { createToolResultMessage } from './chatMessageUtils';
import type { ChatMessage } from '../types/projectTypes';
interface ToolExecutionResult {
success: boolean;
result: string;
}
interface ToolExecutionOptions {
onMessageAdd: (message: ChatMessage) => void;
onStatusUpdate: (status: string) => void;
}
export const extractActionableTools = (response: string): string[] => {
const xmlBlocks = extractXMLFromString(response);
// Consider only actionable tools (exclude think/thinking)
return xmlBlocks.filter((block) => {
const match = block.match(/<([a-zA-Z_][a-zA-Z0-9_-]*)/);
const name = match ? match[1].toLowerCase() : '';
return name !== 'think' && name !== 'thinking';
});
};
export const extractToolName = (xmlBlock: string): string => {
const toolNameMatch = xmlBlock.match(/<([a-zA-Z_][a-zA-Z0-9_-]*)/);
return toolNameMatch ? toolNameMatch[1] : 'unknown_tool';
};
export const executeSingleTool = async (
xmlBlock: string,
toolName: string,
options: ToolExecutionOptions
): Promise<ToolExecutionResult> => {
const { onMessageAdd } = options;
try {
const executor = XMLToolExecutor.instance();
const results = await executor.executeXMLTools(xmlBlock);
const result = results[0]; // Single block should give single result
if (result) {
// Add friendly display message
const toolMessage = createToolResultMessage(toolName, result.success, result.result);
onMessageAdd(toolMessage);
return {
success: result.success,
result: result.result
};
}
return {
success: false,
result: 'No result returned from tool execution'
};
} catch (error) {
// Handle individual tool error
const errorMessage = `Tool execution failed: ${error}`;
const toolMessage = createToolResultMessage(toolName, false, errorMessage);
onMessageAdd(toolMessage);
return {
success: false,
result: errorMessage
};
}
};
export const formatToolResultForLLM = (toolName: string, result: ToolExecutionResult): string => {
// Skip thinking tools
if (toolName === 'thinking' || toolName === 'think') {
return '';
}
return `tool: ${toolName}\nsuccess: ${result.success}\nresult:\n${result.result}\n------------\n`;
};
export const executeAllTools = async (
actionableBlocks: string[],
options: ToolExecutionOptions
): Promise<string> => {
const { onStatusUpdate } = options;
onStatusUpdate(`Executing ${actionableBlocks.length} tool(s)...`);
let accumulatedResults = '';
// Execute tools sequentially with real-time updates
for (let i = 0; i < actionableBlocks.length; i++) {
onStatusUpdate(`Executing tool ${i + 1} of ${actionableBlocks.length}...`);
const toolName = extractToolName(actionableBlocks[i]);
const result = await executeSingleTool(actionableBlocks[i], toolName, options);
// Accumulate formatted result for LLM
accumulatedResults += formatToolResultForLLM(toolName, result);
}
return accumulatedResults;
};