caarleexx commited on
Commit
6dbf398
·
verified ·
1 Parent(s): dfd2d69

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +292 -233
app.py CHANGED
@@ -1,355 +1,414 @@
1
  # ╔════════════════════════════════════════════════════════════════════════════╗
2
- # ║ PIPELINE v29: MEMÓRIA VAGA + CONTEXTO GLOBAL
3
- # ║ Layout: Chat | Depuração | Contexto | Config
4
  # ╚════════════════════════════════════════════════════════════════════════════╝
5
 
6
  import os
7
  import json
8
  import time
9
  from datetime import datetime
10
-
11
  import gradio as gr
12
  import google.generativeai as genai
13
 
14
  # ==================== 1. CONFIGURAÇÃO ====================
15
 
16
  api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
17
- if api_key:
18
  genai.configure(api_key=api_key)
19
 
20
- model_flash = genai.GenerativeModel("gemini-flash-latest")
21
- model_pro = genai.GenerativeModel("gemini-pro-latest")
22
-
23
- ARQUIVO_CONFIG = "protocolo.json"
24
- ARQUIVO_CONTEXTO = "contexto.json"
25
 
 
 
26
 
27
  # ==================== 2. UTILIDADES ====================
28
 
29
  def carregar_protocolo():
 
30
  try:
31
  with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
32
  return f.read()
33
  except:
34
  return "[]"
35
 
36
-
37
  def salvar_protocolo(conteudo):
 
38
  try:
39
  json.loads(conteudo)
40
  with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
41
  f.write(conteudo)
42
- return "✅ Salvo"
43
- except:
44
- return "❌ Erro JSON"
45
-
46
 
47
  def ler_anexo(arquivo):
 
48
  if arquivo is None:
49
  return ""
50
  try:
51
  with open(arquivo.name, "r", encoding="utf-8") as f:
52
- return (
53
- f"\n\n[ANEXO SISTEMA: {os.path.basename(arquivo.name)}]\n"
54
- f"{f.read()}\n[FIM ANEXO]\n"
55
- )
56
- except:
57
- return ""
58
-
59
 
60
- # -------- CONTEXTO GLOBAL (MEMÓRIA VAGA) --------
61
 
62
- def contexto_vazio():
63
- return {
64
- "classificacao": "",
65
- "fatos": [],
66
- "objetivo_usuario": "",
67
- "duvida_central_ultimos_input": "",
68
- "timestamp": ""
69
- }
70
 
 
 
71
 
72
- def carregar_contexto():
73
- try:
74
- with open(ARQUIVO_CONTEXTO, "r", encoding="utf-8") as f:
75
- return json.load(f)
76
- except:
77
- return contexto_vazio()
78
 
 
 
 
79
 
80
- def salvar_contexto(ctx):
81
- try:
82
- with open(ARQUIVO_CONTEXTO, "w", encoding="utf-8") as f:
83
- json.dump(ctx, f, ensure_ascii=False, indent=2)
84
- return ctx
85
- except:
86
- return ctx
87
 
 
 
88
 
