fiel1986 commited on
Commit
1877ea4
·
verified ·
1 Parent(s): 603f1ed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -115
app.py CHANGED
@@ -1,188 +1,246 @@
1
- import os
2
- import re
3
  import gradio as gr
4
  import spaces
5
- import tempfile
6
 
7
  HF_TOKEN = os.getenv("HF_TOKEN")
8
  MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
 
9
 
10
  BLOQUEOS = ["ignora", "olvida tus reglas", "actua como", "eres ahora", "receta", "futbol", "politica", "presidente", "cocina", "chiste", "poema"]
11
- PALABRAS_PROG = ["codigo", "código", "python", "javascript", "html", "css", "bug", "error", "api", "programar", "script", "sql", "git"]
12
-
13
- SYSTEM_PROMPT_BASE = """Eres Aura, asistente de programación. Tu ÚNICA función es programar.
14
- REGLA ABSOLUTA: Si la pregunta NO es 100% sobre programación, responde EXACTAMENTE:
15
- Soy Aura, asistente especializado exclusivamente en programación. Solo puedo ayudarte con código. ¿Qué necesitas programar hoy?
16
- Nunca hables de cocina, política, chistes, fútbol, recetas.
17
- Si tienes MEMORIA USUARIO, úsala para personalizar. Si sabes el nombre, saluda por nombre.
 
 
 
 
 
 
 
18
  """
19
 
 
20
  def extract_file_text(file):
21
- if file is None: return ""
22
  try:
23
  path = file.name if hasattr(file, 'name') else file
24
  with open(path, 'r', encoding='utf-8', errors='ignore') as f:
25
  return f.read()[:20000]
26
- except Exception as e: return f"Error: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  @spaces.GPU
29
- def chat_response(message, history, system_user_custom, max_tokens, temperature, top_p, user_token, file_content, file_name, memoria_usuario):
30
  t_lower = message.lower()
31
-
32
- # --- DETECTAR NOMBRE AUTO ---
33
  memoria_actualizada = memoria_usuario
34
  m = re.search(r'(?:me llamo|soy|mi nombre es)\s+([A-ZÁÉÍÓÚa-z]+)', message, re.IGNORECASE)
35
  if m:
36
- nombre = m.group(1).strip().capitalize()
37
  if f"Me llamo {nombre}" not in memoria_usuario:
38
  memoria_actualizada = (memoria_usuario + f"\n- Me llamo {nombre}").strip()
39
 
40
- if any(b in t_lower for b in BLOQUEOS):
41
- if not any(k in t_lower for k in PALABRAS_PROG):
42
- history.append({"role": "user", "content": message})
43
- history.append({"role": "assistant", "content": "Soy Aura, asistente especializado exclusivamente en programación. Solo puedo ayudarte con código. ¿Qué necesitas programar hoy?"})
44
- yield history, gr.File(visible=False), gr.File(visible=False), memoria_actualizada
45
- return
 
 
 
 
 
46
 
47
  token = user_token if user_token else HF_TOKEN
48
  if not token:
49
- history.append({"role": "assistant", "content": "⚠️ No hay token HF"})
50
- yield history, gr.File(visible=False), gr.File(visible=False), memoria_actualizada
51
  return
52
 
53
  from huggingface_hub import InferenceClient
54
  client = InferenceClient(token=token, model=MODEL_ID)
55
 
56
- full_system = SYSTEM_PROMPT_BASE + f"\n{system_user_custom}"
57
- if memoria_actualizada.strip():
58
- full_system += f"\n--- MEMORIA USUARIO (localStorage) ---\n{memoria_actualizada}\nSi está vacío, pregunta el nombre."
59
- else:
60
- full_system += "\nMEMORIA VACIA: Pregunta '¿Cómo te llamas?' para personalizar."
61
 
62
- final_msg = f"Archivo [{file_name}]:\n{file_content}\n\nPregunta: {message}" if file_content else message
 
 
63
 
64
- messages = [{"role": "system", "content": full_system}]
65
- messages.extend(history)
66
- messages.append({"role": "user", "content": final_msg})
67
 
