UNPKG

contaigents

Version:

Modular AI Content Ecosystem with Audio Generation

353 lines (352 loc) • 13.5 kB
import { Command } from "commander"; import { Server } from "./server.js"; import open from 'open'; import { AudioGenerator } from "./services/audioGenerator.js"; import { ChatService } from "./services/chatService.js"; import { readFile } from 'fs/promises'; import path from 'path'; import dotenv from 'dotenv'; import readline from 'readline'; import { testToolArchitecture } from './tests/toolTest.js'; import { demonstrateChatWithTools } from './tests/chatWithToolsDemo.js'; import { testFileWriteTool } from './tests/fileWriteToolTest.js'; import { testCombinedTools } from './tests/combinedToolsTest.js'; import { testToolCallLimits } from './tests/toolCallLimitTest.js'; import { debugChatServiceWrite } from './tests/debugChatServiceWrite.js'; import { manualWriteTest } from './tests/manualWriteTest.js'; import { createTestCommandToolCommand } from './commands/test-command-tool.js'; import { createConfigureCommand } from './commands/configure.js'; // Load environment variables dotenv.config(); const program = new Command(); program .version("0.1.0-alpha.7") .description("Contaigents - AI Content Ecosystem"); // Default action when no command is provided program.action(async () => { const server = new Server(); server.start(); const url = "http://localhost:2668"; console.log(`Opening editor at ${url}`); await open(url); }); // Keep the develop command as an alias program .command("develop") .description("Start development server with file editing capabilities") .action(async () => { const server = new Server(); server.start(); const url = "http://localhost:2668/files"; console.log(`Opening editor at ${url}`); await open(url); }); // Audio generation command program .command("audio") .description("Generate audio from text using AI providers") .option("-t, --text <text>", "Text to convert to audio (direct input)") .option("-f, --file <file>", "Path to file containing text to convert") .option("-o, --output <output>", "Output file path (default: audio_output)") .option("-p, --provider <provider>", "AI provider to use", "gemini") .option("-v, --voice <voice>", "Voice to use for generation", "Zephyr") .option("-m, --model <model>", "Model to use for generation") .option("--api-key <apiKey>", "API key for the provider (or set GEMINI_API_KEY env var)") .option("--output-dir <dir>", "Output directory (default: current directory)") .action(async (options) => { try { // Validate input if (!options.text && !options.file) { console.error("āŒ Error: Either --text or --file must be provided"); process.exit(1); } if (options.text && options.file) { console.error("āŒ Error: Cannot use both --text and --file options"); process.exit(1); } // Get API key from options or environment const apiKey = options.apiKey || process.env.GEMINI_API_KEY; if (!apiKey) { console.error("āŒ Error: API key is required. Use --api-key option or set GEMINI_API_KEY environment variable"); process.exit(1); } // Get text content let textContent; if (options.file) { try { const filePath = path.resolve(options.file); textContent = await readFile(filePath, 'utf-8'); console.log(`šŸ“– Read text from file: ${filePath}`); } catch (error) { console.error(`āŒ Error reading file: ${error.message}`); process.exit(1); } } else { textContent = options.text; } if (!textContent.trim()) { console.error("āŒ Error: Text content is empty"); process.exit(1); } console.log(`šŸ“ Text length: ${textContent.length} characters`); // Initialize audio generator const audioGenerator = new AudioGenerator({ provider: options.provider, apiKey: apiKey, voice: options.voice, model: options.model }); console.log(`šŸŽ™ļø Available voices: ${audioGenerator.getAvailableVoices().join(', ')}`); // Generate audio console.log("šŸ”„ Generating audio..."); const audioBuffer = await audioGenerator.generateAudio(textContent); // Determine output path const outputDir = options.outputDir || process.cwd(); const outputName = options.output || 'audio_output'; const outputPath = path.resolve(outputDir, outputName); // Save audio file const savedPath = await audioGenerator.saveAudioToFile(outputPath, audioBuffer); console.log(`āœ… Audio generation completed!`); console.log(`šŸ“ File saved to: ${savedPath}`); console.log(`šŸ”— File URL: file://${savedPath}`); } catch (error) { console.error(`āŒ Error generating audio: ${error.message}`); process.exit(1); } }); // Chat command program .command("chat") .description("Start an interactive chat session with AI") .option("-p, --provider <provider>", "AI provider to use (openai, anthropic, google, etc.)") .option("-m, --model <model>", "Model to use for generation") .option("-t, --temperature <temperature>", "Temperature for response generation (0.0-1.0)", parseFloat) .option("--max-tokens <maxTokens>", "Maximum tokens for response", parseInt) .option("--system-prompt <systemPrompt>", "Custom system prompt for the chat session") .option("--non-interactive", "Run in non-interactive mode (for testing)") .option("--debug", "Enable debug logging (shows prompts, responses, and internal operations)") .option("--debug-file <file>", "Save debug logs to file (default: chat-debug.log)") .action(async (options) => { try { console.log("šŸ¤– Starting Contaigents Chat..."); // Set up debug logging const debugFile = options.debug ? (options.debugFile || 'chat-debug.log') : undefined; const chatService = new ChatService(process.cwd(), options.debug, debugFile); if (options.debug) { console.log(`šŸ” Debug logging enabled${debugFile ? ` (saving to ${debugFile})` : ''}`); } // Create a new chat session const session = chatService.createSession({ provider: options.provider, model: options.model, temperature: options.temperature, maxTokens: options.maxTokens, systemPrompt: options.systemPrompt }); console.log(`šŸ’¬ Chat session created: ${session.id}`); console.log("Type 'exit', 'quit', or press Ctrl+C to end the chat.\n"); if (options.nonInteractive) { console.log("Running in non-interactive mode for testing"); // In non-interactive mode, read from stdin if available let stdinData = ''; // Check if there's input from stdin (piped input) if (!process.stdin.isTTY) { process.stdin.setEncoding('utf8'); for await (const chunk of process.stdin) { stdinData += chunk; } if (stdinData.trim()) { console.log(`šŸ“ Processing input: ${stdinData.trim()}`); try { const response = await chatService.sendMessage(session.id, stdinData.trim(), { provider: options.provider, model: options.model, temperature: options.temperature, maxTokens: options.maxTokens }); console.log(`šŸ¤– Assistant: ${response.content}`); } catch (error) { console.error(`āŒ Error: ${error.message}`); } } else { console.log("No input provided via stdin"); } } else { console.log("No piped input detected"); } return; } // Set up readline interface for interactive chat const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'šŸ‘¤ You: ' }); // Handle graceful shutdown const cleanup = async () => { console.log("\nšŸ‘‹ Chat session ended."); // Save debug logs if enabled if (options.debug) { const debugLogger = chatService.getDebugLogger(); const dumpFile = `chat-debug-dump-${Date.now()}.json`; await debugLogger.saveDumpToFile(dumpFile); console.log(`šŸ” Debug session saved to: ${dumpFile}`); } console.log("Goodbye!"); rl.close(); process.exit(0); }; process.on('SIGINT', () => cleanup().catch(console.error)); process.on('SIGTERM', () => cleanup().catch(console.error)); // Start the interactive chat loop rl.prompt(); rl.on('line', async (input) => { const userInput = input.trim(); if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') { cleanup().catch(console.error); return; } if (!userInput) { rl.prompt(); return; } try { console.log("šŸ¤” Thinking..."); const response = await chatService.sendMessage(session.id, userInput, { provider: options.provider, model: options.model, temperature: options.temperature, maxTokens: options.maxTokens }); console.log(`šŸ¤– Assistant: ${response.content}\n`); } catch (error) { console.error(`āŒ Error: ${error.message}\n`); } rl.prompt(); }); rl.on('close', () => { cleanup().catch(console.error); }); } catch (error) { console.error(`āŒ Error starting chat: ${error.message}`); process.exit(1); } }); // Test tools command program .command("test-tools") .description("Test the tool architecture functionality") .action(async () => { try { await testToolArchitecture(); } catch (error) { console.error(`āŒ Tool test failed: ${error.message}`); process.exit(1); } }); // Demo chat with tools command program .command("demo-chat-tools") .description("Demonstrate chat service with tool integration") .action(async () => { try { await demonstrateChatWithTools(); } catch (error) { console.error(`āŒ Chat tools demo failed: ${error.message}`); process.exit(1); } }); // Test conversation flow command program .command("test-conversation-flow") .description("Test improved conversation flow with tool calls") .action(async () => { try { const { testImprovedConversationFlow } = await import('./tests/conversationFlowTest.js'); await testImprovedConversationFlow(); } catch (error) { console.error(`āŒ Conversation flow test failed: ${error.message}`); process.exit(1); } }); // Test file write tool command program .command("test-file-write") .description("Test the file writing tool functionality") .action(async () => { try { await testFileWriteTool(); } catch (error) { console.error(`āŒ File write tool test failed: ${error.message}`); process.exit(1); } }); // Test combined tools command program .command("test-combined-tools") .description("Test the combined read/write tool workflow") .action(async () => { try { await testCombinedTools(); } catch (error) { console.error(`āŒ Combined tools test failed: ${error.message}`); process.exit(1); } }); // Test tool call limits command program .command("test-tool-limits") .description("Test the limits and performance of tool call processing") .action(async () => { try { await testToolCallLimits(); } catch (error) { console.error(`āŒ Tool call limits test failed: ${error.message}`); process.exit(1); } }); // Debug ChatService write issue command program .command("debug-chat-write") .description("Debug ChatService file writing issues") .action(async () => { try { await debugChatServiceWrite(); } catch (error) { console.error(`āŒ Debug chat write failed: ${error.message}`); process.exit(1); } }); // Manual write test command program .command("manual-write-test") .description("Manually test writing to README.md file") .action(async () => { try { await manualWriteTest(); } catch (error) { console.error(`āŒ Manual write test failed: ${error.message}`); process.exit(1); } }); // Test command execution tool program.addCommand(createTestCommandToolCommand()); // Configure command program.addCommand(createConfigureCommand()); program.parse(process.argv);