89
- def atualizar_contexto(full_input, timeline, contexto_anterior):
90
- """
91
- Agente MEMÓRIA VAGA:
92
- - escuta o diálogo e o histórico.
93
- - Atualiza e retorna APENAS o JSON de contexto global.
94
- - Nunca escreve na timeline, nunca responde ao usuário.
95
- """
96
- base_ctx = contexto_vazio()
97
- ctx_prev = contexto_anterior if contexto_anterior else contexto_vazio()
98
-
99
- modelo = model_pro # memória usa sempre o modelo mais robusto
100
-
101
- instrucao = (
102
- "Você está atuando como um 'ouvinte' que mantém um MEMORIAL do diálogo, "
103
- "na posição de Promotor de Justiça em casos de violência doméstica. "
104
- "Você NÃO responde ao usuário, NÃO sugere decisões, "
105
- "apenas organiza o que é importante em um JSON de CONTEXTO GLOBAL.\n\n"
106
- "TAREFA:\n"
107
- "- Ler o input atual, o contexto anterior e a timeline.\n"
108
- "- Atualizar o CONTEXTO GLOBAL com:\n"
109
- " * classificacao: \"duvida\" ou \"critica\" (ou vazio se não se aplicar).\n"
110
- " * fatos: lista de objetos {\"descricao\": str, \"peso\": float, \"ultima_mencao\": str ISO8601}\n"
111
- " representando elementos factuais relevantes do caso, com peso de 0.0 a 1.0.\n"
112
- " - Fatos que voltam a aparecer podem ter peso aumentado.\n"
113
- " - Fatos que deixam de aparecer podem ter peso suavemente reduzido.\n"
114
- " * objetivo_usuario: 1 frase objetiva sobre o que o usuário quer alcançar agora.\n"
115
- " * duvida_central_ultimos_input: síntese em 1–2 frases da dúvida/foco do último input.\n"
116
- " * timestamp: momento atual em ISO8601.\n\n"
117
- "IMPORTANTE:\n"
118
- "- Não explique, não comente. Retorne APENAS um JSON válido.\n"
119
- "- Mantenha a mesma estrutura de chaves do contexto_anterior, atualizando o que fizer sentido.\n"
120
- )
121
 
122
- payload = {
123
- "input_atual": full_input,
124
- "contexto_anterior": ctx_prev,
125
- "timeline": timeline,
126
- }
127
-
128
- schema_exemplo = base_ctx
129
-
130
- prompt = (
131
- instrucao
132
- + "\n\n--- DADOS ---\n"
133
- + json.dumps(payload, ensure_ascii=False, indent=2)
134
- + "\n\n--- SCHEMA EXATO A SEGUIR ---\n"
135
- + json.dumps(schema_exemplo, ensure_ascii=False, indent=2)
136
- + "\n\n--- SAÍDA ---\n"
137
- + "Retorne APENAS um JSON válido seguindo o schema (sem comentários, sem texto extra)."
138
- )
139
 
140
  try:
141
- resp = modelo.generate_content(prompt)
142
- raw = resp.text.strip()
143
- cleaned = raw.replace("``````", "").strip()
144
- novo_ctx = json.loads(cleaned)
145
-
146
- # Garantir chaves mínimas
147
- for k, v in base_ctx.items():
148
- if k not in novo_ctx:
149
- novo_ctx[k] = v
150
-
151
- return novo_ctx
152
- except Exception:
153
- # Fallback: preserva memória antiga e marca timestamp / classificação mínima
154
- ctx_fallback = ctx_prev
155
- ctx_fallback["timestamp"] = datetime.now().isoformat()
156
- if "?" in full_input:
157
- ctx_fallback["classificacao"] = "duvida"
158
- return ctx_fallback
159
-
160
-
161
- # ==================== 3. ENGINE DE EXECUÇÃO (AGENTES) ====================
162
-
163
- def executar_no(timeline, config, contexto):
164
- """
165
- Executa um nó do protocolo:
166
- - Recebe timeline + contexto global (memória vaga).
167
- - Gera resposta de agente (texto ou JSON).
168
- """
169
- modelo = model_pro if config.get("modelo") == "pro" else model_flash
170
-
171
- contexto_timeline = json.dumps(timeline, ensure_ascii=False, indent=2)
172
- contexto_global = json.dumps(contexto, ensure_ascii=False, indent=2)
173
-
174
- prompt = (
175
- f"--- CONTEXTO GLOBAL (MEMÓRIA DE QUEM OUVE) ---\n{contexto_global}\n"
176
- f"--- TIMELINE ---\n{contexto_timeline}\n"
177
- f"----------------\n"
178
- f"AGENTE: {config['nome']}\n"
179
- f"MISSÃO: {config['missao']}"
180
- )
181
 
182
- log = f"\n🔸 {config['nome']}..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- try:
185
- inicio = time.time()
186
- resp = modelo.generate_content(prompt)
187
- out = resp.text
188
  tempo = time.time() - inicio