68
- response_text = ""
69
  for chunk in client.chat_completion(messages, max_tokens=int(max_tokens), stream=True, temperature=temperature, top_p=top_p):
70
  if chunk.choices and chunk.choices[0].delta.content:
71
- response_text += chunk.choices[0].delta.content
72
- new_hist = history + [{"role": "user", "content": message}, {"role": "assistant", "content": response_text}]
73
- yield new_hist, gr.File(visible=False), gr.File(visible=False), memoria_actualizada
74
-
75
- code_blocks = re.findall(r"```(\w+)?\n(.*?)```", response_text, re.DOTALL)
76
- code_file = None
77
- if code_blocks:
78
- lang, code = code_blocks[0]
79
- ext = {"py":"py","python":"py","html":"html","js":"js","javascript":"js","css":"css"}.get((lang or "").lower(),"txt")
80
- fd, path = tempfile.mkstemp(suffix=f".{ext}")
81
- with os.fdopen(fd, 'w', encoding='utf-8') as f: f.write(code)
82
- code_file = path
83
-
84
- fd2, txt_path = tempfile.mkstemp(suffix=".txt")
85
- with os.fdopen(fd2, 'w', encoding='utf-8') as f: f.write(response_text)
86
-
87
- final_hist = history + [{"role": "user", "content": message}, {"role": "assistant", "content": response_text}]
88
- if code_file:
89
- yield final_hist, gr.File(value=code_file, visible=True), gr.File(value=txt_path, visible=True), memoria_actualizada
90
- else:
91
- yield final_hist, gr.File(visible=False), gr.File(value=txt_path, visible=True), memoria_actualizada
92
-
93
- # --- JS FUSIONADO: GHOST + MEMORIA ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  ghost_js = """
95
  function(){
96
- const KEY_FRASES='aura_frases';
97
- const KEY_MEM='aura_memoria_prog';
98
  const get=()=>JSON.parse(localStorage.getItem(KEY_FRASES)||'[]');
99
  const save=(t)=>{t=t.trim();if(t.length<6)return;let a=get().filter(x=>x!==t);a.unshift(t);localStorage.setItem(KEY_FRASES,JSON.stringify(a.slice(0,100)));};
100
-
101
- function loadMem(){
102
- const mem = localStorage.getItem(KEY_MEM) || '';
103
- const h = document.getElementById('hidden-memoria-prog');
104
- const v = document.getElementById('visible-memoria-prog');
105
- if(h && h.value!== mem){ h.value = mem; h.dispatchEvent(new Event('input',{bubbles:true})); }
106
- if(v && document.activeElement!==v && v.value!== mem){ v.value = mem; }
107
- }
108
- setInterval(loadMem, 800);
109
-
110
- function attach(){
111
- const input=document.querySelector('textarea[placeholder*="programación"]');
112
- if(!input){setTimeout(attach,1000);return;}
113
- let ghost=document.getElementById('ghost-text');
114
- if(!ghost){
115
- ghost=document.createElement('div');
116
- ghost.id='ghost-text';
117
- ghost.style.cssText='position:absolute;left:14px;top:12px;color:rgba(130,130,130,0.45);pointer-events:none;font-size:16px;white-space:pre-wrap;z-index:2;';
118
- input.parentElement.style.position='relative';
119
- input.parentElement.appendChild(ghost);
120
- }
121
- let sug='';
122
- input.addEventListener('input',()=>{
123
- const v=input.value;
124
- if(!v){ghost.textContent='';sug='';return;}
125
- const m=get().find(f=>f.toLowerCase().startsWith(v.toLowerCase())&&f.toLowerCase()!==v.toLowerCase());
126
- if(m){sug=m;ghost.textContent=m;}else{ghost.textContent='';sug='';}
127
- });
128
- input.addEventListener('keydown',(e)=>{
129
- if((e.key==='Tab'||e.key==='ArrowRight')&&sug){e.preventDefault();input.value=sug;input.dispatchEvent(new Event('input',{bubbles:true}));ghost.textContent='';sug='';}
130
- if(e.key==='Enter'&&!e.shiftKey&&input.value)save(input.value);
131
- });
132
- }
133
- setTimeout(attach,2000);
134
- window.addEventListener('load', loadMem);
135
  }
