diff --git a/AI_FEATURE_README.md b/AI_FEATURE_README.md new file mode 100644 index 0000000..4a4101c --- /dev/null +++ b/AI_FEATURE_README.md @@ -0,0 +1,422 @@ +# 🤖 AI 智能代码分析功能 + +## 📖 概述 + +这是为 **MCP Feedback Enhanced** 项目设计的 AI 集成方案,在自动提交前通过 AI 智能分析代码变更,动态生成提示词和建议。 + +### 🎯 核心价值 + +**传统方式的问题:** +- ❌ 手动编写 commit message 耗时 +- ❌ 容易遗漏代码中的潜在问题 +- ❌ 缺乏代码质量的客观评估 +- ❌ 团队成员提交规范不统一 + +**AI 增强后的优势:** +- ✅ **自动分析** - AI 自动检查代码变更 +- ✅ **智能建议** - 生成规范的 commit message +- ✅ **问题检测** - 发现潜在 bug、安全漏洞、性能问题 +- ✅ **风险评估** - 评估变更的影响范围和风险等级 +- ✅ **持续学习** - 基于项目特点提供定制化建议 + +--- + +## 🏗️ 技术架构 + +### 文件结构 + +``` +mcp-feedback-enhanced/ +├── src/mcp_feedback_enhanced/ +│ ├── ai/ # 🆕 AI 模块 +│ │ ├── __init__.py # 模块入口 +│ │ └── ai_analyzer.py # AI 分析器核心 +│ └── web/ +│ ├── routes/ +│ │ └── ai_routes.py # 🆕 AI API 路由 +│ └── static/ +│ ├── js/modules/ai/ +│ │ └── ai-analyzer.js # 🆕 前端 AI 模块 +│ └── css/ +│ └── ai-analyzer.css # 🆕 AI UI 样式 +├── examples/ +│ ├── ai-config-example.env # 🆕 配置示例 +│ └── ai-integration-example.py # 🆕 集成示例 +└── docs/ + └── ai-integration-guide.md # 🆕 详细文档 +``` + +### 核心组件 + +#### 1. **AI Analyzer (后端)** +文件: `src/mcp_feedback_enhanced/ai/ai_analyzer.py` + +**功能:** +- 支持多种 AI 提供商 (OpenAI, Anthropic, Ollama) +- Git diff 智能解析 +- 结构化分析结果 +- 异步 API 调用 + +**关键类:** +```python +class AIAnalyzer: + async def analyze_git_diff(git_diff, git_status, context) -> AIAnalysisResult +``` + +#### 2. **API Routes (后端)** +文件: `src/mcp_feedback_enhanced/web/routes/ai_routes.py` + +**端点:** +- `POST /api/ai/analyze-code` - 执行代码分析 +- `GET /api/ai/status` - 获取 AI 功能状态 + +#### 3. **AI Analyzer Module (前端)** +文件: `src/mcp_feedback_enhanced/web/static/js/modules/ai/ai-analyzer.js` + +**功能:** +- 调用 AI 分析 API +- 格式化展示分析结果 +- 一键复制 commit message +- 状态管理 + +--- + +## 🚀 快速开始 + +### 方式 1: 本地免费方案 (Ollama) + +**优势**: 完全免费、隐私安全、无需 API 密钥 + +```bash +# 1. 安装 Ollama +brew install ollama # macOS +# 或访问 https://ollama.com/download + +# 2. 下载代码分析模型 +ollama pull qwen2.5-coder:latest + +# 3. 配置环境变量 +export MCP_AI_ENABLED=true +export MCP_AI_PROVIDER=ollama +export MCP_AI_MODEL=qwen2.5-coder:latest + +# 4. 启动测试 +uvx mcp-feedback-enhanced@latest test --web +``` + +### 方式 2: 云端方案 (OpenAI/Claude) + +```bash +# 配置 OpenAI +export MCP_AI_ENABLED=true +export MCP_AI_PROVIDER=openai +export OPENAI_API_KEY=sk-your-api-key + +# 或配置 Claude +export MCP_AI_PROVIDER=anthropic +export ANTHROPIC_API_KEY=sk-ant-your-api-key +``` + +--- + +## 💡 使用场景 + +### 场景 1: 自动提交前的代码审查 + +**工作流:** +``` +1. 修改代码文件 + ↓ +2. 点击 "🤖 AI 分析" 按钮 + ↓ +3. AI 分析 git diff + ↓ +4. 展示分析报告: + - 变更类型 (feat/fix/refactor...) + - Commit Message 建议 + - 发现的问题 + - 优化建议 + - 风险评估 + ↓ +5. 一键复制 commit message + ↓ +6. 提交代码 +``` + +**示例输出:** +``` +🤖 AI 代码分析报告 +───────────────────────────────────── + +📋 变更摘要 +修复了用户输入处理中的安全漏洞,将危险的 eval() 替换为安全的 json.loads() + +💬 Commit Message 建议 +标题: fix(security): 修复用户输入处理安全漏洞 +正文: +- 移除危险的 eval() 调用 +- 使用 json.loads() 替代,添加异常处理 +- 降低 SQL 注入和代码注入风险 + +⚠️ 发现的问题 +1. 原代码使用 eval() 存在严重安全风险 +2. 缺少输入验证和错误处理 + +💡 优化建议 +1. 添加输入长度限制 +2. 记录异常日志便于追踪 +3. 考虑使用 schema 验证 + +🎯 风险等级: 中 +🎯 置信度: 95% +``` + +### 场景 2: 团队代码规范检查 + +AI 自动检查: +- Commit message 是否符合 Conventional Commits 规范 +- 代码风格是否一致 +- 是否有明显的代码异味 +- 文档是否需要更新 + +### 场景 3: 安全漏洞扫描 + +针对性检测: +- SQL 注入风险 +- XSS 跨站脚本 +- 密码/密钥硬编码 +- 不安全的加密算法 +- 依赖库安全问题 + +--- + +## 🔧 配置选项 + +### 环境变量 + +| 变量 | 说明 | 默认值 | 示例 | +|------|------|--------|------| +| `MCP_AI_ENABLED` | 启用 AI 功能 | `false` | `true` | +| `MCP_AI_PROVIDER` | AI 提供商 | `ollama` | `openai`, `anthropic`, `ollama` | +| `MCP_AI_MODEL` | 模型名称 | 自动选择 | `gpt-4-turbo-preview` | +| `MCP_AI_API_KEY` | 通用 API 密钥 | - | `sk-...` | +| `OPENAI_API_KEY` | OpenAI 密钥 | - | `sk-...` | +| `ANTHROPIC_API_KEY` | Claude 密钥 | - | `sk-ant-...` | +| `MCP_AI_BASE_URL` | 自定义 API 端点 | - | `https://api.openai.com/v1` | + +### MCP 配置示例 + +在 Cursor/Cline 的 MCP 配置文件中: + +```json +{ + "mcpServers": { + "mcp-feedback-enhanced": { + "command": "uvx", + "args": ["mcp-feedback-enhanced@latest"], + "timeout": 600, + "env": { + "MCP_AI_ENABLED": "true", + "MCP_AI_PROVIDER": "ollama", + "MCP_AI_MODEL": "qwen2.5-coder:latest", + "MCP_WEB_HOST": "127.0.0.1", + "MCP_WEB_PORT": "8765", + "MCP_DEBUG": "false" + }, + "autoApprove": ["interactive_feedback"] + } + } +} +``` + +--- + +## 📊 性能与成本 + +### Ollama (本地) +- **成本**: 免费 +- **速度**: 快 (取决于本地硬件) +- **隐私**: 100% 本地,无数据上传 +- **推荐**: 日常开发使用 + +### OpenAI GPT-4 +- **成本**: $0.01/1K tokens (输入) + $0.03/1K tokens (输出) +- **速度**: 中等 (网络延迟) +- **质量**: 非常高 +- **推荐**: 关键代码审查、生产环境 + +### Anthropic Claude 3.5 Sonnet +- **成本**: $0.003/1K tokens (输入) + $0.015/1K tokens (输出) +- **速度**: 快 +- **质量**: 高,代码理解深度好 +- **推荐**: 大规模代码分析 + +**估算**: 分析一次中等规模的代码变更 (~500 行 diff): +- Ollama: 免费 +- GPT-4: ~$0.05 +- Claude 3.5: ~$0.02 + +--- + +## 🛠️ 开发与测试 + +### 运行示例 + +```bash +# 基础示例 +python examples/ai-integration-example.py + +# 测试 AI 功能 +uv run python -m mcp_feedback_enhanced.ai.ai_analyzer +``` + +### API 测试 + +```bash +# 检查 AI 状态 +curl http://localhost:8765/api/ai/status + +# 分析代码 +curl -X POST http://localhost:8765/api/ai/analyze-code \ + -H "Content-Type: application/json" \ + -d '{ + "project_dir": ".", + "context": "Python Web 项目" + }' +``` + +### 单元测试 + +```bash +# 运行 AI 模块测试 +pytest tests/unit/test_ai_analyzer.py -v + +# 测试覆盖率 +pytest tests/ --cov=src/mcp_feedback_enhanced/ai +``` + +--- + +## 🎨 UI 集成 + +### 在 Web UI 中添加 AI 分析按钮 + +```html + + + +
+``` + +```javascript +// 调用 AI 分析 +async function analyzeCode() { + const analyzer = new MCPFeedback.AI.AIAnalyzer(); + + const result = await analyzer.analyzeCode( + '/path/to/project', + '项目上下文信息' + ); + + // 展示结果 + const html = analyzer.formatAnalysisHTML(result); + document.getElementById('ai-analysis-result').innerHTML = html; +} +``` + +--- + +## 🔒 安全性考虑 + +### 数据隐私 + +- ✅ **Ollama**: 完全本地运行,零数据泄露风险 +- ⚠️ **OpenAI/Claude**: 代码会发送到云端,请注意: + - 不要分析包含敏感信息的代码 + - 遵守公司的数据安全政策 + - 考虑使用私有化部署 + +### 最佳实践 + +1. **敏感项目**: 使用 Ollama 本地分析 +2. **开源项目**: 可以使用云端服务 +3. **企业项目**: 咨询安全团队后决定 +4. **API 密钥**: 使用环境变量,不要提交到 Git + +--- + +## 📚 进阶应用 + +### 自定义分析维度 + +创建专项分析器: + +```python +from mcp_feedback_enhanced.ai import AIAnalyzer + +class PerformanceAnalyzer(AIAnalyzer): + """专注于性能优化的分析器""" + + def _build_analysis_prompt(self, git_diff, git_status, context): + return f""" + 请从性能优化角度分析这次代码变更: + + 关注点: + 1. 算法复杂度 + 2. 内存使用 + 3. 数据库查询优化 + 4. 缓存策略 + 5. 异步处理 + + ... + """ +``` + +### 集成到 CI/CD + +在 GitHub Actions 中: + +```yaml +- name: AI Code Review + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MCP_AI_ENABLED: true + run: | + python -c " + from mcp_feedback_enhanced.ai import AIAnalyzer + # 分析代码并发布评论 + " +``` + +--- + +## 🤝 贡献 + +欢迎贡献改进! 可以: + +- 🔧 添加新的 AI 提供商支持 +- 📝 优化分析提示词模板 +- 🎨 改进 UI 展示 +- 📖 完善文档 +- 🐛 修复 Bug + +--- + +## 📝 许可证 + +MIT License - 详见 [LICENSE](LICENSE) + +--- + +## 📞 支持 + +- 📖 详细文档: [docs/ai-integration-guide.md](docs/ai-integration-guide.md) +- 🐛 问题反馈: [GitHub Issues](https://github.com/Minidoracat/mcp-feedback-enhanced/issues) +- 💬 社区讨论: [Discord](https://discord.gg/Gur2V67) + +--- + +**更新日期**: 2025-10-29 +**作者**: MCP Feedback Enhanced Team diff --git a/docs/ai-integration-guide.md b/docs/ai-integration-guide.md new file mode 100644 index 0000000..a7b7468 --- /dev/null +++ b/docs/ai-integration-guide.md @@ -0,0 +1,456 @@ +# AI 集成使用指南 + +## 📚 目录 + +- [功能简介](#功能简介) +- [快速开始](#快速开始) +- [配置说明](#配置说明) +- [使用方法](#使用方法) +- [进阶配置](#进阶配置) +- [常见问题](#常见问题) + +--- + +## 功能简介 + +MCP Feedback Enhanced 的 AI 集成功能为项目提供智能代码分析能力,主要特性包括: + +### 🎯 核心功能 + +1. **Git Diff 智能分析** - AI 自动分析代码变更内容 +2. **Commit Message 生成** - 自动生成符合规范的提交信息 +3. **代码问题检测** - 识别潜在的 bug、安全问题、性能问题 +4. **优化建议提供** - 提供具体可行的代码优化建议 +5. **风险评估** - 评估代码变更的风险等级 +6. **动态提示词生成** - 基于分析结果动态生成智能提示 + +### 🤖 支持的 AI 提供商 + +| 提供商 | 推荐场景 | API 密钥 | 成本 | +|--------|---------|----------|------| +| **Ollama** | 本地开发、隐私要求高 | ❌ 不需要 | ✅ 免费 | +| **OpenAI** | 高质量分析、生产环境 | ✅ 需要 | 💰 付费 | +| **Anthropic** | 代码理解深度、安全性 | ✅ 需要 | 💰 付费 | + +--- + +## 快速开始 + +### 方案 1: 使用 Ollama (推荐新手) + +**优势**: 完全免费、无需 API 密钥、隐私保护、响应快速 + +#### 步骤 1: 安装 Ollama + +```bash +# macOS +brew install ollama + +# Linux +curl -fsSL https://ollama.com/install.sh | sh + +# Windows +# 访问 https://ollama.com/download 下载安装程序 +``` + +#### 步骤 2: 下载代码分析模型 + +```bash +# 推荐: Qwen2.5 Coder (专为代码优化) +ollama pull qwen2.5-coder:latest + +# 备选: CodeLlama +ollama pull codellama:latest + +# 备选: DeepSeek Coder +ollama pull deepseek-coder:latest +``` + +#### 步骤 3: 验证模型运行 + +```bash +# 测试模型是否正常运行 +ollama run qwen2.5-coder:latest + +# 输入任意代码相关问题测试 +# 输入 /bye 退出 +``` + +#### 步骤 4: 配置环境变量 + +在项目根目录创建 `.env` 文件: + +```bash +# 启用 AI 功能 +MCP_AI_ENABLED=true + +# 使用 Ollama +MCP_AI_PROVIDER=ollama + +# 指定模型 (可选) +MCP_AI_MODEL=qwen2.5-coder:latest +``` + +#### 步骤 5: 启动 MCP 服务 + +```bash +# 测试 AI 功能 +uvx mcp-feedback-enhanced@latest test --web + +# 或在 MCP 配置中添加环境变量 +``` + +--- + +### 方案 2: 使用 OpenAI GPT-4 + +#### 步骤 1: 获取 API 密钥 + +1. 访问 [OpenAI Platform](https://platform.openai.com/) +2. 登录并进入 API Keys 页面 +3. 创建新的 API 密钥 + +#### 步骤 2: 配置环境变量 + +```bash +# 启用 AI 功能 +MCP_AI_ENABLED=true + +# 使用 OpenAI +MCP_AI_PROVIDER=openai + +# 设置 API 密钥 +OPENAI_API_KEY=sk-your-api-key-here + +# 选择模型 (可选) +MCP_AI_MODEL=gpt-4-turbo-preview +``` + +--- + +### 方案 3: 使用 Anthropic Claude + +#### 步骤 1: 获取 API 密钥 + +1. 访问 [Anthropic Console](https://console.anthropic.com/) +2. 登录并创建 API 密钥 + +#### 步骤 2: 配置环境变量 + +```bash +# 启用 AI 功能 +MCP_AI_ENABLED=true + +# 使用 Anthropic +MCP_AI_PROVIDER=anthropic + +# 设置 API 密钥 +ANTHROPIC_API_KEY=sk-ant-your-api-key-here + +# 选择模型 (可选) +MCP_AI_MODEL=claude-3-5-sonnet-20241022 +``` + +--- + +## 配置说明 + +### 环境变量完整列表 + +```bash +# ===== AI 功能控制 ===== +MCP_AI_ENABLED=true # 启用/禁用 AI 功能 + +# ===== AI 提供商 ===== +MCP_AI_PROVIDER=ollama # 选项: openai, anthropic, ollama + +# ===== API 认证 ===== +MCP_AI_API_KEY=xxx # 通用 API 密钥 (优先级最高) +OPENAI_API_KEY=sk-xxx # OpenAI 专用密钥 +ANTHROPIC_API_KEY=sk-ant-xxx # Anthropic 专用密钥 + +# ===== 模型选择 ===== +MCP_AI_MODEL=gpt-4-turbo-preview # 自定义模型 + +# ===== API 端点 ===== +MCP_AI_BASE_URL=https://api.openai.com/v1 # 自定义 API 端点 +``` + +### MCP 服务器配置示例 + +在 Cursor/Cline 等 IDE 的 MCP 配置中添加: + +```json +{ + "mcpServers": { + "mcp-feedback-enhanced": { + "command": "uvx", + "args": ["mcp-feedback-enhanced@latest"], + "timeout": 600, + "env": { + "MCP_AI_ENABLED": "true", + "MCP_AI_PROVIDER": "ollama", + "MCP_AI_MODEL": "qwen2.5-coder:latest", + "MCP_WEB_HOST": "127.0.0.1", + "MCP_WEB_PORT": "8765" + }, + "autoApprove": ["interactive_feedback"] + } + } +} +``` + +--- + +## 使用方法 + +### 1. 在 Web UI 中使用 + +#### 自动提交前分析 + +1. **修改代码** - 进行代码变更 +2. **点击 "🤖 AI 分析"** 按钮 +3. **等待分析** - AI 自动分析 git diff +4. **查看结果**: + - 📋 变更摘要 + - 💬 Commit Message 建议 + - ⚠️ 潜在问题列表 + - 💡 优化建议 + - 🎯 风险评估 +5. **一键应用** - 点击复制按钮使用建议的 commit message + +#### 动态提示词生成 + +AI 分析完成后,系统会自动: +- 生成与变更类型匹配的提示词 +- 提供针对性的下一步建议 +- 高亮显示需要关注的问题 + +### 2. 通过 API 调用 + +#### 检查 AI 状态 + +```bash +curl http://localhost:8765/api/ai/status +``` + +响应示例: +```json +{ + "enabled": true, + "provider": "ollama", + "model": "qwen2.5-coder:latest", + "configured": true, + "status": "ready" +} +``` + +#### 分析代码变更 + +```bash +curl -X POST http://localhost:8765/api/ai/analyze-code \ + -H "Content-Type: application/json" \ + -d '{ + "project_dir": "/path/to/your/project", + "context": "这是一个 Python Web 项目" + }' +``` + +--- + +## 进阶配置 + +### 1. 自定义 AI 提示词模板 + +修改 `src/mcp_feedback_enhanced/ai/ai_analyzer.py` 中的 `_build_analysis_prompt` 方法: + +```python +def _build_analysis_prompt(self, git_diff, git_status, project_context): + """自定义提示词模板""" + return f""" + 你是一个{project_context}领域的专家。 + + 请分析以下代码变更: + {git_diff} + + 关注点: + 1. 安全性 + 2. 性能 + 3. 可维护性 + + ... + """ +``` + +### 2. 调整分析参数 + +```python +# 在 ai_analyzer.py 中调整 +payload = { + "model": self.model, + "temperature": 0.1, # 降低创造性,提高稳定性 + "max_tokens": 2048, # 限制响应长度 +} +``` + +### 3. 集成到 Git Hooks + +在 `.git/hooks/pre-commit` 中: + +```bash +#!/bin/bash + +# 调用 AI 分析 +python -c " +from mcp_feedback_enhanced.ai import AIAnalyzer +import asyncio + +async def analyze(): + analyzer = AIAnalyzer() + result = await analyzer.analyze_git_diff( + git_diff='$(git diff --cached)', + git_status='$(git status --short)' + ) + + if result.risk_level == 'high': + print('❌ 检测到高风险变更,请仔细审查!') + print(result.summary) + exit(1) + +asyncio.run(analyze()) +" +``` + +--- + +## 常见问题 + +### Q1: AI 分析速度慢怎么办? + +**A**: +- **使用 Ollama**: 本地运行,响应更快 +- **限制 diff 大小**: 在 `ai_analyzer.py` 中调整 `git_diff[:5000]` +- **使用更快的模型**: 如 `gpt-3.5-turbo` 或 `claude-3-haiku` + +### Q2: API 调用失败怎么办? + +**A**: 检查以下几点: +```bash +# 1. 验证 API 密钥 +echo $OPENAI_API_KEY + +# 2. 测试网络连接 +curl https://api.openai.com/v1/models \ + -H "Authorization: Bearer $OPENAI_API_KEY" + +# 3. 检查 Ollama 服务状态 +ollama list +``` + +### Q3: 如何降低 API 成本? + +**A**: +1. **使用 Ollama** - 完全免费 +2. **使用更便宜的模型** - `gpt-3.5-turbo` 而非 `gpt-4` +3. **限制调用频率** - 只在关键提交前分析 +4. **缓存分析结果** - 避免重复分析相同代码 + +### Q4: 分析结果不准确? + +**A**: +1. **提供更多上下文** - 在 `project_context` 中详细描述项目 +2. **调整提示词** - 修改 `_build_analysis_prompt` 方法 +3. **使用更强大的模型** - 切换到 `gpt-4` 或 `claude-3-5-sonnet` +4. **增加 temperature** - 提高创造性(但可能降低一致性) + +### Q5: 如何在 CI/CD 中使用? + +**A**: 在 GitHub Actions 中: + +```yaml +name: AI Code Review + +on: [pull_request] + +jobs: + ai-review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install MCP Feedback Enhanced + run: pip install mcp-feedback-enhanced + + - name: AI Code Analysis + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MCP_AI_ENABLED: true + MCP_AI_PROVIDER: openai + run: | + python -m mcp_feedback_enhanced.ai.ai_analyzer +``` + +--- + +## 技术架构 + +``` +┌─────────────────────────────────────────┐ +│ Web UI / API 层 │ +│ - 用户交互界面 │ +│ - RESTful API 端点 │ +└───────────────┬─────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ AI Analyzer 服务层 │ +│ - AIAnalyzer 类 │ +│ - 提示词构建 │ +│ - 结果解析 │ +└───────────────┬─────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ AI Provider 层 │ +│ ├─ OpenAI API Client │ +│ ├─ Anthropic API Client │ +│ └─ Ollama API Client │ +└───────────────┬─────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ 外部 AI 服务 │ +│ - OpenAI GPT Models │ +│ - Anthropic Claude Models │ +│ - Local Ollama Models │ +└─────────────────────────────────────────┘ +``` + +--- + +## 贡献指南 + +欢迎贡献代码! 以下是一些改进方向: + +1. **新增 AI 提供商** - 支持 Google Gemini、Cohere 等 +2. **提示词优化** - 改进分析准确性 +3. **性能优化** - 减少 API 调用次数 +4. **UI 增强** - 更好的分析结果展示 +5. **多语言支持** - 支持更多编程语言的专项分析 + +--- + +## 许可证 + +MIT License - 详见 [LICENSE](../LICENSE) 文件 + +--- + +**更新日期**: 2025-10-29 +**版本**: 1.0.0 +**维护者**: MCP Feedback Enhanced Team diff --git a/examples/ai-config-example.env b/examples/ai-config-example.env new file mode 100644 index 0000000..c6a3e96 --- /dev/null +++ b/examples/ai-config-example.env @@ -0,0 +1,101 @@ +# MCP Feedback Enhanced - AI 功能配置示例 +# =========================================== +# +# 将此文件复制为 .env 并根据需要配置 + +# ===== AI 功能总开关 ===== +# 是否启用 AI 代码分析功能 +# 值: true/false +MCP_AI_ENABLED=true + +# ===== AI 提供商配置 ===== +# 选择 AI 提供商 +# 可选值: openai, anthropic, ollama +# - openai: 使用 OpenAI GPT 模型 +# - anthropic: 使用 Anthropic Claude 模型 +# - ollama: 使用本地 Ollama 模型(无需 API 密钥) +MCP_AI_PROVIDER=ollama + +# ===== API 密钥配置 ===== +# 通用 API 密钥(优先级最高) +# MCP_AI_API_KEY=your-api-key-here + +# OpenAI API 密钥 +# OPENAI_API_KEY=sk-... + +# Anthropic API 密钥 +# ANTHROPIC_API_KEY=sk-ant-... + +# ===== 模型配置 ===== +# 指定使用的模型(可选,不设置则使用默认模型) +# +# OpenAI 模型示例: +# - gpt-4-turbo-preview (默认) +# - gpt-4 +# - gpt-3.5-turbo +# +# Anthropic 模型示例: +# - claude-3-5-sonnet-20241022 (默认) +# - claude-3-opus-20240229 +# - claude-3-sonnet-20240229 +# +# Ollama 模型示例: +# - qwen2.5-coder:latest (默认,推荐用于代码分析) +# - codellama:latest +# - deepseek-coder:latest +# - llama3:latest +# +# MCP_AI_MODEL=gpt-4-turbo-preview + +# ===== API 端点配置 ===== +# 自定义 API 基础 URL(可选) +# 用于使用代理或自托管服务 +# +# OpenAI 默认: https://api.openai.com/v1 +# Anthropic 默认: https://api.anthropic.com/v1 +# Ollama 默认: http://localhost:11434 +# +# MCP_AI_BASE_URL=https://api.openai.com/v1 + +# ===== 示例配置场景 ===== + +# 场景 1: 使用 OpenAI GPT-4 +# MCP_AI_ENABLED=true +# MCP_AI_PROVIDER=openai +# OPENAI_API_KEY=sk-your-openai-key +# MCP_AI_MODEL=gpt-4-turbo-preview + +# 场景 2: 使用 Claude 3.5 Sonnet +# MCP_AI_ENABLED=true +# MCP_AI_PROVIDER=anthropic +# ANTHROPIC_API_KEY=sk-ant-your-anthropic-key +# MCP_AI_MODEL=claude-3-5-sonnet-20241022 + +# 场景 3: 使用本地 Ollama (推荐新手) +# MCP_AI_ENABLED=true +# MCP_AI_PROVIDER=ollama +# MCP_AI_MODEL=qwen2.5-coder:latest +# (无需 API 密钥) + +# ===== Ollama 安装指南 ===== +# 1. 下载安装: https://ollama.com/download +# 2. 安装模型: ollama pull qwen2.5-coder:latest +# 3. 验证运行: ollama run qwen2.5-coder:latest +# 4. 启用本配置即可使用 + +# ===== 其他 MCP 配置 ===== +# 这些是 mcp-feedback-enhanced 的基础配置 + +# 调试模式 +MCP_DEBUG=false + +# Web UI 配置 +MCP_WEB_HOST=127.0.0.1 +MCP_WEB_PORT=8765 + +# 桌面应用模式 +MCP_DESKTOP_MODE=false + +# 界面语言 +# 可选: zh-TW, zh-CN, en +MCP_LANGUAGE=zh-CN diff --git a/examples/ai-integration-example.py b/examples/ai-integration-example.py new file mode 100644 index 0000000..d2bb6b5 --- /dev/null +++ b/examples/ai-integration-example.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +""" +AI 集成完整示例 +=============== + +演示如何在 MCP Feedback Enhanced 中使用 AI 分析功能。 +""" + +import asyncio +import os +import sys + +# 添加项目路径到 Python 路径 +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from src.mcp_feedback_enhanced.ai import AIAnalyzer, AIProvider + + +async def example_basic_analysis(): + """示例 1: 基础代码分析""" + print("=" * 60) + print("示例 1: 基础代码分析 (使用 Ollama)") + print("=" * 60) + + # 模拟 Git Diff + git_diff = """diff --git a/app.py b/app.py +index 1234567..abcdefg 100644 +--- a/app.py ++++ b/app.py +@@ -15,8 +15,12 @@ def process_user_input(user_data): + # 处理用户输入 +- result = eval(user_data) # 危险! 不要使用 eval +- return result ++ try: ++ # 使用 json.loads 替代 eval ++ result = json.loads(user_data) ++ return result ++ except json.JSONDecodeError: ++ return {"error": "Invalid JSON"} +""" + + git_status = """On branch main +Changes to be committed: + modified: app.py +""" + + # 创建分析器 (使用 Ollama) + analyzer = AIAnalyzer(provider=AIProvider.OLLAMA) + + # 执行分析 + print("\n🤖 正在分析代码变更...") + result = await analyzer.analyze_git_diff( + git_diff=git_diff, + git_status=git_status, + project_context="这是一个 Python Flask Web 应用", + ) + + # 打印结果 + print("\n📊 分析结果:") + print(f" 变更类型: {result.change_type.value}") + print(f" 严重性: {result.severity.value}") + print(f" 风险等级: {result.risk_level}") + print(f" 破坏性变更: {'是' if result.breaking_changes else '否'}") + print(f"\n📋 摘要:\n {result.summary}") + print(f"\n💬 建议的 Commit Message:") + print(f" 标题: {result.commit_title}") + print(f" 正文:\n{result.commit_body}") + + if result.issues_found: + print(f"\n⚠️ 发现的问题:") + for i, issue in enumerate(result.issues_found, 1): + print(f" {i}. {issue}") + + if result.suggestions: + print(f"\n💡 优化建议:") + for i, suggestion in enumerate(result.suggestions, 1): + print(f" {i}. {suggestion}") + + print(f"\n🎯 AI 置信度: {result.confidence_score:.1%}") + + +async def example_openai_analysis(): + """示例 2: 使用 OpenAI 进行分析""" + print("\n\n") + print("=" * 60) + print("示例 2: 使用 OpenAI GPT-4 分析") + print("=" * 60) + + # 检查 API 密钥 + api_key = os.getenv("OPENAI_API_KEY") + if not api_key: + print("\n⚠️ 未设置 OPENAI_API_KEY 环境变量,跳过此示例") + return + + git_diff = """diff --git a/database.py b/database.py +index abc123..def456 100644 +--- a/database.py ++++ b/database.py +@@ -10,7 +10,10 @@ class Database: + def query(self, sql): +- cursor = self.conn.cursor() +- cursor.execute(sql) # SQL 注入风险! +- return cursor.fetchall() ++ # 使用参数化查询防止 SQL 注入 ++ cursor = self.conn.cursor() ++ # 添加输入验证 ++ if not self._is_safe_query(sql): ++ raise ValueError("Unsafe query detected") ++ cursor.execute(sql) ++ return cursor.fetchall() +""" + + # 创建 OpenAI 分析器 + analyzer = AIAnalyzer(provider=AIProvider.OPENAI, model="gpt-4-turbo-preview") + + print("\n🤖 正在使用 GPT-4 分析代码...") + result = await analyzer.analyze_git_diff( + git_diff=git_diff, + git_status="modified: database.py", + project_context="Python 数据库访问层,需要高安全性", + ) + + print(f"\n📊 GPT-4 分析结果:") + print(f" 变更类型: {result.change_type.value}") + print(f" 安全风险: {result.risk_level}") + print(f" Commit: {result.commit_title}") + + +async def example_batch_analysis(): + """示例 3: 批量分析多个文件""" + print("\n\n") + print("=" * 60) + print("示例 3: 批量分析多个文件变更") + print("=" * 60) + + # 模拟多个文件的变更 + changes = [ + { + "file": "frontend/app.js", + "diff": """diff --git a/frontend/app.js ++++ b/frontend/app.js +@@ -5,3 +5,4 @@ +-const API_URL = "http://api.example.com" ++const API_URL = process.env.REACT_APP_API_URL || "http://localhost:3000" +""", + }, + { + "file": "backend/auth.py", + "diff": """diff --git a/backend/auth.py ++++ b/backend/auth.py +@@ -10,5 +10,8 @@ +-def hash_password(password): +- return hashlib.md5(password.encode()).hexdigest() ++def hash_password(password): ++ # 使用更安全的 bcrypt ++ import bcrypt ++ return bcrypt.hashpw(password.encode(), bcrypt.gensalt()) +""", + }, + ] + + analyzer = AIAnalyzer(provider=AIProvider.OLLAMA) + + print("\n🤖 开始批量分析...") + + # 并发分析多个文件 + tasks = [] + for change in changes: + task = analyzer.analyze_git_diff( + git_diff=change["diff"], + git_status=f"modified: {change['file']}", + project_context=f"分析文件: {change['file']}", + ) + tasks.append(task) + + results = await asyncio.gather(*tasks) + + # 汇总结果 + print("\n📊 批量分析结果汇总:") + for i, (change, result) in enumerate(zip(changes, results), 1): + print(f"\n {i}. {change['file']}") + print(f" 类型: {result.change_type.value}") + print(f" 风险: {result.risk_level}") + print(f" 摘要: {result.summary}") + + +async def example_custom_prompt(): + """示例 4: 自定义分析提示词""" + print("\n\n") + print("=" * 60) + print("示例 4: 自定义分析维度") + print("=" * 60) + + # 创建自定义分析器类 + class SecurityFocusedAnalyzer(AIAnalyzer): + """专注于安全性的 AI 分析器""" + + def _build_analysis_prompt(self, git_diff, git_status, project_context): + """自定义提示词 - 强调安全性分析""" + return f"""你是一个网络安全专家和代码审查员。 + +项目上下文: {project_context} + +Git Diff: +```diff +{git_diff[:3000]} +``` + +请从安全角度深度分析这次代码变更,关注: +1. **安全漏洞**: SQL 注入、XSS、CSRF、命令注入等 +2. **敏感信息**: API 密钥、密码、token 泄露 +3. **访问控制**: 权限检查、认证绕过 +4. **加密问题**: 弱加密算法、明文存储 +5. **依赖安全**: 第三方库的已知漏洞 + +以 JSON 格式返回: +{{ + "change_type": "fix|feat|refactor|...", + "severity": "high|medium|low", + "summary": "安全角度的变更摘要", + "commit_title": "提交标题", + "commit_body": "详细说明", + "issues_found": ["安全问题1", "安全问题2"], + "suggestions": ["安全建议1", "安全建议2"], + "affected_files": ["文件列表"], + "risk_level": "high|medium|low", + "breaking_changes": false, + "confidence_score": 0.95 +}} +""" + + analyzer = SecurityFocusedAnalyzer(provider=AIProvider.OLLAMA) + + git_diff = """diff --git a/login.py ++++ b/login.py +@@ -5,3 +5,5 @@ +-password = request.form['password'] +-if password == "admin123": ++password = request.form['password'] ++hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) ++if bcrypt.checkpw(password.encode(), stored_hash): +""" + + print("\n🔒 使用安全专注分析器...") + result = await analyzer.analyze_git_diff( + git_diff=git_diff, + git_status="modified: login.py", + project_context="用户认证系统", + ) + + print(f"\n🔍 安全分析结果:") + print(f" 风险等级: {result.risk_level}") + print(f" 发现问题: {len(result.issues_found)} 个") + for issue in result.issues_found: + print(f" - {issue}") + + +async def main(): + """运行所有示例""" + print("\n🚀 MCP Feedback Enhanced - AI 集成示例") + print("=" * 60) + + try: + # 示例 1: 基础分析 (Ollama) + await example_basic_analysis() + + # 示例 2: OpenAI 分析 (需要 API 密钥) + await example_openai_analysis() + + # 示例 3: 批量分析 + await example_batch_analysis() + + # 示例 4: 自定义提示词 + await example_custom_prompt() + + print("\n\n" + "=" * 60) + print("✅ 所有示例运行完成!") + print("=" * 60) + + except Exception as e: + print(f"\n❌ 错误: {e}") + import traceback + + traceback.print_exc() + + +if __name__ == "__main__": + # 运行示例 + asyncio.run(main()) diff --git a/src/mcp_feedback_enhanced/ai/__init__.py b/src/mcp_feedback_enhanced/ai/__init__.py new file mode 100644 index 0000000..d12fd52 --- /dev/null +++ b/src/mcp_feedback_enhanced/ai/__init__.py @@ -0,0 +1,16 @@ +""" +AI 分析模块 +=========== + +提供 AI 驱动的代码分析功能。 +""" + +from .ai_analyzer import AIAnalyzer, AIAnalysisResult, AIProvider, ChangeType, ChangeSeverity + +__all__ = [ + "AIAnalyzer", + "AIAnalysisResult", + "AIProvider", + "ChangeType", + "ChangeSeverity", +] diff --git a/src/mcp_feedback_enhanced/ai/ai_analyzer.py b/src/mcp_feedback_enhanced/ai/ai_analyzer.py new file mode 100644 index 0000000..99682f8 --- /dev/null +++ b/src/mcp_feedback_enhanced/ai/ai_analyzer.py @@ -0,0 +1,407 @@ +#!/usr/bin/env python3 +""" +AI 代码分析服务模块 +================== + +提供 AI 驱动的代码变更分析功能,支持多种 LLM 提供商。 +用于在自动提交前智能分析代码变更并生成建议。 + +支持的 AI 提供商: +- OpenAI (GPT-4, GPT-3.5) +- Anthropic (Claude) +- 本地 LLM (Ollama) + +作者: MCP Feedback Enhanced Team +版本: 1.0.0 +""" + +import asyncio +import json +import os +from dataclasses import dataclass +from enum import Enum +from typing import Any + +import aiohttp + + +class AIProvider(Enum): + """AI 提供商枚举""" + + OPENAI = "openai" + ANTHROPIC = "anthropic" + OLLAMA = "ollama" # 本地 LLM + + +class ChangeSeverity(Enum): + """代码变更严重性评级""" + + HIGH = "high" # 重大变更 + MEDIUM = "medium" # 中等变更 + LOW = "low" # 轻微变更 + + +class ChangeType(Enum): + """代码变更类型""" + + FEAT = "feat" # 新功能 + FIX = "fix" # Bug 修复 + REFACTOR = "refactor" # 重构 + DOCS = "docs" # 文档更新 + STYLE = "style" # 代码格式 + TEST = "test" # 测试相关 + CHORE = "chore" # 构建/工具 + PERF = "perf" # 性能优化 + + +@dataclass +class AIAnalysisResult: + """AI 分析结果数据类""" + + # 基本信息 + change_type: ChangeType + severity: ChangeSeverity + summary: str # 一句话摘要 + + # Commit Message 建议 + commit_title: str # 提交标题 (50 字符内) + commit_body: str # 提交正文 + + # 代码分析 + issues_found: list[str] # 发现的潜在问题 + suggestions: list[str] # 优化建议 + affected_files: list[str] # 影响的文件列表 + + # 风险评估 + risk_level: str # 风险等级: "low", "medium", "high" + breaking_changes: bool # 是否包含破坏性变更 + + # 额外信息 + confidence_score: float # AI 分析置信度 (0-1) + raw_response: dict # 原始 AI 响应 + + +class AIAnalyzer: + """AI 代码分析器主类""" + + def __init__( + self, + provider: AIProvider = AIProvider.OPENAI, + api_key: str | None = None, + model: str | None = None, + base_url: str | None = None, + ): + """ + 初始化 AI 分析器 + + Args: + provider: AI 提供商 + api_key: API 密钥(从环境变量或参数获取) + model: 模型名称(可选,使用默认模型) + base_url: API 基础 URL(用于自定义端点) + """ + self.provider = provider + self.api_key = api_key or self._get_api_key_from_env() + self.model = model or self._get_default_model() + self.base_url = base_url or self._get_default_base_url() + + # 验证配置 + if not self.api_key and provider != AIProvider.OLLAMA: + raise ValueError( + f"API key required for {provider.value}. " + f"Set MCP_AI_API_KEY environment variable." + ) + + def _get_api_key_from_env(self) -> str | None: + """从环境变量获取 API 密钥""" + key_mapping = { + AIProvider.OPENAI: "OPENAI_API_KEY", + AIProvider.ANTHROPIC: "ANTHROPIC_API_KEY", + AIProvider.OLLAMA: None, # 本地 LLM 不需要密钥 + } + env_var = key_mapping.get(self.provider) + if env_var: + # 优先使用 MCP 专用环境变量 + return os.getenv("MCP_AI_API_KEY") or os.getenv(env_var) + return None + + def _get_default_model(self) -> str: + """获取默认模型""" + model_mapping = { + AIProvider.OPENAI: "gpt-4-turbo-preview", + AIProvider.ANTHROPIC: "claude-3-5-sonnet-20241022", + AIProvider.OLLAMA: "qwen2.5-coder:latest", + } + # 允许环境变量覆盖 + env_model = os.getenv("MCP_AI_MODEL") + return env_model or model_mapping.get(self.provider, "gpt-4") + + def _get_default_base_url(self) -> str: + """获取默认 API 基础 URL""" + url_mapping = { + AIProvider.OPENAI: "https://api.openai.com/v1", + AIProvider.ANTHROPIC: "https://api.anthropic.com/v1", + AIProvider.OLLAMA: "http://localhost:11434", # Ollama 默认端口 + } + # 允许环境变量覆盖 + env_url = os.getenv("MCP_AI_BASE_URL") + return env_url or url_mapping.get(self.provider, "") + + async def analyze_git_diff( + self, git_diff: str, git_status: str, project_context: str = "" + ) -> AIAnalysisResult: + """ + 分析 Git 代码变更 + + Args: + git_diff: git diff 输出 + git_status: git status 输出 + project_context: 项目上下文信息(可选) + + Returns: + AIAnalysisResult: AI 分析结果 + """ + # 构建分析提示词 + prompt = self._build_analysis_prompt(git_diff, git_status, project_context) + + # 调用 AI API + response = await self._call_ai_api(prompt) + + # 解析响应 + result = self._parse_ai_response(response) + + return result + + def _build_analysis_prompt( + self, git_diff: str, git_status: str, project_context: str + ) -> str: + """构建 AI 分析提示词""" + return f"""你是一个专业的代码审查助手。请分析以下 Git 代码变更,并提供详细的分析报告。 + +# 项目上下文 +{project_context if project_context else "无额外上下文信息"} + +# Git Status +``` +{git_status} +``` + +# Git Diff +```diff +{git_diff[:5000]} # 限制 diff 长度避免超出 token 限制 +``` + +请以 JSON 格式返回分析结果,包含以下字段: + +{{ + "change_type": "feat|fix|refactor|docs|style|test|chore|perf", + "severity": "high|medium|low", + "summary": "一句话总结这次变更", + "commit_title": "符合 Conventional Commits 规范的提交标题(50字符内)", + "commit_body": "详细的提交说明(多行,解释为什么做这个变更)", + "issues_found": ["潜在问题1", "潜在问题2"], + "suggestions": ["优化建议1", "优化建议2"], + "affected_files": ["文件路径1", "文件路径2"], + "risk_level": "low|medium|high", + "breaking_changes": false, + "confidence_score": 0.95 +}} + +注意事项: +1. commit_title 必须符合格式: (): +2. 检查是否有明显的 bug、安全问题、性能问题 +3. 评估变更的风险等级 +4. 提供具体、可操作的优化建议 +5. 所有文本使用中文 +""" + + async def _call_ai_api(self, prompt: str) -> dict[str, Any]: + """ + 调用 AI API + + Args: + prompt: 分析提示词 + + Returns: + dict: AI API 响应 + """ + if self.provider == AIProvider.OPENAI: + return await self._call_openai_api(prompt) + elif self.provider == AIProvider.ANTHROPIC: + return await self._call_anthropic_api(prompt) + elif self.provider == AIProvider.OLLAMA: + return await self._call_ollama_api(prompt) + else: + raise ValueError(f"Unsupported AI provider: {self.provider}") + + async def _call_openai_api(self, prompt: str) -> dict[str, Any]: + """调用 OpenAI API""" + url = f"{self.base_url}/chat/completions" + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + } + payload = { + "model": self.model, + "messages": [ + { + "role": "system", + "content": "你是一个专业的代码审查助手,擅长分析代码变更并提供建设性建议。", + }, + {"role": "user", "content": prompt}, + ], + "temperature": 0.3, # 降低随机性,提高稳定性 + "response_format": {"type": "json_object"}, + } + + async with aiohttp.ClientSession() as session: + async with session.post( + url, headers=headers, json=payload, timeout=aiohttp.ClientTimeout(total=60) + ) as response: + response.raise_for_status() + data = await response.json() + return data + + async def _call_anthropic_api(self, prompt: str) -> dict[str, Any]: + """调用 Anthropic Claude API""" + url = f"{self.base_url}/messages" + headers = { + "x-api-key": self.api_key, + "anthropic-version": "2023-06-01", + "Content-Type": "application/json", + } + payload = { + "model": self.model, + "max_tokens": 4096, + "messages": [{"role": "user", "content": prompt}], + "temperature": 0.3, + } + + async with aiohttp.ClientSession() as session: + async with session.post( + url, headers=headers, json=payload, timeout=aiohttp.ClientTimeout(total=60) + ) as response: + response.raise_for_status() + data = await response.json() + return data + + async def _call_ollama_api(self, prompt: str) -> dict[str, Any]: + """调用本地 Ollama API""" + url = f"{self.base_url}/api/generate" + payload = { + "model": self.model, + "prompt": prompt, + "stream": False, + "format": "json", + } + + async with aiohttp.ClientSession() as session: + async with session.post( + url, json=payload, timeout=aiohttp.ClientTimeout(total=120) + ) as response: + response.raise_for_status() + data = await response.json() + return data + + def _parse_ai_response(self, response: dict[str, Any]) -> AIAnalysisResult: + """ + 解析 AI API 响应 + + Args: + response: AI API 原始响应 + + Returns: + AIAnalysisResult: 解析后的分析结果 + """ + try: + # 提取内容文本 + if self.provider == AIProvider.OPENAI: + content = response["choices"][0]["message"]["content"] + elif self.provider == AIProvider.ANTHROPIC: + content = response["content"][0]["text"] + elif self.provider == AIProvider.OLLAMA: + content = response["response"] + else: + raise ValueError(f"Unsupported provider: {self.provider}") + + # 解析 JSON 内容 + data = json.loads(content) + + # 构建结果对象 + return AIAnalysisResult( + change_type=ChangeType(data.get("change_type", "chore")), + severity=ChangeSeverity(data.get("severity", "low")), + summary=data.get("summary", "代码变更"), + commit_title=data.get("commit_title", "chore: 更新代码"), + commit_body=data.get("commit_body", ""), + issues_found=data.get("issues_found", []), + suggestions=data.get("suggestions", []), + affected_files=data.get("affected_files", []), + risk_level=data.get("risk_level", "low"), + breaking_changes=data.get("breaking_changes", False), + confidence_score=data.get("confidence_score", 0.8), + raw_response=response, + ) + + except (json.JSONDecodeError, KeyError) as e: + # 解析失败时返回默认结果 + return AIAnalysisResult( + change_type=ChangeType.CHORE, + severity=ChangeSeverity.LOW, + summary="AI 分析失败,请手动审查", + commit_title="chore: 代码更新", + commit_body=f"AI 分析遇到错误: {e!s}\n\n请手动审查代码变更。", + issues_found=[f"AI 解析错误: {e!s}"], + suggestions=["建议手动检查代码变更"], + affected_files=[], + risk_level="medium", + breaking_changes=False, + confidence_score=0.0, + raw_response=response, + ) + + +# ===== 快速测试功能 ===== +async def test_analyzer(): + """测试 AI 分析器""" + # 示例 Git Diff + sample_diff = """diff --git a/src/server.py b/src/server.py +index 1234567..abcdefg 100644 +--- a/src/server.py ++++ b/src/server.py +@@ -10,7 +10,7 @@ def process_request(data): +- return {"status": "ok"} ++ return {"status": "success", "data": data} +""" + + sample_status = """On branch main +Changes to be committed: + modified: src/server.py +""" + + # 创建分析器(使用 Ollama 本地测试) + analyzer = AIAnalyzer(provider=AIProvider.OLLAMA) + + # 执行分析 + result = await analyzer.analyze_git_diff( + git_diff=sample_diff, + git_status=sample_status, + project_context="这是一个 Python Web 服务器项目", + ) + + print("=" * 60) + print("AI 分析结果:") + print("=" * 60) + print(f"变更类型: {result.change_type.value}") + print(f"严重性: {result.severity.value}") + print(f"摘要: {result.summary}") + print(f"\nCommit 标题: {result.commit_title}") + print(f"Commit 正文:\n{result.commit_body}") + print(f"\n发现的问题: {result.issues_found}") + print(f"优化建议: {result.suggestions}") + print(f"风险等级: {result.risk_level}") + print(f"置信度: {result.confidence_score:.2%}") + + +if __name__ == "__main__": + asyncio.run(test_analyzer()) diff --git a/src/mcp_feedback_enhanced/web/routes/ai_routes.py b/src/mcp_feedback_enhanced/web/routes/ai_routes.py new file mode 100644 index 0000000..f66a734 --- /dev/null +++ b/src/mcp_feedback_enhanced/web/routes/ai_routes.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +""" +AI 分析 API 路由 +=============== + +提供 AI 代码分析相关的 API 端点。 +""" + +import asyncio +import os +import subprocess +from typing import Any + +from fastapi import APIRouter, HTTPException, Request +from pydantic import BaseModel + +from ...ai import AIAnalyzer, AIProvider +from ...debug import web_debug_log as debug_log + + +# 创建路由器 +router = APIRouter() + + +class AnalyzeCodeRequest(BaseModel): + """代码分析请求模型""" + + project_dir: str + context: str = "" + + +class AnalyzeCodeResponse(BaseModel): + """代码分析响应模型""" + + success: bool + analysis: dict[str, Any] | None = None + error: str | None = None + + +def get_git_diff(project_dir: str) -> tuple[str, str]: + """ + 获取 Git 状态和差异 + + Args: + project_dir: 项目目录 + + Returns: + tuple: (git_status, git_diff) + """ + try: + # 切换到项目目录 + original_dir = os.getcwd() + os.chdir(project_dir) + + # 获取 git status + status_result = subprocess.run( + ["git", "status", "--short"], + capture_output=True, + text=True, + check=True, + timeout=10, + ) + git_status = status_result.stdout + + # 获取 staged 和 unstaged 的 diff + diff_staged = subprocess.run( + ["git", "diff", "--cached"], + capture_output=True, + text=True, + check=True, + timeout=10, + ) + + diff_unstaged = subprocess.run( + ["git", "diff"], capture_output=True, text=True, check=True, timeout=10 + ) + + # 合并 diff + git_diff = "" + if diff_staged.stdout: + git_diff += "=== Staged Changes ===\n" + diff_staged.stdout + "\n" + if diff_unstaged.stdout: + git_diff += "=== Unstaged Changes ===\n" + diff_unstaged.stdout + + # 恢复原目录 + os.chdir(original_dir) + + return git_status, git_diff + + except subprocess.CalledProcessError as e: + debug_log(f"Git 命令执行失败: {e}") + raise HTTPException(status_code=500, detail=f"Git 命令失败: {e}") + except subprocess.TimeoutExpired: + debug_log("Git 命令超时") + raise HTTPException(status_code=500, detail="Git 命令执行超时") + finally: + # 确保恢复原目录 + try: + os.chdir(original_dir) + except Exception: + pass + + +@router.post("/api/ai/analyze-code", response_model=AnalyzeCodeResponse) +async def analyze_code(request: AnalyzeCodeRequest) -> AnalyzeCodeResponse: + """ + 分析代码变更 + + 使用 AI 分析当前的 Git 代码变更,生成智能建议。 + """ + try: + debug_log(f"收到 AI 分析请求,项目目录: {request.project_dir}") + + # 检查是否启用 AI 功能 + ai_enabled = os.getenv("MCP_AI_ENABLED", "false").lower() in ( + "true", + "1", + "yes", + ) + if not ai_enabled: + return AnalyzeCodeResponse( + success=False, error="AI 功能未启用。请设置 MCP_AI_ENABLED=true 环境变量。" + ) + + # 获取 Git 差异 + git_status, git_diff = get_git_diff(request.project_dir) + + if not git_diff.strip(): + return AnalyzeCodeResponse( + success=False, error="没有检测到代码变更。请先修改文件后再分析。" + ) + + # 确定 AI 提供商 + provider_name = os.getenv("MCP_AI_PROVIDER", "ollama").lower() + provider_map = { + "openai": AIProvider.OPENAI, + "anthropic": AIProvider.ANTHROPIC, + "claude": AIProvider.ANTHROPIC, + "ollama": AIProvider.OLLAMA, + } + provider = provider_map.get(provider_name, AIProvider.OLLAMA) + + debug_log(f"使用 AI 提供商: {provider.value}") + + # 创建 AI 分析器 + analyzer = AIAnalyzer(provider=provider) + + # 执行分析 + result = await analyzer.analyze_git_diff( + git_diff=git_diff, git_status=git_status, project_context=request.context + ) + + # 转换结果为字典 + analysis_data = { + "change_type": result.change_type.value, + "severity": result.severity.value, + "summary": result.summary, + "commit_title": result.commit_title, + "commit_body": result.commit_body, + "issues_found": result.issues_found, + "suggestions": result.suggestions, + "affected_files": result.affected_files, + "risk_level": result.risk_level, + "breaking_changes": result.breaking_changes, + "confidence_score": result.confidence_score, + } + + debug_log(f"AI 分析完成: {result.summary}") + + return AnalyzeCodeResponse(success=True, analysis=analysis_data) + + except HTTPException: + raise + except Exception as e: + debug_log(f"AI 分析失败: {e}") + return AnalyzeCodeResponse(success=False, error=f"分析失败: {e!s}") + + +@router.get("/api/ai/status") +async def get_ai_status() -> dict[str, Any]: + """ + 获取 AI 功能状态 + + 返回当前 AI 配置和可用性信息。 + """ + ai_enabled = os.getenv("MCP_AI_ENABLED", "false").lower() in ("true", "1", "yes") + provider = os.getenv("MCP_AI_PROVIDER", "ollama") + model = os.getenv("MCP_AI_MODEL", "") + + # 检查 API 密钥是否配置 + has_api_key = False + if provider == "openai": + has_api_key = bool(os.getenv("OPENAI_API_KEY") or os.getenv("MCP_AI_API_KEY")) + elif provider in ("anthropic", "claude"): + has_api_key = bool( + os.getenv("ANTHROPIC_API_KEY") or os.getenv("MCP_AI_API_KEY") + ) + elif provider == "ollama": + has_api_key = True # Ollama 不需要 API 密钥 + + return { + "enabled": ai_enabled, + "provider": provider, + "model": model or "默认模型", + "configured": has_api_key, + "status": "ready" if (ai_enabled and has_api_key) else "not_configured", + } diff --git a/src/mcp_feedback_enhanced/web/static/css/ai-analyzer.css b/src/mcp_feedback_enhanced/web/static/css/ai-analyzer.css new file mode 100644 index 0000000..491864c --- /dev/null +++ b/src/mcp_feedback_enhanced/web/static/css/ai-analyzer.css @@ -0,0 +1,354 @@ +/** + * MCP Feedback Enhanced - AI 分析器样式 + * ====================================== + */ + +/* AI 分析按钮 */ +.btn-ai-analyze { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); +} + +.btn-ai-analyze:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); +} + +.btn-ai-analyze:active { + transform: translateY(0); +} + +.btn-ai-analyze:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + +.btn-ai-analyze .ai-icon { + font-size: 16px; + animation: ai-pulse 2s ease-in-out infinite; +} + +@keyframes ai-pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +/* AI 分析结果容器 */ +.ai-analysis-result { + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + border-radius: 12px; + padding: 20px; + margin: 16px 0; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + animation: fadeInUp 0.5s ease; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* AI 分析头部 */ +.ai-analysis-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding-bottom: 16px; + border-bottom: 2px solid rgba(255, 255, 255, 0.5); +} + +.ai-analysis-header h3 { + margin: 0; + font-size: 20px; + font-weight: 600; + color: #2c3e50; +} + +/* 徽章容器 */ +.ai-badges { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.badge { + display: inline-block; + padding: 4px 12px; + border-radius: 20px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.badge-type { + background-color: #3498db; + color: white; +} + +.badge-severity-high { + background-color: #e74c3c; + color: white; +} + +.badge-severity-medium { + background-color: #f39c12; + color: white; +} + +.badge-severity-low { + background-color: #2ecc71; + color: white; +} + +.badge-risk-high { + background-color: #c0392b; + color: white; +} + +.badge-risk-medium { + background-color: #e67e22; + color: white; +} + +.badge-risk-low { + background-color: #27ae60; + color: white; +} + +.badge-breaking { + background-color: #e74c3c; + color: white; + animation: badge-warning 1.5s ease-in-out infinite; +} + +@keyframes badge-warning { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +/* AI 分析区块 */ +.ai-section { + background: white; + border-radius: 8px; + padding: 16px; + margin-bottom: 12px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.ai-section h4 { + margin: 0 0 12px 0; + font-size: 16px; + font-weight: 600; + color: #34495e; + display: flex; + align-items: center; + gap: 6px; +} + +.ai-section p { + margin: 0; + line-height: 1.6; + color: #555; +} + +.ai-section ul { + margin: 8px 0; + padding-left: 24px; +} + +.ai-section li { + margin: 6px 0; + line-height: 1.5; + color: #555; +} + +/* Commit 建议区域 */ +.ai-commit-suggestion { + background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); + border-left: 4px solid #667eea; +} + +.commit-title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; + padding: 12px; + background: white; + border-radius: 6px; +} + +.commit-title code { + flex: 1; + padding: 8px 12px; + background: #f8f9fa; + border-radius: 4px; + font-family: 'Courier New', monospace; + font-size: 13px; + color: #2c3e50; + word-break: break-word; +} + +.btn-copy-commit { + padding: 6px 12px; + background: #667eea; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + transition: all 0.2s ease; + margin-left: 8px; +} + +.btn-copy-commit:hover { + background: #5a67d8; + transform: scale(1.05); +} + +.commit-body { + margin-top: 12px; +} + +.commit-body pre { + margin: 8px 0 0 0; + padding: 12px; + background: white; + border-radius: 6px; + font-family: 'Courier New', monospace; + font-size: 13px; + line-height: 1.6; + color: #2c3e50; + overflow-x: auto; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* 问题区域 */ +.ai-issues { + border-left: 4px solid #e74c3c; +} + +.ai-issues li { + color: #c0392b; +} + +/* 建议区域 */ +.ai-suggestions { + border-left: 4px solid #2ecc71; +} + +.ai-suggestions li { + color: #27ae60; +} + +/* AI 底部 */ +.ai-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 16px; + padding-top: 12px; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +.ai-confidence { + font-size: 13px; + color: #7f8c8d; + font-weight: 500; +} + +/* 加载状态 */ +.ai-analyzing { + text-align: center; + padding: 40px 20px; +} + +.ai-analyzing .spinner { + display: inline-block; + width: 40px; + height: 40px; + border: 4px solid rgba(102, 126, 234, 0.2); + border-top-color: #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.ai-analyzing p { + margin-top: 16px; + color: #7f8c8d; + font-size: 14px; +} + +/* 错误状态 */ +.ai-error { + background: #fee; + border-left: 4px solid #e74c3c; + padding: 16px; + border-radius: 8px; + margin: 16px 0; +} + +.ai-error h4 { + margin: 0 0 8px 0; + color: #c0392b; +} + +.ai-error p { + margin: 0; + color: #555; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .ai-analysis-header { + flex-direction: column; + align-items: flex-start; + gap: 12px; + } + + .commit-title { + flex-direction: column; + align-items: stretch; + } + + .btn-copy-commit { + margin-left: 0; + margin-top: 8px; + width: 100%; + } +} diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/ai/ai-analyzer.js b/src/mcp_feedback_enhanced/web/static/js/modules/ai/ai-analyzer.js new file mode 100644 index 0000000..1e087f4 --- /dev/null +++ b/src/mcp_feedback_enhanced/web/static/js/modules/ai/ai-analyzer.js @@ -0,0 +1,270 @@ +/** + * MCP Feedback Enhanced - AI 分析模块 + * =================================== + * + * 提供 AI 驱动的代码分析功能,集成到自动提交工作流中。 + */ + +(function() { + 'use strict'; + + // 确保命名空间存在 + window.MCPFeedback = window.MCPFeedback || {}; + window.MCPFeedback.AI = window.MCPFeedback.AI || {}; + + // 创建模块专用日志器 + const logger = window.MCPFeedback.Logger ? + new window.MCPFeedback.Logger({ moduleName: 'AIAnalyzer' }) : + console; + + /** + * AI 分析器类 + */ + function AIAnalyzer(options) { + options = options || {}; + + // 配置 + this.apiEndpoint = options.apiEndpoint || '/api/ai/analyze-code'; + this.statusEndpoint = options.statusEndpoint || '/api/ai/status'; + + // 状态 + this.isAnalyzing = false; + this.lastAnalysis = null; + + // 回调 + this.onAnalysisStart = options.onAnalysisStart || null; + this.onAnalysisComplete = options.onAnalysisComplete || null; + this.onAnalysisError = options.onAnalysisError || null; + + logger.info('AIAnalyzer 初始化完成'); + } + + /** + * 检查 AI 功能是否可用 + */ + AIAnalyzer.prototype.checkStatus = function() { + const self = this; + + return fetch(this.statusEndpoint) + .then(function(response) { + if (!response.ok) { + throw new Error('Failed to check AI status'); + } + return response.json(); + }) + .then(function(data) { + logger.info('AI 状态:', data); + return data; + }) + .catch(function(error) { + logger.error('检查 AI 状态失败:', error); + return { + enabled: false, + status: 'error', + error: error.message + }; + }); + }; + + /** + * 分析代码变更 + */ + AIAnalyzer.prototype.analyzeCode = function(projectDir, context) { + const self = this; + + if (this.isAnalyzing) { + logger.warn('AI 分析正在进行中,请稍候'); + return Promise.reject(new Error('分析正在进行中')); + } + + this.isAnalyzing = true; + + // 触发开始回调 + if (this.onAnalysisStart) { + this.onAnalysisStart(); + } + + logger.info('开始 AI 代码分析...', { projectDir, context }); + + return fetch(this.apiEndpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + project_dir: projectDir, + context: context || '' + }) + }) + .then(function(response) { + if (!response.ok) { + throw new Error('AI 分析请求失败: ' + response.status); + } + return response.json(); + }) + .then(function(data) { + self.isAnalyzing = false; + + if (!data.success) { + throw new Error(data.error || '分析失败'); + } + + // 保存分析结果 + self.lastAnalysis = data.analysis; + + logger.info('AI 分析完成:', data.analysis); + + // 触发完成回调 + if (self.onAnalysisComplete) { + self.onAnalysisComplete(data.analysis); + } + + return data.analysis; + }) + .catch(function(error) { + self.isAnalyzing = false; + + logger.error('AI 分析失败:', error); + + // 触发错误回调 + if (self.onAnalysisError) { + self.onAnalysisError(error); + } + + throw error; + }); + }; + + /** + * 获取上次分析结果 + */ + AIAnalyzer.prototype.getLastAnalysis = function() { + return this.lastAnalysis; + }; + + /** + * 清除分析结果 + */ + AIAnalyzer.prototype.clearAnalysis = function() { + this.lastAnalysis = null; + }; + + /** + * 格式化分析结果为 HTML + */ + AIAnalyzer.prototype.formatAnalysisHTML = function(analysis) { + if (!analysis) { + return '

