caarleexx commited on
Commit
52fbcd4
·
verified ·
1 Parent(s): 298db72

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -146
app.py CHANGED
@@ -1,180 +1,159 @@
 
 
 
 
 
 
1
  import gradio as gr
 
 
2
  import os
3
- import json
4
- from pathlib import Path
5
  from datetime import datetime
6
-
7
- try:
8
- from llama_cpp import Llama
9
- LLM_AVAILABLE = True
10
- except Exception as e:
11
- LLM_AVAILABLE = False
12
- _err = str(e)
13
-
14
-
15
- # ==========================
16
- # CONFIG
17
- # ==========================
18
- MODEL_PATH = os.environ.get("MODEL_PATH", "models/gemma-2-2b-it-Q4_K_M.gguf")
19
-
20
- TEMP_A = 0.0 # deterministic
21
- TEMP_M = 0.9 # exploratory
22
- TOP_P = 0.95
23
- MAX_TOKENS = 256
24
-
25
-
26
- # ==========================
27
- # UTILITIES
28
- # ==========================
29
-
30
- def load_llm():
31
- if not LLM_AVAILABLE:
32
- return None
33
- return Llama(
34
- model_path=MODEL_PATH,
35
- n_ctx=4096,
36
- n_threads=8,
37
- verbose=False
38
- )
39
-
40
-
41
- llm = load_llm()
42
-
43
- def generate(prompt, temperature):
44
- if not LLM_AVAILABLE or llm is None:
45
- return f"(LLM não disponível: {_err})"
46
-
47
- out = llm.create_completion(
48
- prompt=prompt,
49
- temperature=temperature,
50
- max_tokens=MAX_TOKENS,
51
- top_p=TOP_P
52
- )
53
- return out["choices"][0]["text"].strip()
54
-
55
 
56
  def divergence(a, b):
57
- if not a or not b:
 
 
 
 
58
  return 1.0
59
- wa = set(a.split())
60
- wb = set(b.split())
61
- inter = wa & wb
62
- union = wa | wb
63
- if not union:
64
- return 1.0
65
- return round(1 - len(inter)/len(union), 3)
66
-
67
 
68
- # ==========================
69
- # DATASET SAVE
70
- # ==========================
71
 
72
- SAVE_DIR = Path("outputs")
73
- SAVE_DIR.mkdir(exist_ok=True)
74
 
75
- def save_dataset(question, respA, respM, div_score):
76
- timestamp = datetime.utcnow().isoformat()
77
- base = SAVE_DIR / f"dataset_{timestamp}"
78
 
79
- md_path = base.with_suffix(".md")
80
- json_path = base.with_suffix(".json")
81
-
82
- # Markdown
83
- md = f"""
84
- # Registro A-M
85
- Gerado: {timestamp}
86
 
87
- ## Pergunta
88
- {question}
89
 
90
- ## Resposta A (determinística)
91
- {respA}
92
 
93
- ## Resposta M (exploratória)
94
- {respM}
 
 
 
 
 
 
 
95
 
96
- ### Divergência
97
- {div_score}
98
- """
99
-
100
- md_path.write_text(md, encoding="utf-8")
101
-
102
- # JSON
103
- data = {
104
  "timestamp": timestamp,
105
  "question": question,
106
- "response_A": respA,
107
- "response_M": respM,
108
- "divergence": div_score
 
109
  }
110
 
111
- json_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
112
-
113
- return str(md_path), str(json_path)
114
 
 
 
115
 
116
- # ==========================
117
- # GRADIO UI
118
- # ==========================
119
-
120
- def run(question):
121
- if not question.strip():
122
- return ["", "", "", ""]
123
-
124
- prompt = f"Pergunta: {question}\nResponda de forma direta:"
125
-
126
- respA = generate(prompt, TEMP_A)
127
- respM = generate(prompt, TEMP_M)
128
- div_score = divergence(respA, respM)
129
-
130
- return respA, respM, str(div_score), ""
131
-
132
 
133
- def save_btn(question, respA, respM, div_score):
134
- if not question or not respA:
135
- return "Nada para salvar."
136
 
