UNPKG

codebridge-ai

Version:

Complete fullstack AI coding platform with AST-based code integration and local LLM support. Now with comprehensive web technology support (HTML/CSS/JS) plus JavaScript, Python, Rust, and C++.

393 lines (335 loc) 11.7 kB
/** * Ollama 모델과 CodeBridge 통합 모듈 */ const { spawn } = require('child_process'); const CodeBridge = require('../code-bridge'); const { preprocessOllamaResponse } = require('../utils/ollama-preprocessor'); const { preprocessWebResponse } = require('../utils/web-preprocessor'); class OllamaCodeBridge { constructor(options = {}) { this.model = options.model || 'deepseek-coder:6.7b'; this.codeBridge = new CodeBridge(); this.baseUrl = options.baseUrl || 'http://localhost:11434'; this.temperature = options.temperature || 0.3; this.maxTokens = options.maxTokens || 2048; // 모델별 최적화 설정 this.modelConfig = { 'deepseek-coder:6.7b': { systemPrompt: this.getDeepSeekSystemPrompt(), temperature: 0.3, contextWindow: 16384, specialties: ['javascript', 'python', 'typescript', 'rust'] }, 'codellama:7b': { systemPrompt: this.getCodeLlamaSystemPrompt(), temperature: 0.2, contextWindow: 4096, specialties: ['python', 'javascript', 'c++', 'java'] }, 'starcoder2:3b': { systemPrompt: this.getStarCoderSystemPrompt(), temperature: 0.4, contextWindow: 16384, specialties: ['javascript', 'typescript', 'python', 'go'] } }; } /** * DeepSeek Coder 모델용 시스템 프롬프트 */ getDeepSeekSystemPrompt() { return `You are a code improvement assistant specialized in merging code snippets. Guidelines: 1. Return ONLY the improved method code 2. Use CodeBridge command format: // @command value 3. Focus on practical improvements: error handling, validation, performance 4. Preserve existing functionality while enhancing it 5. Use modern JavaScript/TypeScript patterns Available commands: - // @decorator [name] - Add decorator - // @access [private|public|protected] - Change access level - // @rename [newName] - Rename method - // @params [param1, param2] - Update parameters - // @delete - Mark for deletion Example: // @decorator cache // @decorator validate async getData(id) { if (!id) throw new Error('ID required'); return await this.fetchData(id); }`; } /** * CodeLlama 모델용 시스템 프롬프트 */ getCodeLlamaSystemPrompt() { return `You are a helpful code assistant. When asked to improve code: 1. Return only the updated method 2. Add improvements like error handling and validation 3. Use these comment commands for special instructions: - // @decorator [name] - // @access [private|public|protected] - // @rename [newName] Keep responses concise and focused on the code.`; } /** * StarCoder 모델용 시스템 프롬프트 */ getStarCoderSystemPrompt() { return `Code improvement assistant. Rules: - Return improved method only - Add error handling and validation - Use // @command format for special instructions - Focus on practical enhancements`; } /** * Ollama API 호출 */ async callOllama(prompt, options = {}) { const config = this.modelConfig[this.model] || this.modelConfig['deepseek-coder:6.7b']; const payload = { model: this.model, prompt: prompt, system: options.systemPrompt || config.systemPrompt, temperature: options.temperature || this.temperature, options: { num_predict: this.maxTokens, temperature: options.temperature || this.temperature, top_p: 0.9, top_k: 40 }, stream: false }; try { const response = await fetch(`${this.baseUrl}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`Ollama API error: ${response.status}`); } const data = await response.json(); return data.response; } catch (error) { console.error('Ollama API 호출 실패:', error); throw error; } } /** * 코드 개선 요청 처리 */ async improveCode(originalCode, instruction, options = {}) { const prompt = this.buildImprovePrompt(originalCode, instruction); console.log(`🤖 ${this.model} 모델로 코드 개선 중...`); console.log(`📝 요청: ${instruction}`); try { // Ollama로 개선된 코드 생성 const rawResponse = await this.callOllama(prompt, options); console.log('🔄 LLM 응답 받음, 전처리 중...'); // 파일타입 기반 전처리기 선택 let improvedSnippet; let fileType = options.fileType || 'js'; // 웹 기술인 경우에만 웹 전처리기 사용 if (fileType === 'web' || fileType === 'html' || fileType === 'css') { const webType = this.detectWebType(originalCode, instruction); if (webType) { console.log(`🌐 웹 기술 감지: ${webType}`); improvedSnippet = preprocessWebResponse(rawResponse, webType, this.model); fileType = webType === 'css' ? 'css' : webType === 'html' ? 'html' : 'js'; } else { console.log(`🌐 웹 타입 자동 감지: ${fileType}`); improvedSnippet = preprocessWebResponse(rawResponse, fileType, this.model); } } else { // 기존 전처리기 사용 (JavaScript, Python, Rust, C++ 등) improvedSnippet = preprocessOllamaResponse(rawResponse, this.model, options.debug); } console.log('🔄 전처리 완료, CodeBridge로 병합 중...'); // CodeBridge로 병합 const result = this.codeBridge.process(originalCode, improvedSnippet, fileType); return { success: true, originalCode, instruction, rawResponse, improvedSnippet, finalCode: result, model: this.model }; } catch (error) { return { success: false, error: error.message, originalCode, instruction, model: this.model }; } } /** * 코드 개선 프롬프트 생성 */ buildImprovePrompt(originalCode, instruction) { return `Original code: \`\`\`javascript ${originalCode} \`\`\` Task: ${instruction} Return only the improved method code that follows the instruction. Use // @command format for special modifications.`; } /** * 메서드별 개선 */ async improveMethod(originalCode, methodName, improvements) { const instruction = `Improve the ${methodName} method with these changes: ${improvements.join(', ')}`; return await this.improveCode(originalCode, instruction); } /** * 다중 개선 작업 */ async batchImprove(originalCode, tasks) { const results = []; for (const task of tasks) { console.log(`\n처리 중: ${task.description}`); const result = await this.improveCode(originalCode, task.instruction); results.push({ task: task.description, ...result }); if (result.success) { originalCode = result.finalCode; // 다음 작업에 이전 결과 사용 } } return results; } /** * 모델 성능 테스트 */ async testModel() { const testCode = ` class TestService { getData(id) { return this.data[id]; } }`; const testTasks = [ { description: "에러 처리 추가", instruction: "Add error handling for invalid ID" }, { description: "비동기로 변경", instruction: "Make the method async and add await" }, { description: "캐싱 추가", instruction: "Add caching with @decorator cache command" } ]; console.log(`\n🧪 ${this.model} 모델 성능 테스트 시작...`); const startTime = Date.now(); const results = await this.batchImprove(testCode, testTasks); const endTime = Date.now(); console.log(`\n📊 테스트 완료 (${endTime - startTime}ms)`); console.log(`성공: ${results.filter(r => r.success).length}/${results.length}`); return { model: this.model, duration: endTime - startTime, results, successRate: results.filter(r => r.success).length / results.length }; } /** * 사용 가능한 모델 확인 */ async getAvailableModels() { try { const response = await fetch(`${this.baseUrl}/api/tags`); const data = await response.json(); return data.models.map(model => model.name); } catch (error) { console.error('모델 목록 조회 실패:', error); return []; } } /** * 모델 전환 */ async switchModel(modelName) { const availableModels = await this.getAvailableModels(); if (!availableModels.includes(modelName)) { throw new Error(`모델 '${modelName}'이 설치되지 않음. 사용 가능: ${availableModels.join(', ')}`); } this.model = modelName; console.log(`✅ 모델 전환: ${modelName}`); } /** * 모델 다운로드 */ async downloadModel(modelName) { console.log(`📥 모델 다운로드 시작: ${modelName}`); return new Promise((resolve, reject) => { const process = spawn('ollama', ['pull', modelName]); process.stdout.on('data', (data) => { console.log(data.toString()); }); process.stderr.on('data', (data) => { console.error(data.toString()); }); process.on('close', (code) => { if (code === 0) { console.log(`✅ ${modelName} 다운로드 완료`); resolve(); } else { reject(new Error(`다운로드 실패: exit code ${code}`)); } }); }); } /** * 웹 기술 타입 감지 */ detectWebType(originalCode, instruction) { // 웹 기술 전용으로만 감지 (언어 매개변수 확인) if (!originalCode || typeof originalCode !== 'string') return null; // 코드 내용 기반 감지 (더 엄격한 기준) const codeIndicators = { html: [/<[a-zA-Z][^>]*>[^<]*<\/[a-zA-Z][^>]*>/, /<!DOCTYPE/, /<html/, /<body/, /<form/, /<input/], css: [/\.[a-zA-Z-_][^{]*\{[^}]+\}/, /#[a-zA-Z-_][^{]*\{[^}]+\}/, /@media[^{]*\{/], javascript: [/function\s+[a-zA-Z_$][a-zA-Z0-9_$]*\s*\([^)]*\)\s*\{/, /=>\s*[{(]/, /document\.[a-zA-Z]/, /window\.[a-zA-Z]/] }; // 인스트럭션 기반 감지 const instructionIndicators = { html: [/html/i, /tag/i, /element/i, /semantic/i, /accessibility/i, /aria/i, /label/i], css: [/css/i, /style/i, /responsive/i, /flexbox/i, /grid/i, /mobile/i, /media query/i], javascript: [/script/i, /function/i, /event/i, /dom/i, /jquery/i, /onclick/i] }; let scores = { html: 0, css: 0, javascript: 0 }; // 코드 분석 for (const [type, patterns] of Object.entries(codeIndicators)) { for (const pattern of patterns) { if (pattern.test(originalCode)) { scores[type] += 10; } } } // 인스트럭션 분석 for (const [type, patterns] of Object.entries(instructionIndicators)) { for (const pattern of patterns) { if (pattern.test(instruction)) { scores[type] += 20; } } } // 최고 점수 타입 반환 const maxScore = Math.max(...Object.values(scores)); if (maxScore < 10) return null; // 웹 기술이 아님 const detectedType = Object.keys(scores).find(type => scores[type] === maxScore); console.log(`🎯 웹 타입 감지 결과: ${detectedType} (점수: ${scores[detectedType]})`); return detectedType; } } module.exports = OllamaCodeBridge;