136
  """
137
 
138
  with gr.Blocks() as demo:
139
- gr.Markdown("**Aura - Asistente de Programación + Memoria**")
140
  hidden_memoria = gr.Textbox(value="", visible=False, elem_id="hidden-memoria-prog")
 
 
 
141
 
142
  with gr.Accordion("⚙️ Configuración + Memoria", open=False):
143
- visible_memoria = gr.Textbox(label="🧠 Memoria privada (se guarda en tu navegador)", lines=3, placeholder="Ej:\n- Me llamo Carlos\n- Trabajo con React\n- Prefiero Python", elem_id="visible-memoria-prog")
144
  with gr.Row():
145
- btn_save_mem = gr.Button("💾 Guardar Memoria")
146
- btn_clear_mem = gr.Button("🗑️ Borrar Memoria")
147
- token_input = gr.Textbox(label="Token HF", type="password", placeholder="Opcional")
148
- system_input = gr.Textbox(value="Respuestas concisas en Markdown.", label="Estilo", lines=2)
149
  with gr.Row():
150
- max_tokens_input = gr.Slider(1, 4096, value=512, label="Max Tokens")
151
- temp_input = gr.Slider(0.1, 2.0, value=0.7, label="Temp")
152
  top_p_input = gr.Slider(0.1, 1.0, value=0.95, label="Top-p")
153
 
154
  with gr.Row():
155
- file_upload = gr.File(label="📁 Sube.txt.py.html.js", file_types=[".txt",".py",".html",".js",".css",".json",".md"], type="filepath")
156
- file_status = gr.Textbox(label="Archivo", interactive=False)
 
157
 
158
  file_content_state = gr.State("")
159
  file_name_state = gr.State("")
160
- chatbot = gr.Chatbot(label="Chat", height=500)
161
  with gr.Row():
162
- chat_input = gr.Textbox(placeholder="Solo programación... TAB para autocompletar", scale=4)
163
  send_btn = gr.Button("Enviar", variant="primary")
164
- clear_btn = gr.Button("🗑️ Chat")
165
 
166
  with gr.Row():
167
- download_code = gr.File(label="📥 Descargar código", visible=False)
168
- download_txt = gr.File(label="📥 Descargar.txt", visible=False)
 
 
 
169
 
170
  def on_file_upload(file):
171
  if file is None: return "", "", "Ningún archivo"
172
  content = extract_file_text(file)
173
  name = os.path.basename(file.name)
174
- return content, name, f"✅ {name} - {len(content)} chars"
175
 
176
- file_upload.change(on_file_upload, inputs=[file_upload], outputs=[file_content_state, file_name_state, file_status])
 
 
 
177
 
178
- chat_input.submit(fn=chat_response, inputs=[chat_input, chatbot, system_input, max_tokens_input, temp_input, top_p_input, token_input, file_content_state, file_name_state, hidden_memoria], outputs=[chatbot, download_code, download_txt, hidden_memoria]).then(lambda: "", None, chat_input)
179
- send_btn.click(fn=chat_response, inputs=[chat_input, chatbot, system_input, max_tokens_input, temp_input, top_p_input, token_input, file_content_state, file_name_state, hidden_memoria], outputs=[chatbot, download_code, download_txt, hidden_memoria]).then(lambda: "", None, chat_input)
180
  clear_btn.click(lambda: [], None, chatbot)
181
 
182
- # Memoria -> localStorage
183
  visible_memoria.change(fn=lambda x: x, inputs=visible_memoria, outputs=hidden_memoria)
184
  hidden_memoria.change(fn=None, inputs=hidden_memoria, outputs=visible_memoria, js="(v)=>{ localStorage.setItem('aura_memoria_prog', v); return v; }")
185
- btn_save_mem.click(fn=None, inputs=visible_memoria, outputs=visible_memoria, js="(v)=>{ localStorage.setItem('aura_memoria_prog', v); alert('Memoria guardada'); return v; }")
186
  btn_clear_mem.click(fn=None, inputs=[], outputs=visible_memoria, js="()=>{ localStorage.removeItem('aura_memoria_prog'); return ''; }")
187
 
188
  demo.queue().launch(theme=gr.themes.Soft(), js=ghost_js)
 
1
+ import os, re, ast, tempfile, io, sys, traceback, contextlib
2
+ from html.parser import HTMLParser
3
  import gradio as gr
4
  import spaces
 
5
 
6
  HF_TOKEN = os.getenv("HF_TOKEN")
7
  MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
8
+ MAX_AUTOFIX = 3 # cuantas veces se auto-corrige sola
9
 
10
  BLOQUEOS = ["ignora", "olvida tus reglas", "actua como", "eres ahora", "receta", "futbol", "politica", "presidente", "cocina", "chiste", "poema"]
11
+ PALABRAS_PROG = ["codigo", "código", "python", "javascript", "html", "css", "bug", "error", "api", "programar", "script", "sql", "git", "revisa", "corrige", "conect", "corre", "ejecuta"]
12
+
13
+ SYSTEM_PROMPT_BASE = """Eres Aura, asistente senior que REVISA, CORRIGE Y AUTO-REPARA código hasta que ejecute.
14
+ Si te doy un error de ejecución, corrige el código COMPLETO inmediatamente.
15
+ Tu ÚNICA función es programar, revisar, debuggear y corregir código.
16
+ FLUJO OBLIGATORIO:
17
+ 1. Analiza ERRORES DETECTADOS que te paso.
18
+ 2. Responde con:
19
+ 🔍 Errores encontrados:
20
+ - lista
21
+ ✅ Código corregido: en bloque de código completo
22
+ 📝 Qué corregiste: explicación breve
23
+ Si NO es programación, responde: Soy Aura, asistente especializado exclusivamente en programación.
24
+ Usa MEMORIA USUARIO para personalizar.
25
  """
26
 
27
+
28
  def extract_file_text(file):
29
+ if not file: return ""
30
  try:
31
  path = file.name if hasattr(file, 'name') else file
32
  with open(path, 'r', encoding='utf-8', errors='ignore') as f:
33
  return f.read()[:20000]
34
+ except: return ""
35
+
36
+ def detectar_errores_python(codigo, nombre="archivo.py"):
37
+ try: ast.parse(codigo); return []
38
+ except SyntaxError as e: return [f"🔴 SyntaxError línea {e.lineno}: {e.msg}"]
39
+
40
+ def detectar_errores_js(codigo, nombre="archivo.js"):
41
+ e=[]
42
+ if codigo.count("{")!= codigo.count("}"): e.append(f"🔴 JS: Llaves desbalanceadas")
43
+ if codigo.count("(")!= codigo.count(")"): e.append(f"🔴 JS: Paréntesis desbalanceados")
44
+ return e
45
+
46
+ class MyHTMLChecker(HTMLParser):
47
+ def __init__(self):
48
+ super().__init__(); self.stack=[]; self.errores=[]; self.void_tags={"br","hr","img","input","meta","link","area","base","col","embed","source","track","wbr"}
49
+ def handle_starttag(self, tag, attrs):
50
+ if tag not in self.void_tags: self.stack.append(tag)
51
+ def handle_endtag(self, tag):
52
+ if not self.stack: self.errores.append(f"🔴 HTML: </{tag}> sin apertura")
53
+ elif self.stack and self.stack[-1]!= tag: self.errores.append(f"🔴 HTML: Esperaba </{self.stack[-1]}> vino </{tag}>")
54
+ else: self.stack.pop() if self.stack else None
55
+
56
+ def detectar_errores_html(codigo, nombre="index.html"):
57
+ checker = MyHTMLChecker()
58
+ try: checker.feed(codigo)
59
+ except Exception as e: checker.errores.append(f"🔴 HTML: {e}")
60
+ for tag in checker.stack: checker.errores.append(f"🔴 HTML: <{tag}> sin cerrar")
61
+ return checker.errores
62
+
63
+ def analizar_todo(codigo, nombre):
64
+ n=nombre.lower()
65
+ if n.endswith(".py"): return detectar_errores_python(codigo,n)
66
+ if n.endswith(".js"): return detectar_errores_js(codigo,n)
67
+ if n.endswith(".html"): return detectar_errores_html(codigo,n)
68
+ return detectar_errores_python(codigo,n)
69
+
70
+ def ejecutar_python(codigo):
71
+ buffer = io.StringIO()
72
+ try:
73
+ with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(buffer):
74
+ exec(codigo, {"__name__":"__main__"})
75
+ return True, f"✅ EJECUCIÓN OK\n\nSalida:\n{buffer.getvalue() or '(sin salida, pero sin errores)'}"
76
+ except Exception:
77
+ return False, f"❌ ERROR AL EJECUTAR:\n{buffer.getvalue()}\n{traceback.format_exc()}"
78
 
79
  @spaces.GPU
80
+ def chat_response(message, history, system_user_custom, max_tokens, temperature, top_p, user_token, file_content, file_name, memoria_usuario, carpeta_archivos):
81
  t_lower = message.lower()
 
 
82
  memoria_actualizada = memoria_usuario
83
  m = re.search(r'(?:me llamo|soy|mi nombre es)\s+([A-ZÁÉÍÓÚa-z]+)', message, re.IGNORECASE)
84
  if m:
85
+ nombre = m.group(1).capitalize()
86
  if f"Me llamo {nombre}" not in memoria_usuario:
87
  memoria_actualizada = (memoria_usuario + f"\n- Me llamo {nombre}").strip()
88
 
89
+ codigo_a_revisar = file_content
90
+ nombre_a_revisar = file_name or "archivo.py"
91
+
92
+ errores_detectados = ""
93
+ if codigo_a_revisar:
94
+ errs = analizar_todo(codigo_a_revisar, nombre_a_revisar)
95
+ if errs: errores_detectados = "\n".join(errs)
96
+
97
+ if any(b in t_lower for b in BLOQUEOS) and not any(k in t_lower for k in PALABRAS_PROG):
98
+ yield history + [{"role":"user","content":message},{"role":"assistant","content":"Soy Aura, solo programación."}], gr.File(visible=False), gr.File(visible=False), "Bloqueado", memoria_actualizada
99
+ return
100
 
101
  token = user_token if user_token else HF_TOKEN
102
  if not token:
103
+ yield history + [{"role":"user","content":message},{"role":"assistant","content":"⚠️ Falta HF_TOKEN"}], gr.File(visible=False), gr.File(visible=False), "Falta token", memoria_actualizada
 
104
  return
105
 
106
  from huggingface_hub import InferenceClient
107
  client = InferenceClient(token=token, model=MODEL_ID)
108
 
109
+ full_system = SYSTEM_PROMPT_BASE + f"\n{system_user_custom}\n"
110
+ if memoria_actualizada.strip(): full_system+=f"MEMORIA: {memoria_actualizada}\n"
111
+ if errores_detectados: full_system+=f"ERRORES LOCALES: {errores_detectados}\nCorrige completo.\n"
112
+ if carpeta_archivos: full_system+=f"CARPETA: {carpeta_archivos.replace('|||', ', ')}\n"
 
113
 
114
+ final_msg = message
115
+ if codigo_a_revisar:
116
+ final_msg = f"Archivo [{nombre_a_revisar}]:\n```\n{codigo_a_revisar[:12000]}\n```\nPetición: {message}\nDame código corregido COMPLETO."
117
 
118
+ messages = [{"role":"system","content":full_system}] + history + [{"role":"user","content":final_msg}]
 
 
119
 
120
+ response_text=""
121
  for chunk in client.chat_completion(messages, max_tokens=int(max_tokens), stream=True, temperature=temperature, top_p=top_p):
122
  if chunk.choices and chunk.choices[0].delta.content:
123
+ response_text+=chunk.choices[0].delta.content
124
+ yield history + [{"role":"user","content":message},{"role":"assistant","content":response_text}], gr.File(visible=False), gr.File(visible=False), "Generando...", memoria_actualizada
125
+
126
+ # AUTO-FIX LOOP
127
+ code_actual = ""
128
+ code_blocks = re.findall(r"```(?:\w+)?\n(.*?)```", response_text, re.DOTALL)
129
+ if code_blocks: code_actual = max(code_blocks, key=len)
130
+
131
+ ejecucion_final = "No es Python."
132
+ intento = 0
133
+ if code_actual and nombre_a_revisar.endswith(".py"):
134
+ ok, log = ejecutar_python(code_actual)
135
+ ejecucion_final = f"Intento 1:\n{log}"
136
+ yield history + [{"role":"user","content":message},{"role":"assistant","content":response_text}], gr.File(visible=False), gr.File(visible=False), ejecucion_final, memoria_actualizada
137
+
138
+ while not ok and intento < MAX_AUTOFIX:
139
+ intento+=1
140
+ fix_prompt = f"El código anterior FALLÓ al ejecutar con este error:\n{log}\n\nCódigo que falló:\n```python\n{code_actual}\n```\nCorrige el error y dame el código completo corregido, ejecutable, sin explicaciones extra."
141
+ fix_messages = [{"role":"system","content":SYSTEM_PROMPT_BASE}] + [{"role":"user","content":fix_prompt}]
142
+
143
+ fix_text=""
144
+ for chunk in client.chat_completion(fix_messages, max_tokens=int(max_tokens), stream=True, temperature=0.2, top_p=0.95):
145
+ if chunk.choices and chunk.choices[0].delta.content:
146
+ fix_text+=chunk.choices[0].delta.content
147
+
148
+ new_blocks = re.findall(r"```(?:\w+)?\n(.*?)```", fix_text, re.DOTALL)
149
+ if new_blocks: code_actual = max(new_blocks, key=len)
150
+ else: code_actual = fix_text
151
+
152
+ ok, log = ejecutar_python(code_actual)
153
+ ejecucion_final += f"\n\n--- Intento {intento+1} (Auto-fix) ---\n{log}"
154
+ response_text += f"\n\n🔄 **Auto-fix intento {intento+1}:**\n```python\n{code_actual[:2000]}\n```\n{log}"
155
+ yield history + [{"role":"user","content":message},{"role":"assistant","content":response_text}], gr.File(visible=False), gr.File(visible=False), ejecucion_final, memoria_actualizada
156
+
157
+ # archivo final corregido
158
+ code_file=None
159
+ if code_actual:
160
+ ext = "py"
161
+ fd, path = tempfile.mkstemp(suffix=f"_FINAL_CORREGIDO.{ext}")
162
+ with os.fdopen(fd, 'w', encoding='utf-8') as f: f.write(code_actual)
163
+ code_file=path
164
+
165
+ fd2, txt_path = tempfile.mkstemp(suffix="_log.txt")
166
+ with os.fdopen(fd2, 'w', encoding='utf-8') as f: f.write(response_text + "\n\n" + ejecucion_final)
167
+
168
+ final_hist = history + [{"role":"user","content":message},{"role":"assistant","content":response_text}]
169
+ if code_file: yield final_hist, gr.File(value=code_file, visible=True), gr.File(value=txt_path, visible=True), ejecucion_final, memoria_actualizada
170
+ else: yield final_hist, gr.File(visible=False), gr.File(value=txt_path, visible=True), ejecucion_final, memoria_actualizada
171
+
172
  ghost_js = """