189
 
190
- if config.get("tipo_saida") == "json":
191
- cleaned = out.strip().replace("``````", "")
192
- content = json.loads(cleaned)
 
 
 
193
  else:
194
- content = out
195
 
196
- log += f" (OK - {tempo:.2f}s)"
197
 
198
- return {"role": "assistant", "agent": config["nome"], "content": content}, log, out
 
 
 
 
 
199
 
200
  except Exception as e:
201
- return {"role": "system", "error": str(e)}, f" (ERRO: {e})", str(e)
 
 
 
 
 
 
202
 
203
-
204
- # ==================== 4. ORQUESTRADOR ====================
205
 
206
  def orquestrador(texto, arquivo, history, json_config):
207
- # 1. Input Check
 
 
208
  anexo = ler_anexo(arquivo)
209
  full_input = f"{texto}\n{anexo}".strip()
210
 
211
- contexto = carregar_contexto()
212
-
213
  if not full_input:
214
- yield history, {}, "Sem input.", contexto
215
  return
216
 
217
- # 2. Setup histórico
218
  history = history + [[texto + (" 📎" if arquivo else ""), None]]
219
 
220
  try:
221
  protocolo = json.loads(json_config)
222
- except:
223
- history[-1][1] = "❌ Erro no JSON de Configuração."
224
- yield history, {}, "Erro JSON", contexto
225
  return
226
 
227
- # Timeline começa só com o usuário
228
- timeline = [{"role": "user", "content": full_input}]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- # 3. MEMÓRIA VAGA: atualiza contexto ANTES de qualquer agente responder
231
- contexto = atualizar_contexto(full_input, timeline, contexto)
232
- salvar_contexto(contexto)
233
 
234
- logs = f"🚀 START: {datetime.now().strftime('%H:%M:%S')}\n"
 
 
 
235
 
236
- history[-1][1] = "⏳ Iniciando análise..."
237
- yield history, timeline, logs, contexto
 
 
 
238
 
239
- # 4. Loop de nós do protocolo
240
- final_response = ""
 
 
241
 
242
- for cfg in protocolo:
243
- history[-1][1] = f"⚙️ {cfg['nome']} trabalhando..."
244
- yield history, timeline, logs, contexto
245
 
246
- res, log_add, raw = executar_no(timeline, cfg, contexto)
247
- timeline.append(res)
 
 
 
 
 
248
  logs += log_add + "\n"
249
 
250
- # Após cada nó, a memória vaga reouve tudo e atualiza o contexto
251
- contexto = atualizar_contexto(full_input, timeline, contexto)
252
- salvar_contexto(contexto)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- if cfg.get("tipo_saida") == "texto" and res.get("role") == "assistant":
255
- final_response = res.get("content", "")
 
 
 
 
 
 
256
  history[-1][1] = final_response
257
- yield history, timeline, logs, contexto
258
 
259
- logs += "✅ FIM."
260
- yield history, timeline, logs, contexto
 
 
 
 
 
 
 
261
 
 
262
 
263
- # ==================== 5. UI LIMPA (v29) ====================
 
264
 
265
- def ui_clean():
266
  css = """
267
  footer {display: none !important;}
268
  .contain {border: none !important;}
269
  """
270
 
271
- config_init = carregar_protocolo()
 
 
 
 
 
 
 
 
 
 
 
272
 
273
- with gr.Blocks(title="AI Forensics", css=css, theme=gr.themes.Soft()) as app:
274
  with gr.Tabs():
275
 
276
- # === ABA 1: CHAT (LIMPO) ===
277
- with gr.Tab("💬 Investigador"):
278
  chatbot = gr.Chatbot(
279
  label="",
280
  show_label=False,
281
- height=600,
282
  show_copy_button=True,
283
- render_markdown=True,
284
  )
285
 
286
  with gr.Row():
287
  with gr.Column(scale=10):
