File size: 3,973 Bytes
824589c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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()