137
- md_path, json_path = save_dataset(question, respA, respM, div_score)
138
- return f"Salvo!\nMD: {md_path}\nJSON: {json_path}"
139
 
 
 
140
 
141
- with gr.Blocks(title="A-M Divergence Generator (Gemma 2B)") as demo:
 
142
 
143
- gr.Markdown("""
144
- # 🔵 A-M Divergence Generator
145
- Gera **duas respostas** usando o mesmo modelo Gemma:
146
 
147
- - **A** → Determinística (Temperature 0.0)
148
- - **M** → Exploratório (Temperature 0.9)
149
- - Mostra divergência → Se ≈0 = iguais, se ≈1 = totalmente diferentes
150
- - Permite salvar dataset `.md` + `.json` automaticamente
151
- """)
152
 
153
- with gr.Row():
154
- question = gr.Textbox(label="Pergunta", placeholder="Digite sua pergunta aqui...", lines=3)
155
 
156
- run_btn = gr.Button("Gerar A / M")
157
-
158
- with gr.Row():
159
- respA = gr.Textbox(label="Resposta A (determinística)")
160
- respM = gr.Textbox(label="Resposta M (exploratória)")
161
 
162
- div = gr.Textbox(label="Divergência (0 = igual, 1 = totalmente diferente)")
 
163
 
164
- status = gr.Textbox(label="Status / Logs")
165
 
166
- save = gr.Button("Salvar como Dataset (.md + .json)")
167
 
168
- run_btn.click(
169
- fn=run,
170
- inputs=[question],
171
- outputs=[respA, respM, div, status]
172
- )
173
 
174
- save.click(
175
- fn=save_btn,
176
- inputs=[question, respA, respM, div],
177
- outputs=[status]
178
- )
 
 
179
 
180
- demo.launch()
 
 
1
+ # ---------------------------------------------------------
2
+ # app.py — Twin-Branch A–M Reasoning Demo (Gemma 3n)
3
+ # Carlos Rodrigues — 2025
4
+ # ---------------------------------------------------------
5
+
6
+ import torch
7
  import gradio as gr
8
+ from huggingface_hub import snapshot_download
9
+ from transformers import AutoTokenizer, AutoModelForCausalLM
10
  import os
 
 
11
  from datetime import datetime
12
+ import re
13
+
14
+ # ---------------------------------------------------------
15
+ # CONFIGURAÇÃO DO MODELO (troque aqui se quiser outro)
16
+ # ---------------------------------------------------------
17
+
18
+ MODEL_REPO = "google/gemma-3n-E2B-it-litert-lm"
19
+
20
+ LOCAL_MODEL_DIR = snapshot_download(
21
+ repo_id=MODEL_REPO,
22
+ allow_patterns=["*.json", "*.bin", "*.model", "*.safetensors", "*.txt"],
23
+ )
24
+
25
+ # ---------------------------------------------------------
26
+ # CARREGAR TOKENIZER & MODELO
27
+ # ---------------------------------------------------------
28
+
29
+ tokenizer = AutoTokenizer.from_pretrained(LOCAL_MODEL_DIR)
30
+
31
+ model = AutoModelForCausalLM.from_pretrained(
32
+ LOCAL_MODEL_DIR,
33
+ torch_dtype=torch.float32,
34
+ device_map="auto" # usa GPU se tiver
35
+ )
36
+
37
+ # ---------------------------------------------------------
38
+ # FUNÇÕES AUXILIARES
39
+ # ---------------------------------------------------------
40
+
41
+ def clean_text(t):
42
+ t = t.replace("<s>", "").replace("</s>", "")
43
+ t = re.sub(r"\s+", " ", t)
44
+ return t.strip()
45
+
46
+ def generate_answer(prompt, temperature=0.0, top_p=1.0, max_new_tokens=180):
47
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
48
+ with torch.no_grad():
49
+ output = model.generate(
50
+ **inputs,
51
+ max_new_tokens=max_new_tokens,
52
+ temperature=temperature,
53
+ top_p=top_p,
54
+ do_sample=temperature > 0
55
+ )
56
+ decoded = tokenizer.decode(output[0])
57
+ return clean_text(decoded.replace(prompt, ""))
 
 
 