288
- txt_in = gr.Textbox(
289
  show_label=False,
290
- placeholder="Descreva o caso ou instrução...",
291
- lines=1,
292
- max_lines=5,
293
- container=False,
294
  )
295
- with gr.Column(scale=1, min_width=50):
296
- file_in = gr.UploadButton(
 
297
  "📎",
298
- file_types=[".txt", ".md", ".csv", ".json"],
299
- size="sm",
300
- )
301
- with gr.Column(scale=1, min_width=80):
302
- btn_send = gr.Button(
303
- "Enviar",
304
- variant="primary",
305
- size="sm",
306
  )
307
 
308
- # Feedback visual do arquivo
 
 
309
  file_status = gr.Markdown("", visible=True)
310
- file_in.upload(
311
- lambda x: f"📎 Anexo: {os.path.basename(x.name)}",
312
- file_in,
313
- file_status,
314
  )
315
 
316
  # === ABA 2: DEPURAÇÃO ===
317
  with gr.Tab("🕵️ Depuração"):
318
- with gr.Row():
319
- out_dna = gr.JSON(label="DNA (Timeline)")
320
- out_logs = gr.Textbox(label="Logs do Sistema", lines=20)
 
 
321
 
322
- # === ABA 3: CONTEXTO GLOBAL ===
323
- with gr.Tab("🧠 Contexto"):
324
- contexto_json = gr.JSON(label="Contexto Global (contexto.json)")
 
 
 
 
 
 
 
 
 
 
 
325
 
326
- # === ABA 4: CONFIG (TÉCNICO) ===
327
- with gr.Tab("⚙️ Config"):
328
  with gr.Row():
329
- btn_save = gr.Button("Salvar Config")
330
- lbl_save = gr.Label(show_label=False)
331
- code_json = gr.Code(
332
- value=config_init,
 
333
  language="json",
334
- label="protocolo.json",
335
- )
336
- btn_save.click(salvar_protocolo, code_json, lbl_save)
337
-
338
- # === TRIGGERS ===
339
- triggers = [btn_send.click, txt_in.submit]
340
-
341
- for trig in triggers:
342
- trig(
343
- orquestrador,
344
- inputs=[txt_in, file_in, chatbot, code_json],
345
- outputs=[chatbot, out_dna, out_logs, contexto_json],
346
- ).then(
347
- lambda: (None, ""),
348
- outputs=[txt_in, file_status],
349
  )
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  return app
352
 
 
353
 
354
  if __name__ == "__main__":
355
- ui_clean().launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ╔════════════════════════════════════════════════════════════════════════════╗
2
+ # ║ PIPELINE EPISTÊMICO v28: Protocolo Causal de 10 Fases
3
+ # ║ Resolução do Paradoxo de Ménon aplicado à investigação forense
4
  # ╚════════════════════════════════════════════════════════════════════════════╝
5
 
6
  import os
7
  import json
8
  import time
9
  from datetime import datetime
 
10
  import gradio as gr
11
  import google.generativeai as genai
12
 
13
  # ==================== 1. CONFIGURAÇÃO ====================
14
 
15
  api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
16
+ if api_key:
17
  genai.configure(api_key=api_key)
18
 
19
+ model_flash = genai.GenerativeModel("gemini-2.0-flash-exp")
20
+ model_pro = genai.GenerativeModel("gemini-1.5-pro-002")
 
 
 
21
 
22
+ ARQUIVO_CONFIG = "protocolo_epistemico_forense.json"
23
+ MAX_ITERACOES = 3 # Máximo de voltas Fase 7 → Fase 3
24
 
25
  # ==================== 2. UTILIDADES ====================
26
 
27
  def carregar_protocolo():
28
+ """Carrega configuração do protocolo"""
29
  try:
30
  with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
31
  return f.read()
32
  except:
33
  return "[]"
34
 
 
35
  def salvar_protocolo(conteudo):
36
+ """Salva e valida JSON do protocolo"""
37
  try:
38
  json.loads(conteudo)
39
  with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
40
  f.write(conteudo)
