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
+119
View File
@@ -0,0 +1,119 @@
import { useState, useCallback } from 'react';
import { AgentCore } from '../agent/core/AgentCore';
import { createStreamingMessage } from '../utils/chatMessageUtils';
import type { ChatMessage } from '../types/projectTypes';
interface StreamProcessorOptions {
onMessageUpdate: (messageId: string, updater: (msg: ChatMessage) => ChatMessage) => void;
onMessageAdd: (message: ChatMessage) => void;
onProcessingChange: (isProcessing: boolean) => void;
}
interface StreamProcessorResult {
processStream: (input: string, logPrefix?: string) => Promise<string>;
abortController: AbortController | null;
isProcessing: boolean;
}
export const useStreamProcessor = (options: StreamProcessorOptions): StreamProcessorResult => {
const { onMessageUpdate, onMessageAdd, onProcessingChange } = options;
const [abortController, setAbortController] = useState<AbortController | null>(null);
const [isProcessing, setIsProcessing] = useState(false);
const processStream = useCallback(async (input: string, logPrefix: string = 'USER'): Promise<string> => {
setIsProcessing(true);
onProcessingChange(true);
// Create abort controller for this request
const controller = new AbortController();
setAbortController(controller);
// Add streaming assistant message
const streamingMessage = createStreamingMessage();
onMessageAdd(streamingMessage);
try {
const agentCore = AgentCore.instance();
let assistantResponse = '';
let tokenCount = 0;
let streamCompleted = false;
// Log the input being sent to LLM
console.log(`------------ ${logPrefix} ------------`);
console.log(input);
console.log('------------------------------');
for await (const chunk of agentCore.processUserInput(input)) {
// Check if request was aborted
if (controller.signal.aborted) {
return '';
}
if (chunk.type === 'text') {
assistantResponse += chunk.content;
tokenCount++;
// Update streaming message with token count and abort link
onMessageUpdate(streamingMessage.id, (msg) => ({
...msg,
content: `<span class="processing-wave">Processing...</span> ${tokenCount} tokens received. click here to abort.`,
tokenCount
}));
} else if (chunk.type === 'done') {
streamCompleted = true;
// Replace with final response
onMessageUpdate(streamingMessage.id, (msg) => ({
...msg,
content: assistantResponse,
isStreaming: false,
tokenCount: undefined
}));
// Log the complete assistant response
console.log('------------ ASSISTANT ------------');
console.log(assistantResponse);
console.log('-----------------------------------');
break;
}
}
// If stream didn't complete normally, finalize the message
if (!streamCompleted && !controller.signal.aborted) {
onMessageUpdate(streamingMessage.id, (msg) => ({
...msg,
content: assistantResponse || 'Stream was interrupted unexpectedly',
isStreaming: false,
tokenCount: undefined
}));
}
return assistantResponse;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
// Request was aborted, don't show error
return '';
}
console.error('Error processing stream:', error);
// Update with error message
onMessageUpdate(streamingMessage.id, (msg) => ({
...msg,
content: 'Error: Failed to process message',
isStreaming: false,
tokenCount: undefined
}));
return '';
} finally {
setAbortController(null);
setIsProcessing(false);
onProcessingChange(false);
}
}, [onMessageUpdate, onMessageAdd, onProcessingChange]);
return {
processStream,
abortController,
isProcessing
};
};