mirror of
http://hblu.top:3000/st2411020106/111.git
synced 2026-01-27 22:43:24 +08:00
353 lines
13 KiB
Python
353 lines
13 KiB
Python
|
|
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()
|