暂无分析结果

'; + } + + const severityBadge = this._getSeverityBadge(analysis.severity); + const riskBadge = this._getRiskBadge(analysis.risk_level); + + let html = '
'; + + // 标题和标签 + html += '
'; + html += '

🤖 AI 代码分析报告

'; + html += '
'; + html += '' + analysis.change_type + ''; + html += severityBadge; + html += riskBadge; + if (analysis.breaking_changes) { + html += '⚠️ 破坏性变更'; + } + html += '
'; + html += '
'; + + // 摘要 + html += '
'; + html += '

📋 变更摘要

'; + html += '

' + this._escapeHtml(analysis.summary) + '

'; + html += '
'; + + // Commit Message 建议 + html += '
'; + html += '

💬 Commit Message 建议

'; + html += '
'; + html += '标题: ' + this._escapeHtml(analysis.commit_title) + ''; + html += ''; + html += '
'; + if (analysis.commit_body) { + html += '
'; + html += '正文:
'; + html += '
' + this._escapeHtml(analysis.commit_body) + '
'; + html += '
'; + } + html += '
'; + + // 问题和建议 + if (analysis.issues_found && analysis.issues_found.length > 0) { + html += '
'; + html += '

⚠️ 发现的问题

'; + html += '
    '; + analysis.issues_found.forEach(function(issue) { + html += '
  • ' + self._escapeHtml(issue) + '
  • '; + }); + html += '