41
+ return "✅ Protocolo salvo com sucesso"
42
+ except Exception as e:
43
+ return f"❌ Erro no JSON: {str(e)}"
 
44
 
45
  def ler_anexo(arquivo):
46
+ """Lê arquivo anexado"""
47
  if arquivo is None:
48
  return ""
49
  try:
50
  with open(arquivo.name, "r", encoding="utf-8") as f:
51
+ conteudo = f.read()
52
+ return f"\n\n--- ANEXO: {os.path.basename(arquivo.name)} ---\n{conteudo}\n--- FIM ANEXO ---\n"
53
+ except Exception as e:
54
+ return f"\n[ERRO ao ler anexo: {e}]\n"
 
 
 
55
 
56
+ # ==================== 3. ENGINE DE EXECUÇÃO ====================
57
 
58
+ def executar_fase(timeline, config, fase_atual, total_fases):
59
+ """Executa uma fase do protocolo"""
 
 
 
 
 
 
60
 
61
+ # Selecionar modelo
62
+ modelo = model_pro if config.get("modelo") == "pro" else model_flash
63
 
64
+ # Construir contexto com toda a timeline
65
+ contexto = json.dumps(timeline, ensure_ascii=False, indent=2)
 
 
 
 
66
 
67
+ # Prompt estruturado
68
+ prompt = f"""--- TIMELINE COMPLETA ---
69
+ {contexto}
70
 
71
+ --- FASE ATUAL ---
72
+ AGENTE: {config['nome']}
73
+ FASE: {fase_atual}/{total_fases}
74
+ TIPO_SAIDA: {config['tipo_saida']}
 
 
 
75
 
76
+ --- MISSÃO ---
77
+ {config['missao']}
78
 
79
+ --- INSTRUÇÕES ---
80
+ 1. Analise TODA a timeline acima
81
+ 2. Execute sua missão com rigor
82
+ 3. {"Retorne APENAS JSON válido" if config['tipo_saida'] == 'json' else "Retorne texto em Markdown"}
83
+ 4. Seja epistemicamente honesto sobre incertezas
84
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ log = f"\n🔸 Fase {fase_atual}: {config['nome']}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
  try:
89
+ inicio = time.time()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ # Executar com retry
92
+ max_tentativas = 2
93
+ for tentativa in range(max_tentativas):
94
+ try:
95
+ resp = modelo.generate_content(
96
+ prompt,
97
+ generation_config={
98
+ "temperature": 0.3 if config['tipo_saida'] == 'json' else 0.7,
99
+ "max_output_tokens": 8000
100
+ }
101
+ )
102
+ output_raw = resp.text
103
+ break
104
+ except Exception as e:
105
+ if tentativa == max_tentativas - 1:
106
+ raise e
107
+ time.sleep(2)
108
 
 
 
 
 
109
  tempo = time.time() - inicio
110
 
111
+ # Processar output
112
+ if config['tipo_saida'] == 'json':
113
+ # Limpar markdown
114
+ output_limpo = output_raw.strip()
115
+ output_limpo = output_limpo.replace('```json', '').replace('```', '')
116
+ content = json.loads(output_limpo)
117
  else:
118
+ content = output_raw
119
 
120
+ log += f" ({tempo:.1f}s)"
121
 
122
+ return {
123
+ "role": "assistant",
124
+ "agent": config['nome'],
125
+ "fase": config['fase'],
126
+ "content": content
127
+ }, log, True
128
 
129
  except Exception as e:
130
+ log += f" ✗ ERRO: {str(e)[:100]}"
131
+ return {
132
+ "role": "system",
133
+ "agent": config['nome'],
134
+ "fase": config['fase'],
135
+ "error": str(e)
136
+ }, log, False
137
 
138
+ # ==================== 4. ORQUESTRADOR COM LOOP ITERATIVO ====================
 
139
 
140
  def orquestrador(texto, arquivo, history, json_config):
141
+ """Orquestra execução do protocolo de 10 fases com loop iterativo"""
142
+
143
+ # 1. Validar input
144
  anexo = ler_anexo(arquivo)
145
  full_input = f"{texto}\n{anexo}".strip()
146
 
 
 
147
  if not full_input:
148
+ yield history, {}, "⚠️ Nenhum input fornecido."
149
  return
150
 
151
+ # 2. Setup
152
  history = history + [[texto + (" 📎" if arquivo else ""), None]]
153
 
154
  try:
155
  protocolo = json.loads(json_config)
156
+ except Exception as e:
157
+ history[-1][1] = f"❌ **Erro no JSON de Configuração**\n\n```\n{str(e)}\n```"
158
+ yield history, {}, f"Erro JSON: {e}"
159
  return
160
 
161
+ # 3. Inicializar timeline
162
+ timeline = [{
163
+ "role": "user",
164
+ "content": full_input,
165
+ "timestamp": datetime.now().isoformat()
166
+ }]
167
+
168
+ logs = f"🚀 INÍCIO: {datetime.now().strftime('%H:%M:%S')}\n"
169
+ logs += f"📋 Protocolo: {len(protocolo)} fases\n"
170
+ logs += f"🔄 Max iterações: {MAX_ITERACOES}\n"
171
+ logs += "=" * 60 + "\n"
172
+
173
+ history[-1][1] = "⏳ **Iniciando Protocolo Epistêmico...**\n\nFase 0/10: Estado Inicial"
174
+ yield history, timeline, logs
175
+
176
+ # 4. Executar fases 0-6 (sequenciais)
177
+ fases_sequenciais = [f for f in protocolo if f['fase'] < 7]
178
+
179
+ for cfg in fases_sequenciais:
180
+ fase_num = cfg['fase']
181
+ history[-1][1] = f"⚙️ **Fase {fase_num}/10: {cfg['nome']}**\n\nProcessando..."
182
+ yield history, timeline, logs
183
+
184
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10)
185
+ timeline.append(resultado)
186
+ logs += log_add + "\n"
187
+
188
+ if not sucesso:
189
+ history[-1][1] = f"❌ **Erro na Fase {fase_num}**\n\n{resultado.get('error', 'Erro desconhecido')}"
190
+ yield history, timeline, logs
191
+ return
192
+
193
+ yield history, timeline, logs
194
+
195
+ # 5. Loop iterativo: Fases 7 (teste) ↔ Fases 3-6 (refinamento)
196
+ iteracao = 0
197
+ fase_7_passou = False
198
 
199
+ while iteracao < MAX_ITERACOES and not fase_7_passou:
200
+ iteracao += 1
201
+ logs += f"\n{'='*60}\n🔄 ITERAÇÃO {iteracao}/{MAX_ITERACOES}\n{'='*60}\n"
202
 
203
+ # Se não é primeira vez, re-executar fases 3-6
204
+ if iteracao > 1:
205
+ history[-1][1] = f"🔄 **Iteração {iteracao}**: Refinando cenários (Fases 3-6)..."
206
+ yield history, timeline, logs
207
 
208
+ fases_refinamento = [f for f in protocolo if 3 <= f['fase'] < 7]
209
+ for cfg in fases_refinamento:
210
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, cfg['fase'], 10)
211
+ timeline.append(resultado)
212
+ logs += log_add + "\n"
213
 
214
+ if not sucesso:
215
+ history[-1][1] = f"❌ **Erro no refinamento**"
216
+ yield history, timeline, logs
217
+ return
218
 
219
+ yield history, timeline, logs
 
 
220
 
221
+ # Executar Fase 7: TESTE_CRUCIALIDADE
222
+ cfg_fase7 = next(f for f in protocolo if f['fase'] == 7)
223
+ history[-1][1] = f"🧪 **Fase 7/10: Teste de Crucialidade** (Iteração {iteracao})\n\nValidando..."
224
+ yield history, timeline, logs
225
+
226
+ resultado, log_add, sucesso = executar_fase(timeline, cfg_fase7, 7, 10)
227
+ timeline.append(resultado)
228
  logs += log_add + "\n"
229
 
230
+ if not sucesso:
231
+ history[-1][1] = f"❌ **Erro na Fase 7**"
232
+ yield history, timeline, logs
233
+ return
234
+
235
+ # Verificar se passou no teste
236
+ teste_resultado = resultado['content']
237
+ fase_7_passou = teste_resultado.get('PASSOU', False)
238
+ score = teste_resultado.get('SCORE_CRUCIALIDADE', 0)
239
+
240
+ if fase_7_passou:
241
+ logs += f"✅ TESTE PASSOU (score: {score:.3f})\n"
242
+ history[-1][1] = f"✅ **Validação Aprovada!**\n\nScore: {score:.3f}"
243
+ else:
244
+ logs += f"⚠️ TESTE FALHOU (score: {score:.3f})\n"
245
+ fragilidades = teste_resultado.get('FRAGILIDADES_IDENTIFICADAS', [])
246
+ history[-1][1] = f"⚠️ **Validação Reprovada** (Score: {score:.3f})\n\nFragilidades:\n" + "\n".join(f"- {f}" for f in fragilidades)
247
+
248
+ yield history, timeline, logs
249
+
250
+ if not fase_7_passou:
251
+ logs += f"\n⚠️ Limite de iterações atingido ({MAX_ITERACOES}). Prosseguindo com melhor resultado.\n"
252
+
253
+ # 6. Fases finais 8-9 (sempre executam)
254
+ fases_finais = [f for f in protocolo if f['fase'] >= 8]
255
+
256
+ for cfg in fases_finais:
257
+ fase_num = cfg['fase']
258
+ history[-1][1] = f"📝 **Fase {fase_num}/10: {cfg['nome']}**\n\nGerando..."
259
+ yield history, timeline, logs
260
+
261
+ resultado, log_add, sucesso = executar_fase(timeline, cfg, fase_num, 10)
262
+ timeline.append(resultado)
263
+ logs += log_add + "\n"
264
 
265
+ if not sucesso:
266
+ history[-1][1] = f" **Erro na Fase {fase_num}**"
267
+ yield history, timeline, logs
268
+ return
269
+
270
+ # Se for a fase final (9 - RELATORIO_FINAL), mostrar resultado
271
+ if cfg['fase'] == 9 and cfg['tipo_saida'] == 'texto':
272
+ final_response = resultado['content']
273
  history[-1][1] = final_response
 
274
 
275
+ yield history, timeline, logs
276
+
277
+ # 7. Finalizar
278
+ logs += "\n" + "=" * 60 + "\n"
279
+ logs += f"✅ CONCLUÍDO: {datetime.now().strftime('%H:%M:%S')}\n"
280
+ logs += f"📊 Total de fases executadas: {len([t for t in timeline if t.get('fase') is not None])}\n"
281
+ logs += f"🔄 Iterações necessárias: {iteracao}\n"
282
+
283
+ yield history, timeline, logs
284
 
285
+ # ==================== 5. UI COM GRADIO ====================
286
 
287
+ def criar_ui():
288
+ """Cria interface Gradio"""
289
 
 
290
  css = """
