Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,177 +1,108 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
-
CodeGPS Pro
|
|
|
|
| 5 |
"""
|
| 6 |
-
import gradio as gr,
|
| 7 |
-
from dataclasses import dataclass
|
| 8 |
-
from typing import List, Dict
|
| 9 |
|
| 10 |
-
#
|
| 11 |
-
#
|
| 12 |
-
#
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
def load(self,code:str)->str:
|
| 22 |
-
if not code.strip(): self.reset(); return "⚠️ 代码为空"
|
| 23 |
-
self.lines=code.split("\n"); self.language=self._detect_lang(code)
|
| 24 |
-
return f"✅ {self.language.upper()} | {len(self.lines)} 行"
|
| 25 |
-
def _detect_lang(self,code):
|
| 26 |
-
pats={'python':[r'def\s','import\s',r':\s*$'],
|
| 27 |
-
'html':[r'<html',r'<div',r'</\w+>'],
|
| 28 |
-
'javascript':[r'function\s',r'=>',r'const\s+']}
|
| 29 |
-
for l,ps in pats.items():
|
| 30 |
-
if sum(bool(re.search(p,code)) for p in ps)>=2:return l
|
| 31 |
-
return 'plaintext'
|
| 32 |
-
def get_code(self): return "\n".join(self.lines)
|
| 33 |
-
def replace_range(self,s,e,new)->Dict:
|
| 34 |
-
if not(1<=s<=len(self.lines) and 1<=e<=len(self.lines)):
|
| 35 |
-
return {'ok':False,'msg':'⚠️ 行号越界'}
|
| 36 |
-
self.history_stack.append(self.lines.copy())
|
| 37 |
-
old=self.lines[s-1:e]
|
| 38 |
-
self.lines[s-1:e]=new.split("\n")
|
| 39 |
-
return {'ok':True,'msg':f'✅ 修改 {s}-{e} 行','old':old,'new':new.split("\n"),'s':s,'e':e}
|
| 40 |
def undo(self):
|
| 41 |
-
if not self.
|
| 42 |
-
self.lines=self.
|
| 43 |
-
def
|
| 44 |
-
code_lines="\n".join(f"{i+1:4d}|{l}" for i,l in enumerate(self.lines))
|
| 45 |
-
return f"语言:{self.language}\n```{self.language}\n{code_lines}\n```\n任务:{task}"
|
| 46 |
-
|
| 47 |
-
# ============================================================
|
| 48 |
-
# -------------------- DeepSeek API 封装 ----------------------
|
| 49 |
-
# ============================================================
|
| 50 |
-
CONF=os.path.expanduser("~/.codegps_conf.json")
|
| 51 |
-
def load_conf():
|
| 52 |
-
if os.path.exists(CONF): return json.load(open(CONF))
|
| 53 |
-
return {"api_key":"","model":"deepseek-chat"}
|
| 54 |
-
def save_conf(c): json.dump(c,open(CONF,"w"),ensure_ascii=False,indent=2)
|
| 55 |
|
| 56 |
-
|
| 57 |
-
url="https://api.deepseek.com/chat/completions"
|
| 58 |
-
sys_prompt=("你是代码修复助手。请只输出JSON补丁,示例:"
|
| 59 |
-
'{"type":"replace","start":行号_int,"end":行号_int,"new_code":"替换内容"},'
|
| 60 |
-
"不要包含多余解释。")
|
| 61 |
-
task_prompt=f"{prompt}\n用户指定修复范围:{start}-{end}行"
|
| 62 |
-
data={"model":model,
|
| 63 |
-
"messages":[{"role":"system","content":sys_prompt},
|
| 64 |
-
{"role":"user","content":task_prompt}],
|
| 65 |
-
"stream":False}
|
| 66 |
-
r=requests.post(url,json=data,
|
| 67 |
-
headers={"Authorization":f"Bearer {key}",
|
| 68 |
-
"Content-Type":"application/json"})
|
| 69 |
-
try: return r.json()["choices"][0]["message"]["content"]
|
| 70 |
-
except Exception: return f"API 返回异常:{r.text}"
|
| 71 |
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
|
|
|
| 79 |
|
| 80 |
-
#
|
| 81 |
-
#
|
| 82 |
-
#
|
| 83 |
-
def
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
for
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
| 91 |
|
| 92 |
-
#
|
| 93 |
-
#
|
| 94 |
-
#
|
| 95 |
-
def
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
with gr.Blocks(title="CodeGPS Pro — DeepSeek AI 补丁修复",theme=gr.themes.Soft()) as app:
|
| 99 |
-
gr.Markdown("## 🎯 CodeGPS Pro — DeepSeek AI 补丁修复 & 聊天版")
|
| 100 |
|
| 101 |
-
#
|
| 102 |
-
|
| 103 |
-
api=gr.Textbox(label="API KEY",value=conf['api_key'],type="password")
|
| 104 |
-
model=gr.Textbox(label="模型名",value=conf['model'])
|
| 105 |
-
save_btn=gr.Button("保存配置"); status=gr.Textbox(label="状态",interactive=False)
|
| 106 |
-
def save_conf_ui(k,m): save_conf({"api_key":k,"model":m}); return "✅ 已保存"
|
| 107 |
-
save_btn.click(save_conf_ui,[api,model],[status])
|
| 108 |
|
| 109 |
with gr.Row():
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
if "<html" not in c.lower(): return "<div style='color:red'>非 HTML 无法预览</div>"
|
| 128 |
-
path="/tmp/preview.html"; open(path,"w",encoding="utf-8").write(c)
|
| 129 |
-
return f"<iframe src='file://{path}' style='width:100%;height:90vh;border:none;'></iframe>"
|
| 130 |
-
run_html.click(run_html_fn,[code],[html_out])
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
task=gr.Textbox(label="任务说明",lines=4,placeholder="示例: 优化第 行循环性能")
|
| 136 |
-
gen_btn=gr.Button("🩹 生成补丁")
|
| 137 |
-
diff_card=gr.HTML(visible=False)
|
| 138 |
-
with gr.Row(visible=False) as confirm_row:
|
| 139 |
-
confirm_btn=gr.Button("✅ 应用补丁")
|
| 140 |
-
cancel_btn=gr.Button("❌ 取消")
|
| 141 |
-
def gen_patch(tasktxt,start,end):
|
| 142 |
-
cf=load_conf()
|
| 143 |
-
resp=call_patch_ai(eng.generate_prompt(tasktxt),cf['model'],cf['api_key'],start,end)
|
| 144 |
-
try: patch=json.loads(resp)
|
| 145 |
-
except Exception:
|
| 146 |
-
return gr.update(value=f"<pre style='color:red'>{resp}</pre>",visible=True),gr.update(visible=False)
|
| 147 |
-
s,e=int(patch.get("start",start)),int(patch.get("end",end))
|
| 148 |
-
new=patch.get("new_code","")
|
| 149 |
-
eng.pending_patch={'s':s,'e':e,'new':new}
|
| 150 |
-
html=make_diff(eng.lines[s-1:e],new.split("\n"),s,e)
|
| 151 |
-
return gr.update(value=html,visible=True),gr.update(visible=True)
|
| 152 |
-
gen_btn.click(gen_patch,[task,start_line,end_line],[diff_card,confirm_row])
|
| 153 |
-
def apply_patch():
|
| 154 |
-
p=eng.pending_patch; r=eng.replace_range(p['s'],p['e'],p['new']); eng.pending_patch=None
|
| 155 |
-
return gr.update(value=eng.get_code()),gr.update(value="<div style='color:green'>✅ 已应用</div>",visible=False),gr.update(visible=False)
|
| 156 |
-
confirm_btn.click(apply_patch,[],[code,diff_card,confirm_row])
|
| 157 |
-
cancel_btn.click(lambda :[gr.update(visible=False),gr.update(visible=False)],[ ],[diff_card,confirm_row])
|
| 158 |
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
|
|
|
|
|
|
| 173 |
return app
|
| 174 |
|
| 175 |
if __name__=="__main__":
|
| 176 |
-
|
| 177 |
-
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
+
CodeGPS Pro — DeepSeek AI 测试安全版
|
| 5 |
+
不在磁盘保存任何凭证;关闭或点击“清除凭证”即忘记。
|
| 6 |
"""
|
| 7 |
+
import gradio as gr, json, re, difflib
|
|
|
|
|
|
|
| 8 |
|
| 9 |
+
# ------------------------------------------------------------
|
| 10 |
+
# 极简代码引擎
|
| 11 |
+
# ------------------------------------------------------------
|
| 12 |
+
class Engine:
|
| 13 |
+
def __init__(self): self.lines=[]; self.history=[]
|
| 14 |
+
def load(self,code):
|
| 15 |
+
self.lines=code.split('\n');return f"✅ 载入 {len(self.lines)} 行"
|
| 16 |
+
def replace_range(self,s,e,new):
|
| 17 |
+
self.history.append(self.lines.copy())
|
| 18 |
+
old=self.lines[s-1:e]; self.lines[s-1:e]=new.split('\n')
|
| 19 |
+
return old
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
def undo(self):
|
| 21 |
+
if not self.history:return"无历史"
|
| 22 |
+
self.lines=self.history.pop();return"✅ 撤销"
|
| 23 |
+
def get_code(self): return '\n'.join(self.lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
eng=Engine()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
# ------------------------------------------------------------
|
| 28 |
+
# 虚拟 AI 调用:你这替换为真实 DeepSeek API 请求
|
| 29 |
+
# ------------------------------------------------------------
|
| 30 |
+
def call_ai_patch(api_key, model, code, task):
|
| 31 |
+
"""这里应调用真实接口;返回 JSON patch 字符串"""
|
| 32 |
+
# mock:演示只改前两行
|
| 33 |
+
fake=[{"start":1,"end":2,"new_code":"print('AI已修复')"}]
|
| 34 |
+
return json.dumps(fake,ensure_ascii=False)
|
| 35 |
|
| 36 |
+
# ------------------------------------------------------------
|
| 37 |
+
# diff 生成
|
| 38 |
+
# ------------------------------------------------------------
|
| 39 |
+
def diff_html(old,new,s,e):
|
| 40 |
+
out=['<pre style="background:#fff;border:1px solid #ccc;'
|
| 41 |
+
'padding:8px;height:300px;overflow:auto;">']
|
| 42 |
+
for l in difflib.unified_diff(old,new,fromfile=f"旧({s}-{e})",
|
| 43 |
+
tofile="新",lineterm=""):
|
| 44 |
+
c='red'if l.startswith('-')else'green'if l.startswith('+')else'#000'
|
| 45 |
+
out.append(f'<span style="color:{c}">{l}</span>')
|
| 46 |
+
out.append('</pre>')
|
| 47 |
+
return ''.join(out)
|
| 48 |
|
| 49 |
+
# ------------------------------------------------------------
|
| 50 |
+
# UI
|
| 51 |
+
# ------------------------------------------------------------
|
| 52 |
+
def make_ui():
|
| 53 |
+
with gr.Blocks(title="CodeGPS Pro — 安全测试版") as app:
|
| 54 |
+
gr.Markdown("## 👁 CodeGPS Pro — DeepSeek AI 视觉补丁安全版")
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
# 仅内存保存的 Key / Model
|
| 57 |
+
creds = gr.State({"api_key":"","model":""})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
with gr.Row():
|
| 60 |
+
code=gr.Code(label="编辑器",language="python",lines=25)
|
| 61 |
+
chat=gr.Chatbot(label="AI 日志")
|
| 62 |
+
|
| 63 |
+
with gr.Accordion("⚙️ 临时凭证设置",open=False):
|
| 64 |
+
api_in=gr.Textbox(label="API Key",type="password")
|
| 65 |
+
model_in=gr.Textbox(label="模型名",value="deepseek-chat")
|
| 66 |
+
save_btn=gr.Button("保存临时凭证")
|
| 67 |
+
clear_btn=gr.Button("🔐 清除凭证")
|
| 68 |
+
out_info=gr.Textbox(label="状态",interactive=False)
|
| 69 |
+
|
| 70 |
+
# 存入内存状态
|
| 71 |
+
def save_creds(api,model):
|
| 72 |
+
return {"api_key":api,"model":model},f"✅ 凭证加载于内存(退出即清除)"
|
| 73 |
+
def clear_creds():
|
| 74 |
+
return {"api_key":"","model":""},"🔐 已清除内存凭证"
|
| 75 |
+
save_btn.click(save_creds,[api_in,model_in],[creds,out_info])
|
| 76 |
+
clear_btn.click(clear_creds,[],[creds,out_info])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
task=gr.Textbox(label="任务描述",placeholder="讲清要修的部分",lines=2)
|
| 79 |
+
load_btn=gr.Button("🚀 加载并自动修复")
|
| 80 |
+
diffview=gr.HTML()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
+
def auto_fix(code_text,task_text,cred,chat_hist):
|
| 83 |
+
status=eng.load(code_text)
|
| 84 |
+
if not cred["api_key"]:
|
| 85 |
+
chat_hist.append(("系统","⚠️ 未设置API Key"))
|
| 86 |
+
return code_text,chat_hist,"未设置Key",""
|
| 87 |
+
resp=call_ai_patch(cred["api_key"],cred["model"],code_text,task_text)
|
| 88 |
+
try:
|
| 89 |
+
patches=json.loads(resp)
|
| 90 |
+
for p in sorted(patches,key=lambda x:x["start"],reverse=True):
|
| 91 |
+
old=eng.replace_range(p["start"],p["end"],p["new_code"])
|
| 92 |
+
html=diff_html(old,p["new_code"].split("\n"),p["start"],p["end"])
|
| 93 |
+
chat_hist.append(("AI",f"🩹 已自动修复 {len(patches)} 处"))
|
| 94 |
+
return eng.get_code(),chat_hist,f"{status}|AI 修复完成",html
|
| 95 |
+
except Exception as e:
|
| 96 |
+
chat_hist.append(("系统",f"⚠️ AI输出错误 {e}\n{resp}"))
|
| 97 |
+
return code_text,chat_hist,"出错",""
|
| 98 |
+
load_btn.click(auto_fix,
|
| 99 |
+
inputs=[code,task,creds,chat],
|
| 100 |
+
outputs=[code,chat,out_info,diffview])
|
| 101 |
|
| 102 |
+
undo=gr.Button("↩️ 撤销")
|
| 103 |
+
undo.click(lambda:[eng.undo(),eng.get_code()],[ ],[out_info,code])
|
| 104 |
return app
|
| 105 |
|
| 106 |
if __name__=="__main__":
|
| 107 |
+
ui=make_ui()
|
| 108 |
+
ui.launch(server_port=7860)
|