UNPKG

ollama-code-qwen

Version:

Un assistant IA en ligne de commande utilisant Ollama et le modèle qwen2.5-coder pour aider au développement, avec des capacités MCP améliorées et détection d'intentions en français et anglais

333 lines (287 loc) 11.5 kB
/** * Mode interactif léger pour les terminaux distants ou à faible performance * Cette version utilise des interfaces textuelles simples au lieu des widgets TUI complexes */ import readline from 'readline'; import chalk from 'chalk'; import { marked } from 'marked'; import TerminalRenderer from 'marked-terminal'; import { PromptService } from '../services/prompt-service.js'; // Configuration du renderer pour Markdown marked.setOptions({ renderer: new TerminalRenderer({ code: chalk.cyan, blockquote: chalk.gray.italic, table: chalk.white, listitem: chalk.yellow, strong: chalk.bold.green, em: chalk.italic.cyan, heading: chalk.bold.blueBright, }) }); export class LightMode { /** * Initialise le mode interactif léger * @param {OllamaService} ollamaService - Service Ollama * @param {ContextManager} contextManager - Gestionnaire de contexte */ constructor(ollamaService, contextManager) { this.ollamaService = ollamaService; this.contextManager = contextManager; this.config = contextManager.config; this.promptService = new PromptService(this.config); // Interface readline pour entrées utilisateur this.rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: chalk.blue('> '), terminal: true }); // Historique de conversation this.conversation = []; this.projectContext = ''; this.modelName = ollamaService.modelName; // Etat de chargement this.isLoading = false; this.loadingMessage = ''; this.loadingInterval = null; this.projectInfo = null; } /** * Démarrer le mode interactif léger */ async start() { console.clear(); this._printHeader(); try { this._startLoading('Chargement du contexte du projet...'); this.projectContext = await this.contextManager.getContext(); this.projectInfo = await this.contextManager.detectProjectType(); this._stopLoading(); // Initialiser la conversation avec le contexte du projet const projectTypeInfo = this.projectInfo?.language ? `Project type: ${this.projectInfo.language}${this.projectInfo.framework ? ' with ' + this.projectInfo.framework : ''}` : ''; this.conversation = [ { role: 'system', content: `You are an AI coding assistant. You help with programming tasks and questions.\n\n${projectTypeInfo}\n\nCurrent project context:\n${this.projectContext}` } ]; console.log(chalk.green('\nContexte du projet chargé. Utilisez /help pour voir les commandes disponibles.')); console.log(chalk.yellow('Mode léger activé pour les terminaux distants.')); console.log(projectTypeInfo ? chalk.blue(`\nDétecté: ${projectTypeInfo}`) : ''); console.log('\n' + chalk.gray('Entrez votre question ou commande:')); this.rl.prompt(); this.rl.on('line', async (line) => { const input = line.trim(); if (input.startsWith('/')) { await this._handleCommand(input); } else if (input) { await this._handleQuestion(input); } if (!this.isLoading) { this.rl.prompt(); } }); this.rl.on('close', () => { console.log(chalk.green('\nAu revoir !')); process.exit(0); }); } catch (error) { this._stopLoading(); console.error(chalk.red(`Erreur de démarrage: ${error.message}`)); process.exit(1); } } /** * Affiche l'en-tête de l'application */ _printHeader() { console.log('\n' + chalk.bgGreen.black(' Ollama Code ') + chalk.green(' - Mode léger pour terminaux distants\n')); console.log(`Modèle: ${chalk.yellow(this.modelName)}`); console.log(chalk.gray('Utilisez /exit pour quitter, /help pour l\'aide\n')); } /** * Traite les questions de l'utilisateur * @param {string} question - Question de l'utilisateur */ async _handleQuestion(question) { try { // Ajouter la question à la conversation this.conversation.push({ role: 'user', content: question }); // Optimiser le prompt système en fonction de la question const promptConfig = this.promptService.generateSystemPrompt( question, this.projectContext ); // Mettre à jour le message système if (this.conversation.length > 1 && this.conversation[0].role === 'system') { this.conversation[0].content = promptConfig.systemPrompt; } // Afficher l'attente this._startLoading('Réflexion en cours...'); // Obtenir la réponse const response = await this.ollamaService.chat( this.conversation, promptConfig.temperature, this.config.get('maxTokens') || 4096 ); this._stopLoading(); // Ajouter la réponse à la conversation this.conversation.push({ role: 'assistant', content: response }); // Afficher la réponse console.log('\n' + chalk.green('Réponse:')); console.log(marked(response)); console.log(''); // Ligne vide pour la lisibilité } catch (error) { this._stopLoading(); console.error(chalk.red(`Erreur: ${error.message}`)); } } /** * Gère les commandes spéciales * @param {string} command - Commande entrée par l'utilisateur */ async _handleCommand(command) { const cmd = command.toLowerCase().trim(); try { if (cmd === '/help') { this._showHelp(); } else if (cmd === '/exit' || cmd === '/quit') { console.log(chalk.green('Au revoir !')); process.exit(0); } else if (cmd === '/clear') { console.clear(); this._printHeader(); } else if (cmd === '/reset' || cmd === '/init') { this._resetConversation(); console.log(chalk.green('Conversation réinitialisée. Le contexte du projet est conservé.')); } else if (cmd === '/refresh') { await this._refreshContext(); } else if (cmd === '/context') { console.log(chalk.yellow('\nContexte du projet:')); // Limiter l'affichage du contexte pour éviter de surcharger le terminal const contextPreview = this.projectContext.substring(0, 500); console.log(contextPreview + (this.projectContext.length > 500 ? '...(tronqué)' : '')); console.log(chalk.gray(`\nTaille totale: ${this.projectContext.length} caractères`)); } else if (cmd === '/project-info') { this._showProjectInfo(); } else { console.log(chalk.yellow(`Commande inconnue: ${command}. Tapez /help pour voir les commandes disponibles.`)); } } catch (error) { console.error(chalk.red(`Erreur d'exécution de la commande: ${error.message}`)); } } /** * Affiche l'aide des commandes disponibles */ _showHelp() { const helpText = ` ${chalk.green('Commandes disponibles:')} ${chalk.cyan('/help')} - Affiche ce message d'aide ${chalk.cyan('/exit')}, ${chalk.cyan('/quit')} - Quitte l'application ${chalk.cyan('/clear')} - Efface l'écran ${chalk.cyan('/reset')}, ${chalk.cyan('/init')} - Réinitialise la conversation mais garde le contexte ${chalk.cyan('/refresh')} - Actualise le contexte du projet ${chalk.cyan('/context')} - Affiche le contexte du projet actuel ${chalk.cyan('/project-info')} - Affiche les informations détectées sur le projet ${chalk.yellow('Note: Ce mode léger offre des performances améliorées mais moins de fonctionnalités.')} ${chalk.yellow('Pour les fonctionnalités avancées, utilisez ollama-code avec l\'option --standard-mode.')} `; console.log(helpText); } /** * Réinitialise la conversation mais conserve le contexte du projet */ _resetConversation() { const projectTypeInfo = this.projectInfo?.language ? `Project type: ${this.projectInfo.language}${this.projectInfo.framework ? ' with ' + this.projectInfo.framework : ''}` : ''; this.conversation = [ { role: 'system', content: `You are an AI coding assistant. You help with programming tasks and questions.\n\n${projectTypeInfo}\n\nCurrent project context:\n${this.projectContext}` } ]; } /** * Actualise le contexte du projet */ async _refreshContext() { try { this._startLoading('Actualisation du contexte du projet...'); this.projectContext = await this.contextManager.getContext(); // Mettre à jour le message système avec le nouveau contexte const projectTypeInfo = this.projectInfo?.language ? `Project type: ${this.projectInfo.language}${this.projectInfo.framework ? ' with ' + this.projectInfo.framework : ''}` : ''; if (this.conversation.length > 0 && this.conversation[0].role === 'system') { this.conversation[0].content = `You are an AI coding assistant. You help with programming tasks and questions.\n\n${projectTypeInfo}\n\nCurrent project context:\n${this.projectContext}`; } this._stopLoading(); console.log(chalk.green('\nContexte du projet actualisé.')); } catch (error) { this._stopLoading(); throw new Error(`Échec d'actualisation du contexte: ${error.message}`); } } /** * Affiche les informations détectées sur le projet */ _showProjectInfo() { if (!this.projectInfo) { console.log(chalk.yellow('\nAucune information disponible sur le projet. Essayez /refresh pour actualiser.')); return; } console.log(chalk.green('\nInformations sur le projet:')); console.log(chalk.yellow(`\n- Langage: ${this.projectInfo.language || 'Non détecté'}`)); console.log(chalk.yellow(`- Framework: ${this.projectInfo.framework || 'Aucun détecté'}`)); console.log(chalk.yellow(`- Type: ${this.projectInfo.type || 'Non détecté'}`)); if (this.projectInfo.buildTools && this.projectInfo.buildTools.length > 0) { console.log(chalk.yellow(`- Outils de build: ${this.projectInfo.buildTools.join(', ')}`)); } if (this.projectInfo.testing && this.projectInfo.testing.length > 0) { console.log(chalk.yellow(`- Tests: ${this.projectInfo.testing.join(', ')}`)); } if (this.projectInfo.database) { console.log(chalk.yellow(`- Base de données: ${this.projectInfo.database}`)); } } /** * Démarre un indicateur de chargement * @param {string} message - Message à afficher */ _startLoading(message) { this.isLoading = true; this.loadingMessage = message || 'Chargement'; let dots = 0; // Afficher le message initial process.stdout.write(`\r${this.loadingMessage}... `); // Mettre à jour les points périodiquement this.loadingInterval = setInterval(() => { dots = (dots + 1) % 4; const dotsStr = '.'.repeat(dots); process.stdout.write(`\r${this.loadingMessage}${dotsStr.padEnd(4)}`); }, 500); } /** * Arrête l'indicateur de chargement */ _stopLoading() { if (this.loadingInterval) { clearInterval(this.loadingInterval); this.loadingInterval = null; // Effacer la ligne process.stdout.write(`\r${' '.repeat(this.loadingMessage.length + 10)}\r`); this.isLoading = false; } }