291
  footer {display: none !important;}
292
  .contain {border: none !important;}
293
  """
294
 
295
+ config_inicial = carregar_protocolo()
296
+
297
+ with gr.Blocks(
298
+ title="🔬 Investigador Epistêmico",
299
+ css=css,
300
+ theme=gr.themes.Soft()
301
+ ) as app:
302
+
303
+ gr.Markdown("""
304
+ # 🔬 Investigador Epistêmico
305
+ ### Protocolo Causal de 10 Fases | Resolução do Paradoxo de Ménon
306
+ """)
307
 
 
308
  with gr.Tabs():
309
 
310
+ # === ABA 1: INVESTIGAÇÃO ===
311
+ with gr.Tab("💬 Investigação"):
312
  chatbot = gr.Chatbot(
313
  label="",
314
  show_label=False,
315
+ height=650,
316
  show_copy_button=True,
317
+ render_markdown=True
318
  )
319
 
320
  with gr.Row():
321
  with gr.Column(scale=10):
322
+ txt_input = gr.Textbox(
323
  show_label=False,
324
+ placeholder="Descreva o caso, pergunta ou situação a investigar...",
325
+ lines=2,
326
+ max_lines=8,
327
+ container=False
328
  )
329
+
330
+ with gr.Column(scale=1, min_width=60):
331
+ file_input = gr.UploadButton(
332
  "📎",
333
+ file_types=[".txt", ".md", ".csv", ".json", ".pdf"],
334
+ size="sm"
 
 
 
 
 
 
335
  )
336
 
337
+ with gr.Column(scale=1, min_width=100):
338
+ btn_enviar = gr.Button("🚀 Investigar", variant="primary", size="sm")
339
+
340
  file_status = gr.Markdown("", visible=True)
341
+ file_input.upload(
342
+ lambda x: f"📎 **Anexo:** {os.path.basename(x.name)}",
343
+ file_input,
344
+ file_status
345
  )
346
 
347
  # === ABA 2: DEPURAÇÃO ===
348
  with gr.Tab("🕵️ Depuração"):
349
+ gr.Markdown("### Timeline Completa (DNA da Investigação)")
350
+ output_timeline = gr.JSON(label="Timeline")
351
+
352
+ gr.Markdown("### Logs do Sistema")
353
+ output_logs = gr.Textbox(label="Logs", lines=25, max_lines=50)
354
 
355
+ # === ABA 3: CONFIGURAÇÃO ===
356
+ with gr.Tab("⚙️ Configuração"):
357
+ gr.Markdown("""
358
+ ### Protocolo Epistêmico (JSON)
359
+
360
+ **Estrutura de cada fase:**
361
+ - `fase`: Número da fase (0-9)
362
+ - `nome`: Identificador da fase
363
+ - `modelo`: "flash" ou "pro"
364
+ - `tipo_saida`: "json" ou "texto"
365
+ - `missao`: Instruções detalhadas
366
+
367
+ **Fluxo:** 0→1→2→3→4→5→6→7(teste)→[volta 3 se falhar]→8→9
368
+ """)
369
 
 
 
370
  with gr.Row():
371
+ btn_salvar = gr.Button("💾 Salvar Protocolo", variant="primary")
372
+ label_salvar = gr.Label(show_label=False)
373
+
374
+ code_protocolo = gr.Code(
375
+ value=config_inicial,
376
  language="json",
377
+ label="protocolo_epistemico_forense.json",
378
+ lines=30
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  )
380
 
381
+ btn_salvar.click(salvar_protocolo, code_protocolo, label_salvar)
382
+
383
+ # === EVENTOS ===
384
+ triggers = [btn_enviar.click, txt_input.submit]
385
+
386
+ for trigger in triggers:
387
+ trigger(
388
+ orquestrador,
389
+ inputs=[txt_input, file_input, chatbot, code_protocolo],
390
+ outputs=[chatbot, output_timeline, output_logs]
391
+ ).then(
392
+ lambda: (None, ""),
393
+ outputs=[txt_input, file_status]
394
+ )
395
+
396
  return app
397
 
398
+ # ==================== 6. MAIN ====================
399
 
400
  if __name__ == "__main__":
401
+ print("=" * 80)
402
+ print("🔬 INVESTIGADOR EPISTÊMICO - Protocolo Causal v28")
403
+ print("=" * 80)
404
+ print(f"📋 Arquivo de configuração: {ARQUIVO_CONFIG}")
405
+ print(f"🔄 Máximo de iterações: {MAX_ITERACOES}")
406
+ print("=" * 80)
407
+
408
+ app = criar_ui()
409
+ app.launch(
410
+ server_name="0.0.0.0",
411
+ server_port=7860,
412
+ share=False,
413
+ show_error=True
414
+ )