'; + html += '
'; + } + + if (analysis.suggestions && analysis.suggestions.length > 0) { + html += '
'; + html += '

💡 优化建议

'; + html += '
    '; + analysis.suggestions.forEach(function(suggestion) { + html += '
  • ' + self._escapeHtml(suggestion) + '
  • '; + }); + html += '
'; + html += '
'; + } + + // 置信度 + html += ''; + + html += '
'; + + return html; + }; + + /** + * 获取严重性徽章 + */ + AIAnalyzer.prototype._getSeverityBadge = function(severity) { + const badges = { + 'high': '🔴 高', + 'medium': '🟡 中', + 'low': '🟢 低' + }; + return badges[severity] || badges['low']; + }; + + /** + * 获取风险徽章 + */ + AIAnalyzer.prototype._getRiskBadge = function(risk) { + const badges = { + 'high': '⚠️ 高风险', + 'medium': '⚡ 中风险', + 'low': '✅ 低风险' + }; + return badges[risk] || badges['low']; + }; + + /** + * HTML 转义 + */ + AIAnalyzer.prototype._escapeHtml = function(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + }; + + // 导出到全局命名空间 + window.MCPFeedback.AI.AIAnalyzer = AIAnalyzer; + + logger.info('✅ AI Analyzer 模块加载完成'); + +})();