58
 
59
  def divergence(a, b):
60
+ """
61
+ Divergência simples: token overlap (0=igual, 1=muito diferente)
62
+ """
63
+ sa, sb = a.split(), b.split()
64
+ if len(sa) == 0 or len(sb) == 0:
65
  return 1.0
66
+ common = len(set(sa) & set(sb))
67
+ union = len(set(sa) | set(sb))
68
+ sim = common / union
69
+ return round(1 - sim, 3)
 
 
 
 
70
 
71
+ # ---------------------------------------------------------
72
+ # PIPELINE A–M
73
+ # ---------------------------------------------------------
74
 
75
+ def twin_branch_pipeline(question):
76
+ timestamp = datetime.utcnow().isoformat() + "Z"
77
 
78
+ prompt = f"Pergunta: {question}\nResponda claramente em até 120 palavras:\n"
 
 
79
 
80
+ # A — determinístico
81
+ answer_A = generate_answer(prompt, temperature=0.0, top_p=1.0)
 
 
 
 
 
82
 
83
+ # M — exploratório (descobre divergências)
84
+ answer_M = generate_answer(prompt, temperature=0.9, top_p=0.95)
85
 
86
+ # Divergência
87
+ div_score = divergence(answer_A, answer_M)
88
 
89
+ # Decisão simples
90
+ if div_score < 0.15:
91
+ decision = "Ambas respostas convergem — usar Resposta A."
92
+ elif div_score < 0.40:
93
+ decision = "Diferença moderada — sugerir resposta A, mas avisar ambiguidade."
94
+ elif div_score < 0.75:
95
+ decision = "As respostas divergem — investigar as duas interpretações."
96
+ else:
97
+ decision = "Alta divergência — perguntar ao operador para sanear intenção."
98
 
99
+ # Retorno estruturado
100
+ return {
 
 
 
 
 
 
101
  "timestamp": timestamp,
102
  "question": question,
103
+ "A (determinística)": answer_A,
104
+ "M (exploratória)": answer_M,
105
+ "Divergência (0=igual,1=muito diferente)": div_score,
106
+ "Decisão da Máquina": decision
107
  }
108
 
109
+ # ---------------------------------------------------------
110
+ # INTERFACE GRADIO
111
+ # ---------------------------------------------------------
112
 
113
+ def run_interface(question):
114
+ result = twin_branch_pipeline(question)
115
 
116
+ md = f"""
117
+ # 🔎 Twin-Reasoning A–M (Gemma-3n E2B)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
+ **Pergunta:**
120
+ {result['question']}
 
121
 
122
+ ---
 
123
 
124
+ ## 🟦 Resposta A (determinística)
125
+ {result['A (determinística)']}
126
 
127
+ ## 🟧 Resposta M (exploratória)
128
+ {result['M (exploratória)']}
129
 
130
+ ---
 
 
131
 
132
+ ### 📊 Divergência
133
+ **{result['Divergência (0=igual,1=muito diferente)']}**
 
 
 
134
 
135
+ ### 🧭 Decisão Interna
136
+ **{result['Decisão da Máquina']}**
137
 
138
+ ---
 
 
 
 
139
 
140
+ *Gerado em:* `{result['timestamp']}`
141
+ """
142
 
143
+ return md
144
 
 
145
 
146
+ # ---------------------------------------------------------
147
+ # LANÇAR A APP
148
+ # ---------------------------------------------------------
 
 
149
 
150
+ demo = gr.Interface(
151
+ fn=run_interface,
152
+ inputs=gr.Textbox(label="Digite uma pergunta:", placeholder="Ex: É legítimo filmar alguém em via pública?"),
153
+ outputs=gr.Markdown(),
154
+ title="Twin-Reasoner A–M — Gemma 3n",
155
+ description="Simulador de Arquitetura A–M (Geração + Verificação) baseado em pesos congelados.",
156
+ )
157
 
158
+ if __name__ == "__main__":
159
+ demo.launch()