Initial commit with all project files
This commit is contained in:
commit
637c71ba40
4
.env
Normal file
4
.env
Normal file
@ -0,0 +1,4 @@
|
||||
SILICONFLOW_API_KEY=sk-owfxudwvbipsofngckympnhtbcknwgqmoprgkfhbafeahmkv
|
||||
SILICONFLOW_MODEL=deepseek-ai/DeepSeek-V2.5
|
||||
APP_HOST=0.0.0.0
|
||||
APP_PORT=8000
|
||||
83
README.md
Normal file
83
README.md
Normal file
@ -0,0 +1,83 @@
|
||||
# 代码解释与修复助手 - AI编程学习工具
|
||||
|
||||
一个智能代码解释与修复工具,帮助编程学习者理解代码并提供修复建议。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- **代码解释**: 对代码进行逐行或分段的详细解释
|
||||
- **Bug修复**: 识别代码问题并提供修复建议
|
||||
- **多语言支持**: 支持 Python、JavaScript、Java、C++、Go、Rust 等多种编程语言
|
||||
- **图形界面**: 简洁美观的界面,参考豆包AI问答方式
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
# 安装核心依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 安装图形界面依赖
|
||||
pip install -r requirements_gui.txt
|
||||
```
|
||||
|
||||
### 运行应用
|
||||
|
||||
#### 图形界面版本(推荐)
|
||||
```bash
|
||||
python gui_app.py
|
||||
```
|
||||
|
||||
#### Web API 版本
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
访问 http://localhost:8000 查看 API 文档
|
||||
|
||||
## 图形界面使用说明
|
||||
|
||||
1. 启动 `gui_app.py` 后,会弹出一个窗口
|
||||
2. 在代码输入框中粘贴或输入您的代码
|
||||
3. 选择任务类型:
|
||||
- 📖 代码解释:获取代码的详细解释
|
||||
- 🔧 代码修复:分析并修复代码问题
|
||||
4. 选择编程语言(可选择 auto 自动检测)
|
||||
5. 按 **Enter** 或 **Ctrl+Enter** 发送请求
|
||||
6. 等待 AI 分析,结果会显示在下方
|
||||
|
||||
## 配置说明
|
||||
|
||||
在项目根目录创建 `.env` 文件:
|
||||
|
||||
```
|
||||
# 硅基流动 API Key(必需)
|
||||
SILICONFLOW_API_KEY=your_api_key_here
|
||||
|
||||
# 可选配置
|
||||
SILICONFLOW_MODEL=deepseek-ai/DeepSeek-V2.5
|
||||
```
|
||||
|
||||
获取 API Key:访问 https://cloud.siliconflow.cn 注册账号
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
code-explainer-fixer/
|
||||
├── gui_app.py # 图形界面版本(推荐使用)
|
||||
├── app.py # Web API 版本
|
||||
├── config.py # 配置文件
|
||||
├── requirements.txt # 核心依赖
|
||||
├── requirements_gui.txt # GUI 依赖
|
||||
├── .env # API 配置(需手动创建)
|
||||
├── agents/ # 智能体模块
|
||||
├── services/ # 服务层
|
||||
├── models/ # 数据模型
|
||||
└── utils/ # 工具函数
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **图形界面**: CustomTkinter(现代化Tkinter)
|
||||
- **AI 服务**: 硅基流动(OpenAI 兼容 API)
|
||||
- **Web 框架**: FastAPI + Uvicorn
|
||||
BIN
__pycache__/app.cpython-313.pyc
Normal file
BIN
__pycache__/app.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/config.cpython-313.pyc
Normal file
BIN
__pycache__/config.cpython-313.pyc
Normal file
Binary file not shown.
4
agents/__init__.py
Normal file
4
agents/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .code_explainer import code_explainer
|
||||
from .bug_fixer import bug_fixer
|
||||
|
||||
__all__ = ["code_explainer", "bug_fixer"]
|
||||
BIN
agents/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
agents/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
agents/__pycache__/bug_fixer.cpython-313.pyc
Normal file
BIN
agents/__pycache__/bug_fixer.cpython-313.pyc
Normal file
Binary file not shown.
BIN
agents/__pycache__/code_explainer.cpython-313.pyc
Normal file
BIN
agents/__pycache__/code_explainer.cpython-313.pyc
Normal file
Binary file not shown.
64
agents/bug_fixer.py
Normal file
64
agents/bug_fixer.py
Normal file
@ -0,0 +1,64 @@
|
||||
from services.ai_service import ai_service
|
||||
from models.schemas import BugFix
|
||||
from typing import Optional
|
||||
import re
|
||||
|
||||
class BugFixer:
|
||||
"""Bug修复智能体"""
|
||||
|
||||
def __init__(self):
|
||||
self.service = ai_service
|
||||
|
||||
def fix(self, code: str, language: str, error_description: Optional[str] = None) -> BugFix:
|
||||
"""修复代码问题"""
|
||||
result = self.service.generate_fix(code, language, error_description)
|
||||
|
||||
fixed_code = result.get("fixed_code", "")
|
||||
problems = self._extract_problems(result.get("analysis", ""))
|
||||
fixes = self._extract_fixes(result.get("analysis", ""))
|
||||
|
||||
return BugFix(
|
||||
original_code=code,
|
||||
fixed_code=fixed_code or code,
|
||||
problems_found=problems,
|
||||
fixes_applied=fixes,
|
||||
explanation=result.get("analysis", "")
|
||||
)
|
||||
|
||||
def _extract_problems(self, analysis: str) -> list:
|
||||
"""提取发现的问题"""
|
||||
problems = []
|
||||
lines = analysis.split('\n')
|
||||
|
||||
in_problems_section = False
|
||||
for line in lines:
|
||||
if any(keyword in line.lower() for keyword in ['问题', 'problem', '错误', 'issue', 'bug']):
|
||||
if not in_problems_section:
|
||||
in_problems_section = True
|
||||
continue
|
||||
|
||||
if in_problems_section and line.strip():
|
||||
if any(keyword in line.lower() for keyword in ['修复', 'fix', '建议', '建议', 'solution']):
|
||||
break
|
||||
problems.append(line.strip())
|
||||
|
||||
return problems if problems else ["代码可能存在问题"]
|
||||
|
||||
def _extract_fixes(self, analysis: str) -> list:
|
||||
"""提取修复内容"""
|
||||
fixes = []
|
||||
lines = analysis.split('\n')
|
||||
|
||||
in_fixes_section = False
|
||||
for line in lines:
|
||||
if any(keyword in line.lower() for keyword in ['修复', 'fix', 'solution', '修改']):
|
||||
in_fixes_section = True
|
||||
continue
|
||||
|
||||
if in_fixes_section and line.strip():
|
||||
if line.strip().startswith(('1.', '2.', '3.', '•', '-', '*')):
|
||||
fixes.append(line.strip())
|
||||
|
||||
return fixes if fixes else None
|
||||
|
||||
bug_fixer = BugFixer()
|
||||
64
agents/code_explainer.py
Normal file
64
agents/code_explainer.py
Normal file
@ -0,0 +1,64 @@
|
||||
from services.ai_service import ai_service
|
||||
from models.schemas import CodeExplanation
|
||||
from typing import List, Optional
|
||||
|
||||
class CodeExplainer:
|
||||
"""代码解释智能体"""
|
||||
|
||||
def __init__(self):
|
||||
self.service = ai_service
|
||||
|
||||
def explain(self, code: str, language: str, depth: str = "detailed") -> CodeExplanation:
|
||||
"""解释代码"""
|
||||
explanation_text = self.service.generate_explanation(code, language, depth)
|
||||
|
||||
return CodeExplanation(
|
||||
explanation=explanation_text,
|
||||
line_by_line=self._parse_line_explanations(explanation_text),
|
||||
key_concepts=self._extract_concepts(explanation_text),
|
||||
suggestions=self._extract_suggestions(explanation_text)
|
||||
)
|
||||
|
||||
def _parse_line_explanations(self, explanation: str) -> List[dict]:
|
||||
"""解析逐行解释"""
|
||||
lines = explanation.split('\n')
|
||||
parsed = []
|
||||
current_item = {}
|
||||
|
||||
for line in lines:
|
||||
if line.strip().startswith(('第', 'Line', '行')):
|
||||
if current_item:
|
||||
parsed.append(current_item)
|
||||
current_item = {"line": line}
|
||||
elif current_item:
|
||||
current_item["explanation"] = current_item.get("explanation", "") + line + "\n"
|
||||
|
||||
if current_item:
|
||||
parsed.append(current_item)
|
||||
|
||||
return parsed if parsed else None
|
||||
|
||||
def _extract_concepts(self, explanation: str) -> List[str]:
|
||||
"""提取关键概念"""
|
||||
import re
|
||||
patterns = [
|
||||
r'关键概念[::]\s*(.+?)(?:\n|$)',
|
||||
r'概念[::]\s*(.+?)(?:\n|$)',
|
||||
r'(?:concept|term|关键字)[s]*[::]\s*(.+?)(?:\n|$)'
|
||||
]
|
||||
|
||||
concepts = []
|
||||
for pattern in patterns:
|
||||
matches = re.findall(pattern, explanation, re.IGNORECASE)
|
||||
concepts.extend(matches)
|
||||
|
||||
return concepts if concepts else None
|
||||
|
||||
def _extract_suggestions(self, explanation: str) -> List[str]:
|
||||
"""提取建议"""
|
||||
import re
|
||||
pattern = r'(?:建议|tips?|提示|最佳实践)[::]\s*(.+?)(?:\n|$)'
|
||||
matches = re.findall(pattern, explanation, re.IGNORECASE)
|
||||
return matches if matches else None
|
||||
|
||||
code_explainer = CodeExplainer()
|
||||
135
app.py
Normal file
135
app.py
Normal file
@ -0,0 +1,135 @@
|
||||
"""
|
||||
Code Explainer & Fixer - AI编程学习助手主应用
|
||||
"""
|
||||
import uvicorn
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import HTMLResponse
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from config import config
|
||||
from models.schemas import CodeRequest, CodeResponse
|
||||
from agents import code_explainer, bug_fixer
|
||||
from utils import detect_language
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
yield
|
||||
|
||||
app = FastAPI(
|
||||
title="代码解释与修复助手",
|
||||
description="帮助编程学习者理解代码并提供修复建议的AI工具",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# 配置静态文件服务
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def root():
|
||||
return open("static/index.html", "r", encoding="utf-8").read()
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "healthy", "service": "code-explainer-fixer"}
|
||||
|
||||
@app.post("/explain", response_model=CodeResponse)
|
||||
async def explain_code(request: CodeRequest):
|
||||
"""解释代码功能"""
|
||||
try:
|
||||
if request.language not in config.SUPPORTED_LANGUAGES:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的语言: {request.language}")
|
||||
|
||||
explanation = code_explainer.explain(
|
||||
code=request.code,
|
||||
language=request.language,
|
||||
depth=request.depth
|
||||
)
|
||||
|
||||
return CodeResponse(
|
||||
success=True,
|
||||
task_type="explain",
|
||||
result=explanation
|
||||
)
|
||||
except Exception as e:
|
||||
return CodeResponse(
|
||||
success=False,
|
||||
task_type="explain",
|
||||
result=None,
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
@app.post("/fix", response_model=CodeResponse)
|
||||
async def fix_code(request: CodeRequest):
|
||||
"""修复代码问题"""
|
||||
try:
|
||||
if request.language not in config.SUPPORTED_LANGUAGES:
|
||||
raise HTTPException(status_code=400, detail=f"不支持的语言: {request.language}")
|
||||
|
||||
fix_result = bug_fixer.fix(
|
||||
code=request.code,
|
||||
language=request.language
|
||||
)
|
||||
|
||||
return CodeResponse(
|
||||
success=True,
|
||||
task_type="fix",
|
||||
result=fix_result
|
||||
)
|
||||
except Exception as e:
|
||||
return CodeResponse(
|
||||
success=False,
|
||||
task_type="fix",
|
||||
result=None,
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
@app.post("/auto", response_model=CodeResponse)
|
||||
async def auto_process(request: CodeRequest):
|
||||
"""自动检测并处理代码"""
|
||||
try:
|
||||
language = request.language
|
||||
if language == "auto":
|
||||
language = detect_language(request.code)
|
||||
if language == "unknown":
|
||||
raise HTTPException(status_code=400, detail="无法自动检测语言,请指定语言类型")
|
||||
|
||||
if request.task_type == "explain":
|
||||
return await explain_code(CodeRequest(
|
||||
code=request.code,
|
||||
language=language,
|
||||
task_type="explain",
|
||||
depth=request.depth
|
||||
))
|
||||
else:
|
||||
return await fix_code(CodeRequest(
|
||||
code=request.code,
|
||||
language=language,
|
||||
task_type="fix"
|
||||
))
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
return CodeResponse(
|
||||
success=False,
|
||||
task_type=request.task_type,
|
||||
result=None,
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
def main():
|
||||
"""主入口函数"""
|
||||
print("🚀 启动代码解释与修复助手...")
|
||||
print(f"🌐 服务地址: http://{config.APP_HOST}:{config.APP_PORT}")
|
||||
print("📚 API文档: http://localhost:8000/docs")
|
||||
|
||||
uvicorn.run(
|
||||
"app:app",
|
||||
host=config.APP_HOST,
|
||||
port=config.APP_PORT,
|
||||
reload=True
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
99
code_explanation_fixer.egg-info/PKG-INFO
Normal file
99
code_explanation_fixer.egg-info/PKG-INFO
Normal file
@ -0,0 +1,99 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: code-explanation-fixer
|
||||
Version: 0.1.0
|
||||
Summary: 代码解释与修复助手
|
||||
Requires-Python: <4.0,>=3.8
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: openai>=1.0.0
|
||||
Requires-Dist: python-dotenv>=1.0.0
|
||||
Requires-Dist: pydantic>=2.0.0
|
||||
Requires-Dist: fastapi>=0.100.0
|
||||
Requires-Dist: uvicorn>=0.23.0
|
||||
Requires-Dist: rich>=13.0.0
|
||||
Requires-Dist: customtkinter>=5.2.0
|
||||
Requires-Dist: streamlit>=1.20.0
|
||||
Requires-Dist: httpx>=0.28.0
|
||||
|
||||
# 代码解释与修复助手 - AI编程学习工具
|
||||
|
||||
一个智能代码解释与修复工具,帮助编程学习者理解代码并提供修复建议。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- **代码解释**: 对代码进行逐行或分段的详细解释
|
||||
- **Bug修复**: 识别代码问题并提供修复建议
|
||||
- **多语言支持**: 支持 Python、JavaScript、Java、C++、Go、Rust 等多种编程语言
|
||||
- **图形界面**: 简洁美观的界面,参考豆包AI问答方式
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
# 安装核心依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 安装图形界面依赖
|
||||
pip install -r requirements_gui.txt
|
||||
```
|
||||
|
||||
### 运行应用
|
||||
|
||||
#### 图形界面版本(推荐)
|
||||
```bash
|
||||
python gui_app.py
|
||||
```
|
||||
|
||||
#### Web API 版本
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
访问 http://localhost:8000 查看 API 文档
|
||||
|
||||
## 图形界面使用说明
|
||||
|
||||
1. 启动 `gui_app.py` 后,会弹出一个窗口
|
||||
2. 在代码输入框中粘贴或输入您的代码
|
||||
3. 选择任务类型:
|
||||
- 📖 代码解释:获取代码的详细解释
|
||||
- 🔧 代码修复:分析并修复代码问题
|
||||
4. 选择编程语言(可选择 auto 自动检测)
|
||||
5. 按 **Enter** 或 **Ctrl+Enter** 发送请求
|
||||
6. 等待 AI 分析,结果会显示在下方
|
||||
|
||||
## 配置说明
|
||||
|
||||
在项目根目录创建 `.env` 文件:
|
||||
|
||||
```
|
||||
# 硅基流动 API Key(必需)
|
||||
SILICONFLOW_API_KEY=your_api_key_here
|
||||
|
||||
# 可选配置
|
||||
SILICONFLOW_MODEL=deepseek-ai/DeepSeek-V2.5
|
||||
```
|
||||
|
||||
获取 API Key:访问 https://cloud.siliconflow.cn 注册账号
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
code-explainer-fixer/
|
||||
├── gui_app.py # 图形界面版本(推荐使用)
|
||||
├── app.py # Web API 版本
|
||||
├── config.py # 配置文件
|
||||
├── requirements.txt # 核心依赖
|
||||
├── requirements_gui.txt # GUI 依赖
|
||||
├── .env # API 配置(需手动创建)
|
||||
├── agents/ # 智能体模块
|
||||
├── services/ # 服务层
|
||||
├── models/ # 数据模型
|
||||
└── utils/ # 工具函数
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **图形界面**: CustomTkinter(现代化Tkinter)
|
||||
- **AI 服务**: 硅基流动(OpenAI 兼容 API)
|
||||
- **Web 框架**: FastAPI + Uvicorn
|
||||
16
code_explanation_fixer.egg-info/SOURCES.txt
Normal file
16
code_explanation_fixer.egg-info/SOURCES.txt
Normal file
@ -0,0 +1,16 @@
|
||||
README.md
|
||||
pyproject.toml
|
||||
agents/__init__.py
|
||||
agents/bug_fixer.py
|
||||
agents/code_explainer.py
|
||||
code_explanation_fixer.egg-info/PKG-INFO
|
||||
code_explanation_fixer.egg-info/SOURCES.txt
|
||||
code_explanation_fixer.egg-info/dependency_links.txt
|
||||
code_explanation_fixer.egg-info/requires.txt
|
||||
code_explanation_fixer.egg-info/top_level.txt
|
||||
models/__init__.py
|
||||
models/schemas.py
|
||||
services/__init__.py
|
||||
services/ai_service.py
|
||||
utils/__init__.py
|
||||
utils/code_parser.py
|
||||
1
code_explanation_fixer.egg-info/dependency_links.txt
Normal file
1
code_explanation_fixer.egg-info/dependency_links.txt
Normal file
@ -0,0 +1 @@
|
||||
|
||||
9
code_explanation_fixer.egg-info/requires.txt
Normal file
9
code_explanation_fixer.egg-info/requires.txt
Normal file
@ -0,0 +1,9 @@
|
||||
openai>=1.0.0
|
||||
python-dotenv>=1.0.0
|
||||
pydantic>=2.0.0
|
||||
fastapi>=0.100.0
|
||||
uvicorn>=0.23.0
|
||||
rich>=13.0.0
|
||||
customtkinter>=5.2.0
|
||||
streamlit>=1.20.0
|
||||
httpx>=0.28.0
|
||||
4
code_explanation_fixer.egg-info/top_level.txt
Normal file
4
code_explanation_fixer.egg-info/top_level.txt
Normal file
@ -0,0 +1,4 @@
|
||||
agents
|
||||
models
|
||||
services
|
||||
utils
|
||||
54
config.py
Normal file
54
config.py
Normal file
@ -0,0 +1,54 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
# 硅基流动 API 配置
|
||||
SILICONFLOW_API_KEY = os.getenv("SILICONFLOW_API_KEY")
|
||||
SILICONFLOW_BASE_URL = os.getenv("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1")
|
||||
SILICONFLOW_MODEL = os.getenv("SILICONFLOW_MODEL", "deepseek-ai/DeepSeek-V2.5")
|
||||
|
||||
# OpenAI 兼容配置(保留但默认使用 SiliconFlow)
|
||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
||||
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4")
|
||||
|
||||
# 应用配置
|
||||
APP_HOST = os.getenv("APP_HOST", "0.0.0.0")
|
||||
APP_PORT = int(os.getenv("APP_PORT", "8000"))
|
||||
|
||||
# 支持的编程语言
|
||||
SUPPORTED_LANGUAGES = [
|
||||
"python",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"java",
|
||||
"cpp",
|
||||
"c",
|
||||
"go",
|
||||
"rust",
|
||||
"ruby",
|
||||
"php"
|
||||
]
|
||||
|
||||
# 解释详细程度
|
||||
EXPLANATION_DEPTH = {
|
||||
"basic": "简单解释每行代码的作用",
|
||||
"detailed": "详细解释,包括语法、原理和最佳实践",
|
||||
"comprehensive": "全面深入的分析,包括复杂度、性能考虑等"
|
||||
}
|
||||
|
||||
# 硅基流动支持的模型列表
|
||||
SILICONFLOW_MODELS = [
|
||||
"deepseek-ai/DeepSeek-V2.5",
|
||||
"deepseek-ai/DeepSeek-V2",
|
||||
"Qwen/Qwen2.5-72B-Instruct",
|
||||
"Qwen/Qwen2.5-32B-Instruct",
|
||||
"Qwen/Qwen2.5-7B-Instruct",
|
||||
"01-ai/Yi-1.5-34B-Chat",
|
||||
"01-ai/Yi-1.5-9B-Chat",
|
||||
"meta-llama/Llama-3.1-405B-Instruct",
|
||||
"meta-llama/Llama-3.1-70B-Instruct"
|
||||
]
|
||||
|
||||
config = Config()
|
||||
352
gui_app.py
Normal file
352
gui_app.py
Normal file
@ -0,0 +1,352 @@
|
||||
import customtkinter as ctk
|
||||
from tkinter import scrolledtext
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
from openai import OpenAI
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
SILICONFLOW_API_KEY = os.getenv("SILICONFLOW_API_KEY")
|
||||
SILICONFLOW_BASE_URL = os.getenv("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1")
|
||||
SILICONFLOW_MODEL = os.getenv("SILICONFLOW_MODEL", "deepseek-ai/DeepSeek-V2.5")
|
||||
|
||||
config = Config()
|
||||
|
||||
class CodeExplanationApp(ctk.CTk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.title("代码解释与修复助手")
|
||||
self.geometry("1100x750")
|
||||
self.resizable(True, True)
|
||||
|
||||
ctk.set_appearance_mode("System")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
self.setup_ui()
|
||||
self.message_queue = queue.Queue()
|
||||
self.check_queue()
|
||||
|
||||
def setup_ui(self):
|
||||
self.grid_columnconfigure(0, weight=1)
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.main_frame = ctk.CTkFrame(self, corner_radius=0)
|
||||
self.main_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.main_frame.grid_columnconfigure(0, weight=1)
|
||||
self.main_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.header_frame = ctk.CTkFrame(self.main_frame, height=50, corner_radius=0)
|
||||
self.header_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0)
|
||||
self.header_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.title_label = ctk.CTkLabel(
|
||||
self.header_frame,
|
||||
text="🤖 代码解释与修复助手",
|
||||
font=("Microsoft YaHei", 20, "bold")
|
||||
)
|
||||
self.title_label.grid(row=0, column=0, padx=15, pady=12)
|
||||
|
||||
self.content_frame = ctk.CTkFrame(self.main_frame, corner_radius=0)
|
||||
self.content_frame.grid(row=1, column=0, sticky="nsew", padx=10, pady=10)
|
||||
self.content_frame.grid_columnconfigure(0, weight=2)
|
||||
self.content_frame.grid_columnconfigure(1, weight=3)
|
||||
self.content_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.left_panel = ctk.CTkFrame(self.content_frame, corner_radius=8)
|
||||
self.left_panel.grid(row=0, column=0, sticky="nsew", padx=(0, 8))
|
||||
self.left_panel.grid_columnconfigure(0, weight=1)
|
||||
self.left_panel.grid_rowconfigure(0, weight=1)
|
||||
self.left_panel.grid_rowconfigure(1, weight=1)
|
||||
|
||||
self.input_frame = ctk.CTkFrame(self.left_panel, corner_radius=6)
|
||||
self.input_frame.grid(row=0, column=0, sticky="nsew", padx=8, pady=8)
|
||||
self.input_frame.grid_columnconfigure(0, weight=1)
|
||||
self.input_frame.grid_rowconfigure(1, weight=1)
|
||||
|
||||
self.input_label = ctk.CTkLabel(
|
||||
self.input_frame,
|
||||
text="📝 输入您的代码:",
|
||||
font=("Microsoft YaHei", 13, "bold")
|
||||
)
|
||||
self.input_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4))
|
||||
|
||||
self.code_input = ctk.CTkTextbox(
|
||||
self.input_frame,
|
||||
font=("Consolas", 13),
|
||||
corner_radius=6
|
||||
)
|
||||
self.code_input.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8))
|
||||
|
||||
self.button_frame = ctk.CTkFrame(self.left_panel, corner_radius=6)
|
||||
self.button_frame.grid(row=1, column=0, sticky="ew", padx=8, pady=(0, 8))
|
||||
self.button_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self.options_frame = ctk.CTkFrame(self.button_frame, corner_radius=6)
|
||||
self.options_frame.grid(row=0, column=0, sticky="ew", padx=8, pady=8)
|
||||
self.options_frame.grid_columnconfigure((0, 1, 2), weight=1)
|
||||
|
||||
self.task_type = ctk.StringVar(value="explain")
|
||||
ctk.CTkRadioButton(
|
||||
self.options_frame,
|
||||
text="<EFBFBD> 代码解释",
|
||||
variable=self.task_type,
|
||||
value="explain",
|
||||
font=("Microsoft YaHei", 11)
|
||||
).grid(row=0, column=0, padx=15, pady=6)
|
||||
|
||||
ctk.CTkRadioButton(
|
||||
self.options_frame,
|
||||
text="<EFBFBD> 代码修复",
|
||||
variable=self.task_type,
|
||||
value="fix",
|
||||
font=("Microsoft YaHei", 11)
|
||||
).grid(row=0, column=1, padx=15, pady=6)
|
||||
|
||||
self.language_var = ctk.StringVar(value="auto")
|
||||
self.language_combo = ctk.CTkComboBox(
|
||||
self.options_frame,
|
||||
values=["auto", "python", "javascript", "java", "cpp", "c", "go", "rust", "ruby", "php"],
|
||||
variable=self.language_var,
|
||||
font=("Microsoft YaHei", 11)
|
||||
)
|
||||
self.language_combo.grid(row=0, column=2, padx=15, pady=6)
|
||||
|
||||
self.button_inner_frame = ctk.CTkFrame(self.button_frame, corner_radius=0, fg_color="transparent")
|
||||
self.button_inner_frame.grid(row=1, column=0, sticky="e", padx=8, pady=(0, 8))
|
||||
|
||||
self.submit_button = ctk.CTkButton(
|
||||
self.button_inner_frame,
|
||||
text="<EFBFBD> 开始分析",
|
||||
command=self.on_submit_clicked,
|
||||
font=("Microsoft YaHei", 12, "bold"),
|
||||
height=30,
|
||||
corner_radius=6
|
||||
)
|
||||
self.submit_button.grid(row=0, column=0, padx=4)
|
||||
|
||||
self.clear_button = ctk.CTkButton(
|
||||
self.button_inner_frame,
|
||||
text="<EFBFBD>️ 清空",
|
||||
command=self.on_clear_clicked,
|
||||
font=("Microsoft YaHei", 11),
|
||||
height=30,
|
||||
corner_radius=6,
|
||||
fg_color="gray",
|
||||
hover_color="darkgray"
|
||||
)
|
||||
self.clear_button.grid(row=0, column=1, padx=4)
|
||||
|
||||
self.right_panel = ctk.CTkFrame(self.content_frame, corner_radius=8)
|
||||
self.right_panel.grid(row=0, column=1, sticky="nsew", padx=(8, 0))
|
||||
self.right_panel.grid_columnconfigure(0, weight=1)
|
||||
self.right_panel.grid_rowconfigure(0, weight=1)
|
||||
self.right_panel.grid_rowconfigure(1, weight=2)
|
||||
|
||||
self.process_frame = ctk.CTkFrame(self.right_panel, corner_radius=6)
|
||||
self.process_frame.grid(row=0, column=0, sticky="nsew", padx=8, pady=8)
|
||||
self.process_frame.grid_columnconfigure(0, weight=1)
|
||||
self.process_frame.grid_rowconfigure(1, weight=1)
|
||||
|
||||
self.process_label = ctk.CTkLabel(
|
||||
self.process_frame,
|
||||
text="⚙️ 进程状态",
|
||||
font=("Microsoft YaHei", 13, "bold")
|
||||
)
|
||||
self.process_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4))
|
||||
|
||||
self.process_text = ctk.CTkTextbox(
|
||||
self.process_frame,
|
||||
font=("Consolas", 11),
|
||||
corner_radius=6,
|
||||
state="disabled"
|
||||
)
|
||||
self.process_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8))
|
||||
|
||||
self.result_frame = ctk.CTkFrame(self.right_panel, corner_radius=6)
|
||||
self.result_frame.grid(row=1, column=0, sticky="nsew", padx=8, pady=(0, 8))
|
||||
self.result_frame.grid_columnconfigure(0, weight=1)
|
||||
self.result_frame.grid_rowconfigure(1, weight=1)
|
||||
|
||||
self.result_label = ctk.CTkLabel(
|
||||
self.result_frame,
|
||||
text="💡 AI 分析结果",
|
||||
font=("Microsoft YaHei", 13, "bold")
|
||||
)
|
||||
self.result_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4))
|
||||
|
||||
self.result_text = ctk.CTkTextbox(
|
||||
self.result_frame,
|
||||
font=("Microsoft YaHei", 12),
|
||||
corner_radius=6,
|
||||
state="disabled"
|
||||
)
|
||||
self.result_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8))
|
||||
|
||||
self.status_label = ctk.CTkLabel(
|
||||
self,
|
||||
text="就绪 - 请输入代码后点击「🚀 开始分析」按钮",
|
||||
font=("Microsoft YaHei", 10)
|
||||
)
|
||||
self.status_label.grid(row=2, column=0, sticky="ew", padx=15, pady=(0, 8))
|
||||
|
||||
def log_process(self, message):
|
||||
self.process_text.configure(state="normal")
|
||||
timestamp = time.strftime("%H:%M:%S", time.localtime())
|
||||
self.process_text.insert("end", f"[{timestamp}] {message}\n")
|
||||
self.process_text.see("end")
|
||||
self.process_text.configure(state="disabled")
|
||||
|
||||
def on_submit_clicked(self):
|
||||
code = self.code_input.get("0.0", "end").strip()
|
||||
if code:
|
||||
self.process_code(code)
|
||||
else:
|
||||
self.status_label.configure(text="请先输入代码!")
|
||||
|
||||
def on_clear_clicked(self):
|
||||
self.code_input.delete("0.0", "end")
|
||||
self.result_text.configure(state="normal")
|
||||
self.result_text.delete("0.0", "end")
|
||||
self.result_text.configure(state="disabled")
|
||||
self.process_text.configure(state="normal")
|
||||
self.process_text.delete("0.0", "end")
|
||||
self.process_text.configure(state="disabled")
|
||||
self.status_label.configure(text="已清空,请输入新的代码")
|
||||
|
||||
def process_code(self, code):
|
||||
task = self.task_type.get()
|
||||
language = self.language_var.get()
|
||||
|
||||
self.process_text.configure(state="normal")
|
||||
self.process_text.delete("0.0", "end")
|
||||
self.process_text.configure(state="disabled")
|
||||
|
||||
self.log_process("🚀 任务已启动")
|
||||
self.log_process(f"📋 任务类型: {'代码解释' if task == 'explain' else '代码修复'}")
|
||||
self.log_process(f"🌐 检测语言: {language}")
|
||||
self.log_process(f"📏 代码长度: {len(code)} 字符")
|
||||
|
||||
self.status_label.configure(text="正在处理...")
|
||||
self.result_text.configure(state="normal")
|
||||
self.result_text.delete("0.0", "end")
|
||||
self.result_text.insert("0.0", "🚀 正在实时生成响应...\n\n")
|
||||
self.result_text.configure(state="disabled")
|
||||
|
||||
threading.Thread(target=self.call_ai, args=(code, task, language), daemon=True).start()
|
||||
|
||||
def call_ai(self, code, task, language):
|
||||
try:
|
||||
api_key = config.SILICONFLOW_API_KEY
|
||||
if not api_key:
|
||||
self.message_queue.put(("error", "请在 .env 文件中设置 SILICONFLOW_API_KEY"))
|
||||
return
|
||||
|
||||
self.log_process("🔌 正在连接 SiliconFlow API...")
|
||||
self.log_process(f"🤖 使用模型: {config.SILICONFLOW_MODEL}")
|
||||
|
||||
client = OpenAI(api_key=api_key, base_url=config.SILICONFLOW_BASE_URL)
|
||||
|
||||
if task == "explain":
|
||||
prompt = f"""
|
||||
请作为一位耐心的编程导师,解释以下{language}代码。
|
||||
|
||||
请提供:
|
||||
1. 代码整体功能的概述
|
||||
2. 逐行或分段的详细解释
|
||||
3. 关键概念和语法的说明
|
||||
4. 相关的最佳实践建议
|
||||
|
||||
代码:
|
||||
```{language}
|
||||
{code}
|
||||
```
|
||||
"""
|
||||
else:
|
||||
prompt = f"""
|
||||
请作为一位经验丰富的开发者,分析并修复以下{language}代码中的问题。
|
||||
|
||||
请提供:
|
||||
1. 发现的问题列表
|
||||
2. 修复后的完整代码
|
||||
3. 每个修复的详细说明
|
||||
4. 相关的最佳实践建议
|
||||
|
||||
代码:
|
||||
```{language}
|
||||
{code}
|
||||
```
|
||||
"""
|
||||
|
||||
self.log_process("📤 正在发送请求...")
|
||||
start_time = time.time()
|
||||
|
||||
stream = client.chat.completions.create(
|
||||
model=config.SILICONFLOW_MODEL,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=0.7 if task == "explain" else 0.5,
|
||||
stream=True
|
||||
)
|
||||
|
||||
self.log_process("📥 正在接收响应...")
|
||||
full_response = ""
|
||||
chunk_count = 0
|
||||
for chunk in stream:
|
||||
if chunk.choices[0].delta.content:
|
||||
content = chunk.choices[0].delta.content
|
||||
full_response += content
|
||||
chunk_count += 1
|
||||
self.message_queue.put(("stream", content))
|
||||
|
||||
elapsed_time = time.time() - start_time
|
||||
self.log_process(f"✅ 响应接收完成")
|
||||
self.log_process(f"📊 收到 {chunk_count} 个数据块")
|
||||
self.log_process(f"⏱️ 耗时: {elapsed_time:.2f} 秒")
|
||||
self.log_process(f"📏 结果长度: {len(full_response)} 字符")
|
||||
|
||||
self.message_queue.put(("success", full_response))
|
||||
|
||||
except Exception as e:
|
||||
self.log_process(f"❌ 发生错误: {str(e)}")
|
||||
self.message_queue.put(("error", f"调用 AI 服务失败:{str(e)}"))
|
||||
|
||||
def check_queue(self):
|
||||
try:
|
||||
while not self.message_queue.empty():
|
||||
msg_type, content = self.message_queue.get_nowait()
|
||||
|
||||
if msg_type == "stream":
|
||||
self.result_text.configure(state="normal")
|
||||
self.result_text.insert("end", content)
|
||||
self.result_text.see("end")
|
||||
self.result_text.configure(state="disabled")
|
||||
self.status_label.configure(text="正在生成响应中...")
|
||||
|
||||
else:
|
||||
self.result_text.configure(state="normal")
|
||||
|
||||
if msg_type == "success":
|
||||
if content and not content.startswith("✅"):
|
||||
self.result_text.insert("end", f"\n\n{content}")
|
||||
self.status_label.configure(text="✅ 处理完成")
|
||||
self.log_process("🎉 任务完成!")
|
||||
else:
|
||||
self.result_text.insert("end", f"\n\n❌ 错误:{content}")
|
||||
self.status_label.configure(text="❌ 发生错误")
|
||||
|
||||
self.result_text.configure(state="disabled")
|
||||
except:
|
||||
pass
|
||||
|
||||
self.after(50, self.check_queue)
|
||||
|
||||
def main():
|
||||
app = CodeExplanationApp()
|
||||
app.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
9
models/__init__.py
Normal file
9
models/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from .schemas import CodeRequest, CodeExplanation, BugFix, CodeResponse, ChatMessage
|
||||
|
||||
__all__ = [
|
||||
"CodeRequest",
|
||||
"CodeExplanation",
|
||||
"BugFix",
|
||||
"CodeResponse",
|
||||
"ChatMessage"
|
||||
]
|
||||
BIN
models/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
models/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
models/__pycache__/schemas.cpython-313.pyc
Normal file
BIN
models/__pycache__/schemas.cpython-313.pyc
Normal file
Binary file not shown.
31
models/schemas.py
Normal file
31
models/schemas.py
Normal file
@ -0,0 +1,31 @@
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
class CodeRequest(BaseModel):
|
||||
code: str
|
||||
language: str
|
||||
task_type: str # "explain" 或 "fix"
|
||||
depth: str = "detailed"
|
||||
|
||||
class CodeExplanation(BaseModel):
|
||||
explanation: str
|
||||
line_by_line: Optional[List[dict]] = None
|
||||
key_concepts: Optional[List[str]] = None
|
||||
suggestions: Optional[List[str]] = None
|
||||
|
||||
class BugFix(BaseModel):
|
||||
original_code: str
|
||||
fixed_code: str
|
||||
problems_found: List[str]
|
||||
fixes_applied: List[str]
|
||||
explanation: str
|
||||
|
||||
class CodeResponse(BaseModel):
|
||||
success: bool
|
||||
task_type: str
|
||||
result: CodeExplanation | BugFix
|
||||
error: Optional[str] = None
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
32
pyproject.toml
Normal file
32
pyproject.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[project]
|
||||
name = "code-explanation-fixer"
|
||||
version = "0.1.0"
|
||||
description = "代码解释与修复助手"
|
||||
authors = []
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8,<4.0"
|
||||
dependencies = [
|
||||
"openai>=1.0.0",
|
||||
"python-dotenv>=1.0.0",
|
||||
"pydantic>=2.0.0",
|
||||
"fastapi>=0.100.0",
|
||||
"uvicorn>=0.23.0",
|
||||
"rich>=13.0.0",
|
||||
"customtkinter>=5.2.0",
|
||||
"streamlit>=1.20.0", # 添加Streamlit依赖
|
||||
"httpx>=0.28.0", # 添加HTTP客户端依赖
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
exclude = ["static"] # 排除static目录,它不是Python包
|
||||
|
||||
[tool.uv]
|
||||
required-version = "0.9.22"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = "."
|
||||
3
services/__init__.py
Normal file
3
services/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .ai_service import ai_service
|
||||
|
||||
__all__ = ["ai_service"]
|
||||
BIN
services/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
services/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
services/__pycache__/ai_service.cpython-313.pyc
Normal file
BIN
services/__pycache__/ai_service.cpython-313.pyc
Normal file
Binary file not shown.
87
services/ai_service.py
Normal file
87
services/ai_service.py
Normal file
@ -0,0 +1,87 @@
|
||||
from openai import OpenAI
|
||||
from config import config
|
||||
from typing import Optional
|
||||
|
||||
class AIService:
|
||||
def __init__(self):
|
||||
api_key = config.SILICONFLOW_API_KEY or config.OPENAI_API_KEY
|
||||
base_url = config.SILICONFLOW_BASE_URL
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("请设置 SILICONFLOW_API_KEY 或 OPENAI_API_KEY 环境变量")
|
||||
|
||||
self.client = OpenAI(
|
||||
api_key=api_key,
|
||||
base_url=base_url
|
||||
)
|
||||
self.model = config.SILICONFLOW_MODEL
|
||||
|
||||
def generate_explanation(self, code: str, language: str, depth: str) -> str:
|
||||
"""生成代码解释"""
|
||||
depth_instruction = config.EXPLANATION_DEPTH.get(depth, config.EXPLANATION_DEPTH["detailed"])
|
||||
|
||||
prompt = f"""
|
||||
请作为一位耐心的编程导师,解释以下{language}代码。
|
||||
|
||||
解释要求:{depth_instruction}
|
||||
|
||||
代码:
|
||||
```{language}
|
||||
{code}
|
||||
```
|
||||
|
||||
请提供:
|
||||
1. 代码整体功能的概述
|
||||
2. 逐行或分段的详细解释
|
||||
3. 关键概念和语法的说明
|
||||
4. 相关的最佳实践建议
|
||||
"""
|
||||
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
return response.choices[0].message.content
|
||||
|
||||
def generate_fix(self, code: str, language: str, error_description: Optional[str] = None) -> dict:
|
||||
"""生成代码修复建议"""
|
||||
prompt = f"""
|
||||
请作为一位经验丰富的开发者,分析并修复以下{language}代码中的问题。
|
||||
|
||||
{('错误描述: ' + error_description) if error_description else ''}
|
||||
|
||||
代码:
|
||||
```{language}
|
||||
{code}
|
||||
```
|
||||
|
||||
请提供:
|
||||
1. 发现的问题列表
|
||||
2. 修复后的完整代码
|
||||
3. 每个修复的详细说明
|
||||
4. 相关的最佳实践建议
|
||||
"""
|
||||
|
||||
response = self.client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=0.5
|
||||
)
|
||||
|
||||
content = response.choices[0].message.content
|
||||
|
||||
return {
|
||||
"analysis": content,
|
||||
"fixed_code": self._extract_code_from_response(content)
|
||||
}
|
||||
|
||||
def _extract_code_from_response(self, response: str) -> str:
|
||||
"""从AI响应中提取代码块"""
|
||||
import re
|
||||
pattern = r'```[\w]*\n([\s\S]*?)```'
|
||||
matches = re.findall(pattern, response)
|
||||
return matches[0] if matches else ""
|
||||
|
||||
ai_service = AIService()
|
||||
112
static/index.html
Normal file
112
static/index.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>代码解释与修复助手</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 顶部标题栏 -->
|
||||
<header class="header">
|
||||
<div class="title-section">
|
||||
<i class="fas fa-code header-icon"></i>
|
||||
<h1>代码解释与修复助手</h1>
|
||||
</div>
|
||||
<p class="subtitle">帮助编程学习者理解代码并提供修复建议的AI工具</p>
|
||||
</header>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="main-content">
|
||||
<!-- 左侧输入面板 -->
|
||||
<div class="panel input-panel">
|
||||
<div class="panel-header">
|
||||
<h2><i class="fas fa-edit"></i> 代码输入</h2>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="codeInput"><i class="fas fa-code"></i> 请输入您的代码:</label>
|
||||
<textarea id="codeInput" placeholder="在此输入您需要分析的代码..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="options-container">
|
||||
<div class="form-group">
|
||||
<label for="languageSelect"><i class="fas fa-language"></i> 编程语言:</label>
|
||||
<select id="languageSelect">
|
||||
<option value="python">Python</option>
|
||||
<option value="javascript">JavaScript</option>
|
||||
<option value="typescript">TypeScript</option>
|
||||
<option value="java">Java</option>
|
||||
<option value="cpp">C++</option>
|
||||
<option value="c">C</option>
|
||||
<option value="go">Go</option>
|
||||
<option value="rust">Rust</option>
|
||||
<option value="ruby">Ruby</option>
|
||||
<option value="php">PHP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="taskTypeSelect"><i class="fas fa-tasks"></i> 任务类型:</label>
|
||||
<select id="taskTypeSelect">
|
||||
<option value="explain">代码解释</option>
|
||||
<option value="fix">代码修复</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="depthGroup">
|
||||
<label for="depthSelect"><i class="fas fa-depth-chart"></i> 详细程度:</label>
|
||||
<select id="depthSelect">
|
||||
<option value="basic">基础</option>
|
||||
<option value="detailed" selected>详细</option>
|
||||
<option value="comprehensive">全面</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="startBtn" class="btn btn-primary">
|
||||
<i class="fas fa-play"></i> 开始分析
|
||||
</button>
|
||||
<button id="clearBtn" class="btn btn-secondary">
|
||||
<i class="fas fa-trash"></i> 清空
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧结果面板 -->
|
||||
<div class="panel result-panel">
|
||||
<div class="panel-header">
|
||||
<h2><i class="fas fa-clipboard-check"></i> 分析结果</h2>
|
||||
<div id="statusIndicator" class="status status-idle">
|
||||
<i class="fas fa-circle"></i> 就绪
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-container">
|
||||
<div id="loadingIndicator" class="loading" style="display: none;">
|
||||
<div class="spinner"></div>
|
||||
<p>正在分析代码...请稍候</p>
|
||||
</div>
|
||||
|
||||
<div id="resultContent" class="result-content">
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
<p>输入代码并点击"开始分析"按钮,获取AI分析结果</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<footer class="footer">
|
||||
<p>© 2026 代码解释与修复助手 | 基于硅基流动AI服务</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
204
static/script.js
Normal file
204
static/script.js
Normal file
@ -0,0 +1,204 @@
|
||||
// 代码解释与修复助手 - 前端JavaScript交互逻辑
|
||||
|
||||
// DOM元素
|
||||
const codeInput = document.getElementById('codeInput');
|
||||
const languageSelect = document.getElementById('languageSelect');
|
||||
const taskTypeSelect = document.getElementById('taskTypeSelect');
|
||||
const depthSelect = document.getElementById('depthSelect');
|
||||
const depthGroup = document.getElementById('depthGroup');
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const clearBtn = document.getElementById('clearBtn');
|
||||
const statusIndicator = document.getElementById('statusIndicator');
|
||||
const loadingIndicator = document.getElementById('loadingIndicator');
|
||||
const resultContent = document.getElementById('resultContent');
|
||||
|
||||
// 任务类型切换
|
||||
function toggleDepthOption() {
|
||||
if (taskTypeSelect.value === 'explain') {
|
||||
depthGroup.style.display = 'block';
|
||||
} else {
|
||||
depthGroup.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化事件监听
|
||||
function initEventListeners() {
|
||||
// 任务类型选择变化
|
||||
taskTypeSelect.addEventListener('change', toggleDepthOption);
|
||||
|
||||
// 开始分析按钮
|
||||
startBtn.addEventListener('click', analyzeCode);
|
||||
|
||||
// 清空按钮
|
||||
clearBtn.addEventListener('click', clearAll);
|
||||
|
||||
// 键盘快捷键
|
||||
codeInput.addEventListener('keydown', (e) => {
|
||||
// Ctrl+Enter 或 Cmd+Enter 触发分析
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
analyzeCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新状态显示
|
||||
function updateStatus(status, message) {
|
||||
statusIndicator.className = `status status-${status}`;
|
||||
statusIndicator.innerHTML = `<i class="fas fa-circle"></i> ${message}`;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
loadingIndicator.style.display = 'flex';
|
||||
resultContent.style.display = 'none';
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
function hideLoading() {
|
||||
loadingIndicator.style.display = 'none';
|
||||
resultContent.style.display = 'block';
|
||||
}
|
||||
|
||||
// 显示结果
|
||||
function showResult(data) {
|
||||
if (data.success) {
|
||||
resultContent.innerHTML = `
|
||||
<div class="result-header">
|
||||
<h3>
|
||||
<i class="fas fa-check-circle success-icon"></i>
|
||||
分析完成
|
||||
</h3>
|
||||
<div class="task-info">
|
||||
<span class="language-tag">${getLanguageName(languageSelect.value)}</span>
|
||||
<span class="task-type">${taskTypeSelect.value === 'explain' ? '代码解释' : '代码修复'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-body">
|
||||
${formatResult(data.result)}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
resultContent.innerHTML = `
|
||||
<div class="error-state">
|
||||
<i class="fas fa-exclamation-circle error-icon"></i>
|
||||
<h3>分析失败</h3>
|
||||
<p class="error-message">${data.error || '发生未知错误'}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化结果为HTML
|
||||
function formatResult(result) {
|
||||
if (!result) return '<p>无结果</p>';
|
||||
|
||||
// 简单的Markdown到HTML转换
|
||||
let html = result
|
||||
.replace(/^# (.*$)/gm, '<h3>$1</h3>')
|
||||
.replace(/^## (.*$)/gm, '<h4>$1</h4>')
|
||||
.replace(/`(.*?)`/g, '<code>$1</code>')
|
||||
.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>')
|
||||
.replace(/\n/g, '<br>');
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// 获取语言名称
|
||||
function getLanguageName(langCode) {
|
||||
const languages = {
|
||||
'python': 'Python',
|
||||
'javascript': 'JavaScript',
|
||||
'typescript': 'TypeScript',
|
||||
'java': 'Java',
|
||||
'cpp': 'C++',
|
||||
'c': 'C',
|
||||
'go': 'Go',
|
||||
'rust': 'Rust',
|
||||
'ruby': 'Ruby',
|
||||
'php': 'PHP'
|
||||
};
|
||||
return languages[langCode] || langCode;
|
||||
}
|
||||
|
||||
// 分析代码
|
||||
async function analyzeCode() {
|
||||
const code = codeInput.value.trim();
|
||||
const language = languageSelect.value;
|
||||
const taskType = taskTypeSelect.value;
|
||||
const depth = taskType === 'explain' ? depthSelect.value : undefined;
|
||||
|
||||
// 验证输入
|
||||
if (!code) {
|
||||
alert('请输入代码后再进行分析');
|
||||
codeInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
updateStatus('loading', '正在分析...');
|
||||
showLoading();
|
||||
|
||||
try {
|
||||
// 发送请求
|
||||
const response = await fetch('/auto', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
language: language,
|
||||
task_type: taskType,
|
||||
depth: depth
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// 显示结果
|
||||
hideLoading();
|
||||
showResult(data);
|
||||
updateStatus(data.success ? 'success' : 'error', data.success ? '分析完成' : '分析失败');
|
||||
} catch (error) {
|
||||
hideLoading();
|
||||
updateStatus('error', '请求失败');
|
||||
resultContent.innerHTML = `
|
||||
<div class="error-state">
|
||||
<i class="fas fa-exclamation-circle error-icon"></i>
|
||||
<h3>网络错误</h3>
|
||||
<p class="error-message">无法连接到服务器,请检查网络连接</p>
|
||||
</div>
|
||||
`;
|
||||
console.error('请求错误:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空所有内容
|
||||
function clearAll() {
|
||||
codeInput.value = '';
|
||||
languageSelect.value = 'python';
|
||||
taskTypeSelect.value = 'explain';
|
||||
depthSelect.value = 'detailed';
|
||||
toggleDepthOption();
|
||||
|
||||
resultContent.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-lightbulb"></i>
|
||||
<p>输入代码并点击"开始分析"按钮,获取AI分析结果</p>
|
||||
</div>
|
||||
`;
|
||||
updateStatus('idle', '就绪');
|
||||
|
||||
codeInput.focus();
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
function initApp() {
|
||||
toggleDepthOption();
|
||||
initEventListeners();
|
||||
codeInput.focus();
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', initApp);
|
||||
370
static/styles.css
Normal file
370
static/styles.css
Normal file
@ -0,0 +1,370 @@
|
||||
/* 全局样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 2.5rem;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: #4a5568;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: #718096;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
/* 面板样式 */
|
||||
.panel {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px 30px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.panel-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 输入面板样式 */
|
||||
.input-panel {
|
||||
min-height: 700px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.form-group textarea,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
resize: vertical;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
min-height: 300px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.form-group select {
|
||||
height: 50px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.form-group textarea:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.options-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
padding: 20px 30px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 15px 30px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #e2e8f0;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #cbd5e0;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 结果面板样式 */
|
||||
.result-panel {
|
||||
min-height: 700px;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
padding: 8px 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.status-idle {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-processing {
|
||||
background-color: rgba(255, 255, 0, 0.2);
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: rgba(0, 255, 0, 0.2);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: rgba(255, 0, 0, 0.2);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
padding: 30px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 加载指示器 */
|
||||
.loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 50px 0;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #e2e8f0;
|
||||
border-top: 5px solid #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 结果内容 */
|
||||
.result-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 80px 0;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 结果文本样式 */
|
||||
.result-text {
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
line-height: 1.8;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
/* 底部信息 */
|
||||
.footer {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 20px 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
text-align: center;
|
||||
color: #718096;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.main-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.options-container {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #a1a1a1;
|
||||
}
|
||||
309
streamlit_app.py
Normal file
309
streamlit_app.py
Normal file
@ -0,0 +1,309 @@
|
||||
import streamlit as st
|
||||
import time
|
||||
from config import config
|
||||
from agents import code_explainer, bug_fixer
|
||||
from utils import detect_language
|
||||
|
||||
# 设置页面配置
|
||||
st.set_page_config(
|
||||
page_title="代码解释与修复助手",
|
||||
page_icon="🤖",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
# 自定义CSS样式
|
||||
st.markdown("""
|
||||
<style>
|
||||
/* 全局样式 */
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.stApp h1 {
|
||||
color: #2c3e50;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stApp h2 {
|
||||
color: #34495e;
|
||||
font-size: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* 容器样式 */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.stButton > button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.stButton > button:hover {
|
||||
background-color: #2980b9;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 12px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
/* 输入区域样式 */
|
||||
.stTextArea > div > div {
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.stTextArea > div > div:focus-within {
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
/* 选择框样式 */
|
||||
.stSelectbox > div > div {
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
/* 结果区域样式 */
|
||||
.result-container {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
border-left: 4px solid #3498db;
|
||||
}
|
||||
|
||||
/* 错误提示样式 */
|
||||
.error-container {
|
||||
background-color: #fff5f5;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
border-left: 4px solid #e74c3c;
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
/* 加载动画样式 */
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* 响应式布局 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stApp h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# 页面标题和描述
|
||||
st.title("🤖 代码解释与修复助手")
|
||||
st.markdown("""
|
||||
<div style='text-align: center; color: #6c757d; font-size: 1.1rem; margin-bottom: 2rem;'>
|
||||
输入您的代码,选择功能,AI将为您提供专业的代码解释或修复建议
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# 创建两列布局
|
||||
col1, col2 = st.columns([1, 1], gap="large")
|
||||
|
||||
# 左侧:输入区域
|
||||
with col1:
|
||||
with st.container():
|
||||
st.subheader("📝 代码输入")
|
||||
|
||||
# 代码输入框
|
||||
code_input = st.text_area(
|
||||
"请输入您需要分析的代码:",
|
||||
height=300,
|
||||
placeholder="在此输入您的代码...\n例如:\ndef hello():\n print('Hello World')",
|
||||
label_visibility="collapsed"
|
||||
)
|
||||
|
||||
# 语言选择
|
||||
language = st.selectbox(
|
||||
"编程语言:",
|
||||
options=[(lang.capitalize(), lang) for lang in config.SUPPORTED_LANGUAGES],
|
||||
format_func=lambda x: x[0],
|
||||
key="language"
|
||||
)
|
||||
|
||||
# 任务类型选择
|
||||
task_type = st.selectbox(
|
||||
"任务类型:",
|
||||
options=[("代码解释", "explain"), ("代码修复", "fix")],
|
||||
format_func=lambda x: x[0],
|
||||
key="task_type"
|
||||
)
|
||||
|
||||
# 解释详细程度(仅在代码解释时显示)
|
||||
depth = None
|
||||
if task_type[1] == "explain":
|
||||
# 使用简单的字符串选项,完全避免元组带来的状态管理问题
|
||||
depth_options = ["基础", "详细", "全面"]
|
||||
depth_values = {"基础": "basic", "详细": "detailed", "全面": "comprehensive"}
|
||||
|
||||
# 设置默认值为"详细"
|
||||
depth_str = st.select_slider(
|
||||
"解释详细程度:",
|
||||
options=depth_options,
|
||||
value="详细",
|
||||
key="depth_str"
|
||||
)
|
||||
|
||||
# 构建需要的返回值格式
|
||||
depth = (depth_str, depth_values[depth_str])
|
||||
|
||||
# 开始分析按钮
|
||||
start_button = st.button(
|
||||
"🚀 开始分析",
|
||||
type="primary",
|
||||
use_container_width=True
|
||||
)
|
||||
|
||||
# 右侧:结果区域
|
||||
with col2:
|
||||
with st.container():
|
||||
st.subheader("📊 分析结果")
|
||||
|
||||
# 结果显示区域
|
||||
result_placeholder = st.empty()
|
||||
|
||||
# 初始状态
|
||||
with result_placeholder.container():
|
||||
st.markdown("""
|
||||
<div style='text-align: center; color: #95a5a6; padding: 3rem 0;'>
|
||||
<div style='font-size: 3rem; margin-bottom: 1rem;'>💡</div>
|
||||
<p style='font-size: 1.1rem;'>输入代码并点击"开始分析"按钮,获取AI分析结果</p>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# 处理分析请求
|
||||
if start_button:
|
||||
# 验证输入
|
||||
if not code_input.strip():
|
||||
with result_placeholder.container():
|
||||
st.error("请输入代码后再进行分析!")
|
||||
else:
|
||||
# 显示加载状态
|
||||
with result_placeholder.container():
|
||||
with st.spinner("🤖 AI正在分析代码...请稍候"):
|
||||
# 添加视觉反馈
|
||||
loading_bar = st.progress(0)
|
||||
for i in range(100):
|
||||
time.sleep(0.01) # 模拟进度
|
||||
loading_bar.progress(i + 1)
|
||||
|
||||
try:
|
||||
# 调用相应的处理函数
|
||||
if task_type[1] == "explain":
|
||||
# 代码解释
|
||||
result = code_explainer.explain(
|
||||
code=code_input,
|
||||
language=language[1],
|
||||
depth=depth[1]
|
||||
)
|
||||
|
||||
# 显示解释结果
|
||||
with result_placeholder.container():
|
||||
st.success("✅ 代码解释完成!")
|
||||
st.markdown("""
|
||||
<div class='result-container'>
|
||||
<h3 style='color: #27ae60; margin-bottom: 1rem;'>📚 代码解释</h3>
|
||||
<div style='white-space: pre-wrap; line-height: 1.6;'>{}</div>
|
||||
</div>
|
||||
""".format(result), unsafe_allow_html=True)
|
||||
|
||||
else:
|
||||
# 代码修复
|
||||
result = bug_fixer.fix(
|
||||
code=code_input,
|
||||
language=language[1]
|
||||
)
|
||||
|
||||
# 显示修复结果
|
||||
with result_placeholder.container():
|
||||
st.success("✅ 代码修复完成!")
|
||||
|
||||
# 显示问题分析
|
||||
st.markdown("""
|
||||
<div class='result-container'>
|
||||
<h3 style='color: #e67e22; margin-bottom: 1rem;'>🔍 问题分析</h3>
|
||||
<ul style='line-height: 1.8;'>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# 解析并显示问题
|
||||
if hasattr(result, 'problems_found'):
|
||||
for problem in result.problems_found:
|
||||
st.markdown(f"- {problem}")
|
||||
|
||||
st.markdown("""
|
||||
</ul>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# 显示修复方案
|
||||
st.markdown("""
|
||||
<div class='result-container'>
|
||||
<h3 style='color: #27ae60; margin-bottom: 1rem;'>🔧 修复方案</h3>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
if hasattr(result, 'fixed_code'):
|
||||
st.code(result.fixed_code, language=language[1])
|
||||
|
||||
# 显示修复说明
|
||||
if hasattr(result, 'explanation'):
|
||||
st.markdown("""
|
||||
<h4 style='color: #3498db; margin-top: 1rem; margin-bottom: 0.5rem;'>📝 修复说明</h4>
|
||||
<div style='line-height: 1.6;'>{}</div>
|
||||
</div>
|
||||
""".format(result.explanation), unsafe_allow_html=True)
|
||||
|
||||
except Exception as e:
|
||||
# 显示错误信息
|
||||
with result_placeholder.container():
|
||||
st.error("❌ 分析失败!")
|
||||
st.markdown("""
|
||||
<div class='error-container'>
|
||||
<h4>错误信息:</h4>
|
||||
<p>{}</p>
|
||||
</div>
|
||||
""".format(str(e)), unsafe_allow_html=True)
|
||||
|
||||
# 页脚信息
|
||||
st.markdown("""
|
||||
<div style='text-align: center; color: #95a5a6; font-size: 0.9rem; margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #ecf0f1;'>
|
||||
<p>基于硅基流动AI服务 | 支持多种编程语言</p>
|
||||
</div>
|
||||
""", unsafe_allow_html=True)
|
||||
53
test_code_with_bugs.py
Normal file
53
test_code_with_bugs.py
Normal file
@ -0,0 +1,53 @@
|
||||
"""
|
||||
测试代码 - 包含多个常见错误和问题
|
||||
"""
|
||||
|
||||
def calculate_average(numbers)
|
||||
total = 0
|
||||
count = 0
|
||||
for num in numbers
|
||||
total += num
|
||||
count += 1
|
||||
average = total / count
|
||||
return average
|
||||
|
||||
def find_max(numbers):
|
||||
max_value = 0
|
||||
for num in numbers:
|
||||
if num > max_value:
|
||||
max_value = num
|
||||
return max_value
|
||||
|
||||
def process_data(data):
|
||||
result = []
|
||||
for item in data:
|
||||
if item > 0:
|
||||
result.append(item * 2)
|
||||
if item < 0:
|
||||
result.append(abs(item))
|
||||
return result
|
||||
|
||||
def create_user(name, age):
|
||||
user = {
|
||||
"name": name,
|
||||
"age": age,
|
||||
}
|
||||
return user
|
||||
|
||||
def main():
|
||||
numbers = [10, 20, -5, 30, 15, -10]
|
||||
|
||||
avg = calculate_average(numbers)
|
||||
print("平均值:", avg)
|
||||
|
||||
max_num = find_max(numbers)
|
||||
print("最大值:", max_num)
|
||||
|
||||
processed = process_data(numbers)
|
||||
print("处理结果:", processed)
|
||||
|
||||
user = create_user("张三", 25)
|
||||
print("用户信息:", user)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
utils/__init__.py
Normal file
7
utils/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .code_parser import parse_code_structure, detect_language, extract_code_blocks
|
||||
|
||||
__all__ = [
|
||||
"parse_code_structure",
|
||||
"detect_language",
|
||||
"extract_code_blocks"
|
||||
]
|
||||
BIN
utils/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/code_parser.cpython-313.pyc
Normal file
BIN
utils/__pycache__/code_parser.cpython-313.pyc
Normal file
Binary file not shown.
32
utils/code_parser.py
Normal file
32
utils/code_parser.py
Normal file
@ -0,0 +1,32 @@
|
||||
from typing import List
|
||||
|
||||
def parse_code_structure(code: str) -> dict:
|
||||
"""解析代码结构,返回基本信息"""
|
||||
lines = code.split('\n')
|
||||
return {
|
||||
"total_lines": len(lines),
|
||||
"non_empty_lines": len([l for l in lines if l.strip()]),
|
||||
"code_lines": lines
|
||||
}
|
||||
|
||||
def detect_language(code: str) -> str:
|
||||
"""简单检测代码语言"""
|
||||
code_stripped = code.strip()
|
||||
|
||||
if code_stripped.startswith(('def ', 'import ', 'from ', 'class ', 'if __name__')):
|
||||
return 'python'
|
||||
elif 'function' in code or 'const' in code or 'let' in code or '=>' in code:
|
||||
return 'javascript'
|
||||
elif 'public class' in code or 'private void' in code:
|
||||
return 'java'
|
||||
elif 'std::' in code or '#include <iostream>' in code:
|
||||
return 'cpp'
|
||||
|
||||
return 'unknown'
|
||||
|
||||
def extract_code_blocks(text: str) -> List[str]:
|
||||
"""从文本中提取代码块"""
|
||||
import re
|
||||
pattern = r'```[\w]*\n([\s\S]*?)```'
|
||||
matches = re.findall(pattern, text)
|
||||
return matches if matches else []
|
||||
Loading…
Reference in New Issue
Block a user