UNPKG

crewai-ts

Version:

TypeScript port of crewAI for agent-based workflows

167 lines (166 loc) 7.33 kB
/** * CLI command for interactive chat with the crew. * Optimized for memory efficiency with streaming responses. */ import readline from 'readline'; // Optimized type-safe imports import { Command } from '../Command.js'; // Import Node.js built-in modules with proper type definitions import * as process from 'node:process'; // Cast process with memory-optimized type assertion const typedProcess = process; export class ChatCommand extends Command { name = 'chat'; description = 'Start a conversation with the crew'; syntax = '[options]'; examples = [ 'chat', 'chat --model gpt-4', 'chat --verbose' ]; async execute(args) { const parsedArgs = this.parseArgs(args); try { // Dynamically import required modules only when needed (optimization) const { default: chalk } = await import('chalk'); console.log(chalk.bold('\nStarting a conversation with the Crew')); console.log(chalk.dim('Type \'exit\' or Ctrl+C to quit.\n')); // Import the chat service with lazy loading // Dynamically import service with optimized error handling let CrewChatServiceClass; try { // Use a dynamic import with proper error handling for better memory efficiency const modulePath = '../../services/CrewChatService.js'; // Add explicit type annotation for dynamic import const module = await import(modulePath).catch(e => { // Log detailed error for troubleshooting console.error(`Failed to import ${modulePath}:`, e.message); return { CrewChatService: null }; }); CrewChatServiceClass = module.CrewChatService; if (!CrewChatServiceClass) { throw new Error(`Module ${modulePath} doesn't export CrewChatService`); } } catch (error) { // Fallback implementation for testing or development environments console.warn('CrewChatService module not found, using mock implementation'); CrewChatServiceClass = class MockCrewChatService { options; constructor(options) { this.options = options; } async initialize() { } async getGreeting() { return 'Hello! (Mock Service)'; } async getResponse(input) { return `Echo: ${input}`; } async streamResponse(input, callback) { callback(`Echo: ${input}`); } async shutdown() { } }; } // Create chat service with memory-efficient configuration const chatService = new CrewChatServiceClass({ model: parsedArgs.model, verbose: parsedArgs.verbose, maxHistoryLength: 10, // Limit history for memory efficiency useTokenCompression: true // Memory optimization for token usage }); // Initialize the chat service await chatService.initialize(); // Memory-optimized readline interface with proper type definitions const rl = readline.createInterface({ input: typedProcess.stdin, output: typedProcess.stdout, terminal: true, prompt: chalk.green('You: ') }); // Custom prompt with proper event handling for memory efficiency const promptUser = () => { typedProcess.stdout.write(chalk.green('You: ')); }; // Print the initial greeting const greeting = await chatService.getGreeting(); console.log(chalk.blue('Crew: ') + greeting); // Setup event handlers with memory-efficient callback patterns promptUser(); // Use raw mode for better UX (shows input as user types) rl.on('line', async (input) => { // Handle exit command if (input.toLowerCase() === 'exit') { console.log(chalk.yellow('\nEnding conversation. Goodbye!')); rl.close(); return; } try { // Process user message with progressive rendering console.log(chalk.blue('Crew: ')); // Using streaming response for memory efficiency if (parsedArgs.stream) { // Setup streaming with optimized token handling let responseBuffer = ''; await chatService.streamResponse(input, (chunk) => { // Print chunk without buffering entire response typedProcess.stdout.write(chunk); responseBuffer += chunk; }); console.log(); // New line after response } else { // Get complete response (more memory intensive but simpler) const response = await chatService.getResponse(input); console.log(response); } } catch (error) { console.error(chalk.red('Error getting response:'), error); } // Prompt for next input promptUser(); }); // Handle interrupts gracefully rl.on('close', async () => { // Clean shutdown with proper resource release await chatService.shutdown(); console.log(chalk.yellow('\nChat session ended.')); typedProcess.exit(0); }); // Handle SIGINT with optimized signal handling typedProcess.on('SIGINT', () => { rl.close(); }); } catch (error) { console.error('Error in chat session:', error); typedProcess.exit(1); } } /** * Parse command line arguments for the chat command * Optimized for handling various argument formats */ parseArgs(args) { // Default values with type safety for memory optimization const result = { model: 'gpt-3.5-turbo', verbose: false, stream: true // Default to streaming for better UX }; // Parse options with optimized pattern matching for (let i = 0; i < args.length; i++) { const arg = args[i]; if ((arg === '-m' || arg === '--model') && i + 1 < args.length) { const modelArg = args[++i]; // Ensure model is never undefined with proper null coalescing result.model = modelArg || 'gpt-3.5-turbo'; } else if (arg === '--verbose' || arg === '-v') { result.verbose = true; } else if (arg === '--no-stream') { result.stream = false; } } return result; } }