import os import sys import json import textwrap from pathlib import Path # Detectar ambiente def is_huggingface(): return "SPACE_ID" in os.environ or "HF_HOME" in os.environ RUNNING_IN_HF = is_huggingface() # ========================== # MODELOS # ========================== LOCAL_MODEL = "qwen3.5:latest" HF_MODEL = "google/gemma-2-2b-it" # ========================== # MODELO LOCAL (OLLAMA) # ========================== def call_ollama(prompt): import requests try: resp = requests.post( "http://localhost:11434/api/generate", json={"model": LOCAL_MODEL, "prompt": prompt, "stream": False}, timeout=30 ) return resp.json().get("response", "") except: return None # ========================== # MODELO HUGGING FACE (TRANSFORMERS) # ========================== hf_pipeline = None def load_hf_model(): global hf_pipeline if hf_pipeline is None: from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline tok = AutoTokenizer.from_pretrained(HF_MODEL) model = AutoModelForCausalLM.from_pretrained(HF_MODEL) hf_pipeline = pipeline("text-generation", model=model, tokenizer=tok) return hf_pipeline def call_hf_transformers(prompt): pipe = load_hf_model() out = pipe(prompt, max_new_tokens=256) return out[0]["generated_text"] # ========================== # FALLBACK # ========================== def call_model(prompt): # 1) Tentar Ollama local if not RUNNING_IN_HF: r = call_ollama(prompt) if r: return r # 2) Tentar Gemma local (Transformers) try: return call_hf_transformers(prompt) except: pass # 3) Fallback interno return "Não consegui usar nenhum modelo. Resposta fallback." # ========================== # AGENTE # ========================== SYSTEM_PROMPT = """ És um agente de desenvolvimento híbrido. Cria ficheiros, lê ficheiros e corrige código. No Hugging Face não usas subprocessos. Responde sempre em JSON com: { "thoughts": "...", "steps": [ {"tool": "...", "args": {...}, "comment": "..."} ] } """ def build_prompt(user): return SYSTEM_PROMPT + "\nUtilizador:\n" + user + "\nJSON:" # Ferramentas def tool_read(path): p = Path(path) if not p.exists(): return "[ERRO] ficheiro não existe" return p.read_text() def tool_write(path, content): p = Path(path) p.parent.mkdir(parents=True, exist_ok=True) p.write_text(content) return "[OK] escrito" def tool_run(cmd): if RUNNING_IN_HF: return "[BLOQUEADO] subprocessos não são permitidos no Hugging Face." import subprocess r = subprocess.run(cmd, shell=True, capture_output=True, text=True) return r.stdout + "\n" + r.stderr # Execução dos passos def execute_steps(steps): outputs = [] for s in steps: tool = s.get("tool") args = s.get("args", {}) if tool == "read_file": outputs.append(tool_read(args["path"])) elif tool == "write_file": outputs.append(tool_write(args["path"], args["content"])) elif tool == "run_command": outputs.append(tool_run(args["command"])) else: outputs.append("[ERRO] ferramenta desconhecida") return "\n".join(outputs) # ========================== # INTERFACE GRADIO # ========================== import gradio as gr def agent_chat(user_input): prompt = build_prompt(user_input) raw = call_model(prompt) try: data = json.loads(raw) except: return "Modelo não devolveu JSON válido:\n" + raw steps = data.get("steps", []) out = execute_steps(steps) return out with gr.Blocks() as demo: gr.Markdown("# Dev Agent Híbrido (Local + Hugging Face)") inp = gr.Textbox(label="Comando") out = gr.Textbox(label="Output") btn = gr.Button("Executar") btn.click(agent_chat, inp, out) demo.launch()