173
  function(){
174
+ const KEY_FRASES='aura_frases'; const KEY_MEM='aura_memoria_prog';
 
175
  const get=()=>JSON.parse(localStorage.getItem(KEY_FRASES)||'[]');
176
  const save=(t)=>{t=t.trim();if(t.length<6)return;let a=get().filter(x=>x!==t);a.unshift(t);localStorage.setItem(KEY_FRASES,JSON.stringify(a.slice(0,100)));};
177
+ function loadMem(){ const mem=localStorage.getItem(KEY_MEM)||''; const h=document.getElementById('hidden-memoria-prog'); const v=document.getElementById('visible-memoria-prog'); if(h&&h.value!==mem){h.value=mem;h.dispatchEvent(new Event('input',{bubbles:true}));} if(v&&document.activeElement!==v&&v.value!==mem){v.value=mem;} }
178
+ setInterval(loadMem,800);
179
+ window.folderFiles={};
180
+ async function connectFolder(){ try{ const dirHandle=await window.showDirectoryPicker(); let fileList=[]; window.folderFiles={}; for await (const entry of dirHandle.values()){ if(entry.kind==='file' && entry.name.endsWith('.py')){ fileList.push(entry.name); const file=await entry.getFile(); window.folderFiles[entry.name]=await file.text(); } } document.getElementById('folder-status').value=fileList.join('|||'); document.getElementById('folder-status').dispatchEvent(new Event('input',{bubbles:true})); alert('✅ Conectada: '+fileList.join(', ')); }catch(e){alert('Usa Chrome/Edge PC');} }
181
+ window.connectFolder=connectFolder;
182
+ function attach(){ const input=document.querySelector('textarea[placeholder*="programación"]'); if(!input){setTimeout(attach,1000);return;} let ghost=document.getElementById('ghost-text'); if(!ghost){ ghost=document.createElement('div'); ghost.id='ghost-text'; ghost.style.cssText='position:absolute;left:14px;top:12px;color:rgba(130,130,130,0.45);pointer-events:none;font-size:16px;white-space:pre-wrap;z-index:2;'; input.parentElement.style.position='relative'; input.parentElement.appendChild(ghost); } let sug=''; input.addEventListener('input',()=>{ const v=input.value; if(!v){ghost.textContent='';sug='';return;} const m=get().find(f=>f.toLowerCase().startsWith(v.toLowerCase())&&f.toLowerCase()!==v.toLowerCase()); if(m){sug=m;ghost.textContent=m;}else{ghost.textContent='';sug='';} }); input.addEventListener('keydown',(e)=>{ if((e.key==='Tab'||e.key==='ArrowRight')&&sug){e.preventDefault();input.value=sug;input.dispatchEvent(new Event('input',{bubbles:true}));ghost.textContent='';sug='';} if(e.key==='Enter'&&!e.shiftKey&&input.value)save(input.value); if(e.key==='Enter'&&!e.shiftKey){ const txt=input.value.toLowerCase(); for(const fname in window.folderFiles){ if(txt.includes(fname.toLowerCase())){ document.getElementById('hidden-file-content').value=window.folderFiles[fname]; document.getElementById('hidden-file-content').dispatchEvent(new Event('input',{bubbles:true})); document.getElementById('hidden-file-name').value=fname; document.getElementById('hidden-file-name').dispatchEvent(new Event('input',{bubbles:true})); } } } }); }
183
+ setTimeout(attach,2000); window.addEventListener('load', loadMem);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  }
185
  """
186
 
187
  with gr.Blocks() as demo:
188
+ gr.Markdown("**Aura - Auto-Fix hasta que corra ✅**")
189
  hidden_memoria = gr.Textbox(value="", visible=False, elem_id="hidden-memoria-prog")
190
+ hidden_file_content = gr.Textbox(value="", visible=False, elem_id="hidden-file-content")
191
+ hidden_file_name = gr.Textbox(value="", visible=False, elem_id="hidden-file-name")
192
+ folder_status = gr.Textbox(value="", visible=False, elem_id="folder-status")
193
 
194
  with gr.Accordion("⚙️ Configuración + Memoria", open=False):
195
+ visible_memoria = gr.Textbox(label="🧠 Memoria", lines=2, elem_id="visible-memoria-prog")
196
  with gr.Row():
197
+ btn_save_mem = gr.Button("💾 Guardar")
198
+ btn_clear_mem = gr.Button("🗑️ Borrar")
199
+ token_input = gr.Textbox(label="Token HF", type="password")
200
+ system_input = gr.Textbox(value="Concisa.", label="Estilo", lines=1)
201
  with gr.Row():
202
+ max_tokens_input = gr.Slider(1, 4096, value=1000, label="Max Tokens")
203
+ temp_input = gr.Slider(0.1, 2.0, value=0.2, label="Temp")
204
  top_p_input = gr.Slider(0.1, 1.0, value=0.95, label="Top-p")
205
 
206
  with gr.Row():
207
+ file_upload = gr.File(label="📁 Archivo", file_types=[".py",".html",".js",".txt"], type="filepath")
208
+ file_status_box = gr.Textbox(label="Archivo", interactive=False)
209
+ btn_connect = gr.Button("📂 Conectar carpeta", variant="secondary")
210
 
211
  file_content_state = gr.State("")
212
  file_name_state = gr.State("")
213
+ chatbot = gr.Chatbot(label="Chat", height=400)
214
  with gr.Row():
215
+ chat_input = gr.Textbox(placeholder="Ej: revisa main.py y correlo hasta que funcione", scale=4)
216
  send_btn = gr.Button("Enviar", variant="primary")
217
+ clear_btn = gr.Button("🗑️")
218
 
219
  with gr.Row():
220
+ download_code = gr.File(label="📥 Código FINAL CORREGIDO", visible=False)
221
+ download_txt = gr.File(label="📥 Log", visible=False)
222
+
223
+ gr.Markdown("### ▶️ Consola Auto-fix")
224
+ exec_output = gr.Textbox(label="Consola", lines=8, interactive=False)
225
 
226
  def on_file_upload(file):
227
  if file is None: return "", "", "Ningún archivo"
228
  content = extract_file_text(file)
229
  name = os.path.basename(file.name)
230
+ return content, name, f"✅ {name}"
231
 
232
+ file_upload.change(on_file_upload, inputs=[file_upload], outputs=[file_content_state, file_name_state, file_status_box])
233
+ hidden_file_content.change(fn=lambda x: x, inputs=hidden_file_content, outputs=file_content_state)
234
+ hidden_file_name.change(fn=lambda x: x, inputs=hidden_file_name, outputs=file_name_state)
235
+ btn_connect.click(fn=None, inputs=[], outputs=[], js="()=>{ window.connectFolder(); }")
236
 
237
+ chat_input.submit(fn=chat_response, inputs=[chat_input, chatbot, system_input, max_tokens_input, temp_input, top_p_input, token_input, file_content_state, file_name_state, hidden_memoria, folder_status], outputs=[chatbot, download_code, download_txt, exec_output, hidden_memoria]).then(lambda: "", None, chat_input)
238
+ send_btn.click(fn=chat_response, inputs=[chat_input, chatbot, system_input, max_tokens_input, temp_input, top_p_input, token_input, file_content_state, file_name_state, hidden_memoria, folder_status], outputs=[chatbot, download_code, download_txt, exec_output, hidden_memoria]).then(lambda: "", None, chat_input)
239
  clear_btn.click(lambda: [], None, chatbot)
240
 
 
241
  visible_memoria.change(fn=lambda x: x, inputs=visible_memoria, outputs=hidden_memoria)
242
  hidden_memoria.change(fn=None, inputs=hidden_memoria, outputs=visible_memoria, js="(v)=>{ localStorage.setItem('aura_memoria_prog', v); return v; }")
243
+ btn_save_mem.click(fn=None, inputs=visible_memoria, outputs=visible_memoria, js="(v)=>{ localStorage.setItem('aura_memoria_prog', v); alert('Guardado'); return v; }")
244
  btn_clear_mem.click(fn=None, inputs=[], outputs=visible_memoria, js="()=>{ localStorage.removeItem('aura_memoria_prog'); return ''; }")
245
 
246
  demo.queue().launch(theme=gr.themes.Soft(), js=ghost_js)