UNPKG

mini-claude-code

Version:

Advanced AI-powered coding assistant with streaming responses, context memory, intelligent auto-completion, error handling, test generation, and task planning

479 lines (421 loc) 12 kB
/** * DeepSeek AI 集成模块 * 提供与DeepSeek AI API的完整集成,支持流式和非流式响应 * * 功能特性: * - 🚀 流式AI响应,避免长时间等待 * - 🔄 多种对话模式支持 * - 🎨 智能代码生成 * - 🔍 代码审查和错误分析 * - ⚙️ 灵活的配置管理 * * @author Mini Claude Code Team * @version 3.0.0 * @since 1.0.0 */ const OpenAI = require('openai/index.js'); const fs = require('fs-extra'); const path = require('path'); /** * DeepSeek AI 客户端类 * 封装了与DeepSeek API的所有交互逻辑 * * @class DeepSeekAI * @example * ```javascript * const ai = new DeepSeekAI('your-api-key'); * const response = await ai.chat('Hello, how are you?'); * console.log(response.response); * ``` */ class DeepSeekAI { /** * 创建DeepSeek AI实例 * * @param {string|null} apiKey - DeepSeek API密钥,如果为null则从环境变量或配置文件加载 * @throws {Error} 如果无法初始化客户端 * * @example * ```javascript * // 使用指定API密钥 * const ai = new DeepSeekAI('sk-your-api-key'); * * // 从环境变量加载 * const ai = new DeepSeekAI(); * ``` */ constructor(apiKey = null) { /** @type {string} 模块名称 */ this.name = 'DeepSeekAI'; /** @type {string|null} API密钥 */ this.apiKey = apiKey; /** @type {OpenAI|null} OpenAI客户端实例 */ this.client = null; /** * 可用的AI模型配置 * @type {Object.<string, string>} */ this.models = { /** 通用对话模型 */ chat: 'deepseek-chat', /** 推理专用模型 */ reasoner: 'deepseek-reasoner' }; // 初始化客户端 this.initializeClient(); } /** * 初始化 OpenAI 客户端 */ initializeClient() { if (!this.apiKey) { // 尝试从环境变量或配置文件加载 this.apiKey = this.loadApiKey(); } if (this.apiKey) { this.client = new OpenAI({ baseURL: 'https://api.deepseek.com', apiKey: this.apiKey }); } } /** * 从多个来源加载 API Key */ loadApiKey() { // 1. 环境变量 if (process.env.DEEPSEEK_API_KEY) { return process.env.DEEPSEEK_API_KEY; } // 2. 配置文件 try { const configPath = path.join(process.cwd(), '.deepseek-config.json'); if (fs.existsSync(configPath)) { const config = fs.readJsonSync(configPath); return config.apiKey; } } catch (error) { // 忽略配置文件错误 } // 3. 从文档中的默认值(用于演示) return 'sk-386b598ba19f49eba2d681f8135f5ae3'; } /** * 保存 API Key 到配置文件 */ async saveApiKey(apiKey) { try { const configPath = path.join(process.cwd(), '.deepseek-config.json'); await fs.writeJson(configPath, { apiKey }, { spaces: 2 }); this.apiKey = apiKey; this.initializeClient(); return { success: true, message: 'API Key saved successfully' }; } catch (error) { return { success: false, error: error.message }; } } /** * 检查 API 是否可用 */ isAvailable() { return this.client !== null && this.apiKey !== null; } /** * 发送聊天消息 - 支持流式和非流式响应 */ async chat(messages, options = {}) { if (!this.isAvailable()) { return { success: false, error: 'DeepSeek API not configured. Please set API key first.' }; } try { const { model = this.models.chat, stream = false, temperature = 0.7, maxTokens = 2000, systemPrompt = null, timeout = 30000 } = options; // 构建消息数组 const chatMessages = []; if (systemPrompt) { chatMessages.push({ role: 'system', content: systemPrompt }); } // 如果 messages 是字符串,转换为消息格式 if (typeof messages === 'string') { chatMessages.push({ role: 'user', content: messages }); } else if (Array.isArray(messages)) { chatMessages.push(...messages); } else { chatMessages.push(messages); } // 设置超时 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const completion = await this.client.chat.completions.create({ model, messages: chatMessages, stream, temperature, max_tokens: maxTokens }, { signal: controller.signal }); clearTimeout(timeoutId); if (stream) { return { success: true, stream: completion, isStreaming: true }; } else { return { success: true, response: completion.choices[0].message.content, usage: completion.usage, model: completion.model, isStreaming: false }; } } catch (apiError) { clearTimeout(timeoutId); throw apiError; } } catch (error) { if (error.name === 'AbortError') { return { success: false, error: 'Request timed out. Please try again with a shorter prompt.', timeout: true }; } return { success: false, error: error.message, details: error.response?.data || null }; } } /** * 流式聊天 - 专门用于流式响应的方法 */ async chatStream(messages, options = {}, onChunk = null) { const streamOptions = { ...options, stream: true }; const result = await this.chat(messages, streamOptions); if (!result.success) { return result; } let fullResponse = ''; const chunks = []; try { for await (const chunk of result.stream) { const content = chunk.choices?.[0]?.delta?.content || ''; if (content) { fullResponse += content; chunks.push({ content, timestamp: new Date(), index: chunks.length }); // 如果提供了回调函数,实时调用 if (onChunk && typeof onChunk === 'function') { await onChunk({ content, fullResponse, isComplete: false, chunk: chunk, chunkIndex: chunks.length - 1 }); } } // 检查是否完成 if (chunk.choices?.[0]?.finish_reason === 'stop') { if (onChunk && typeof onChunk === 'function') { await onChunk({ content: '', fullResponse, isComplete: true, chunk: chunk, chunkIndex: chunks.length }); } break; } } return { success: true, response: fullResponse, chunks, totalChunks: chunks.length, isStreaming: true }; } catch (error) { return { success: false, error: error.message, partialResponse: fullResponse, chunks }; } } /** * 解析自然语言指令为具体操作 */ async parseCommand(userInput, projectContext = null) { const systemPrompt = `你是一个智能编程助手,专门解析用户的自然语言指令并转换为具体的操作。 可用的操作类型: - "read_file": 读取文件 - 参数: filePath - "write_file": 写入文件 - 参数: filePath, content - "edit_file": 编辑文件 - 参数: filePath, oldText, newText - "create_file": 创建文件 - 参数: fileType, fileName - "search_files": 搜索文件内容 - 参数: searchTerm, path (可选) - "run_command": 执行命令 - 参数: command - "list_files": 列出文件 - 参数: path (可选) - "analyze_project": 分析项目结构 - "chat": 普通对话 - 参数: message 项目上下文信息: ${projectContext ? JSON.stringify(projectContext, null, 2) : '暂无项目信息'} 请将用户输入解析为 JSON 格式,包含 action 和 parameters 字段。如果无法解析为具体操作,使用 "chat" 类型。 用户输入:${userInput} 请只返回 JSON,不要包含其他文字解释。`; try { const result = await this.chat(userInput, { systemPrompt, temperature: 0.3, maxTokens: 500 }); if (result.success) { // 尝试解析 JSON 响应 try { const parsed = JSON.parse(result.response); return { success: true, action: parsed.action, parameters: parsed.parameters || {}, originalInput: userInput }; } catch (parseError) { // 如果无法解析 JSON,回退到对话模式 return { success: true, action: 'chat', parameters: { message: result.response }, originalInput: userInput }; } } else { return result; } } catch (error) { return { success: false, error: error.message }; } } /** * 智能代码生成 */ async generateCode(description, context = {}) { const systemPrompt = `你是一个专业的代码生成助手。根据用户描述和项目上下文,生成高质量的代码。 项目信息: - 语言: ${context.languages ? Object.keys(context.languages).join(', ') : '未知'} - 框架: ${context.frameworks ? context.frameworks.map(f => f.name).join(', ') : '未知'} - 包管理器: ${context.packageManager || '未知'} 请生成符合项目风格的代码,包含必要的注释和错误处理。只返回代码,不要包含解释文字。`; try { const result = await this.chat(description, { systemPrompt, temperature: 0.5, maxTokens: 2000 }); if (result.success) { return { success: true, code: result.response, description, context }; } else { return result; } } catch (error) { return { success: false, error: error.message }; } } /** * 代码审查和建议 */ async reviewCode(code, filePath = null) { const systemPrompt = `你是一个专业的代码审查专家。请分析提供的代码并给出改进建议。 关注以下方面: 1. 代码质量和最佳实践 2. 性能优化机会 3. 安全问题 4. 代码风格和一致性 5. 潜在的 bug 或错误 请提供具体、可操作的建议。`; const userInput = `文件路径: ${filePath || '未知'} 代码内容: \`\`\` ${code} \`\`\``; try { const result = await this.chat(userInput, { systemPrompt, temperature: 0.4, maxTokens: 1500 }); return result; } catch (error) { return { success: false, error: error.message }; } } /** * 解释错误信息 */ async explainError(errorMessage, context = {}) { const systemPrompt = `你是一个专业的调试专家。请分析错误信息并提供解决方案。 项目信息: ${context.projectInfo ? JSON.stringify(context.projectInfo, null, 2) : '暂无项目信息'} 请提供: 1. 错误原因的清晰解释 2. 具体的解决步骤 3. 预防类似错误的建议 4. 相关的文档链接(如果适用)`; try { const result = await this.chat(`错误信息: ${errorMessage}`, { systemPrompt, temperature: 0.3, maxTokens: 1000 }); return result; } catch (error) { return { success: false, error: error.message }; } } /** * 获取使用统计 */ getStats() { return { isConfigured: this.isAvailable(), apiKey: this.apiKey ? `${this.apiKey.slice(0, 8)}...` : null, availableModels: this.models }; } } module.exports = DeepSeekAI;