caarleexx commited on
Commit
22d6f06
Β·
verified Β·
1 Parent(s): 50a59c3

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -410
app.py DELETED
@@ -1,410 +0,0 @@
1
- # ╔════════════════════════════════════════════════════════════════════════════╗
2
- # ║ PIPELINE V44: FRAG + VISÃO PAGINADA + PARALELISMO + CACHE + AUDITORIA ║
3
- # β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
4
-
5
- import os
6
- import json
7
- import time
8
- import hashlib
9
- from datetime import datetime
10
- from concurrent.futures import ThreadPoolExecutor, as_completed
11
-
12
- import gradio as gr
13
- import google.generativeai as genai
14
- import pypdf # pip install pypdf
15
-
16
- # ==================== 1. CONFIGURAÇÃO ====================
17
-
18
- api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
19
- if api_key and api_key != "SUA_API_KEY_AQUI":
20
- genai.configure(api_key=api_key)
21
-
22
- model_flash = genai.GenerativeModel("gemini-flash-latest")
23
- model_pro = genai.GenerativeModel("gemini-pro-latest")
24
-
25
- ARQUIVO_CONFIG = "protocolo_fragmentacao_visao-3.json"
26
- PASTA_CACHE = "cache_processamento"
27
- MAX_WORKERS = 5 # Paralelismo
28
-
29
- os.makedirs(PASTA_CACHE, exist_ok=True)
30
-
31
- # ==================== 2. UTILIDADES ====================
32
-
33
- def log_point(msg, logs):
34
- ts = datetime.now().strftime("%H:%M:%S")
35
- return logs + f"[{ts}] {msg}\n"
36
-
37
- def carregar_protocolo():
38
- try:
39
- with open(ARQUIVO_CONFIG, "r", encoding="utf-8") as f:
40
- return f.read()
41
- except:
42
- proto = [
43
- {
44
- "nome": "PAGINADOR_VISUAL",
45
- "missao": (
46
- "VocΓͺ recebe o texto bruto de um conjunto de pΓ‘ginas de um PDF. "
47
- "Separe por pΓ‘gina e devolva uma lista JSON com objetos "
48
- "{'pagina','transcricao_fiel','descricao_visual'}."
49
- "Retorne APENAS essa lista JSON, sem texto extra."
50
- ),
51
- "tipo_saida": "json",
52
- "modelo": "flash",
53
- }
54
- ]
55
- return json.dumps(proto, ensure_ascii=False, indent=2)
56
-
57
- def salvar_protocolo(conteudo):
58
- try:
59
- json.loads(conteudo)
60
- with open(ARQUIVO_CONFIG, "w", encoding="utf-8") as f:
61
- f.write(conteudo)
62
- return "βœ… Salvo"
63
- except:
64
- return "❌ Erro JSON"
65
-
66
- def gerar_hash_arquivo(nome_arquivo):
67
- return hashlib.md5(nome_arquivo.encode()).hexdigest()
68
-
69
- def salvar_cache(hash_id, dados):
70
- caminho = os.path.join(PASTA_CACHE, f"{hash_id}.json")
71
- with open(caminho, "w", encoding="utf-8") as f:
72
- json.dump(dados, f, ensure_ascii=False, indent=2)
73
-
74
- def carregar_cache(hash_id):
75
- caminho = os.path.join(PASTA_CACHE, f"{hash_id}.json")
76
- if os.path.exists(caminho):
77
- with open(caminho, "r", encoding="utf-8") as f:
78
- return json.load(f)
79
- return None
80
-
81
- # --------- DIVISÃO PDF ---------
82
-
83
- def ler_anexo_e_fragmentar(arquivo, paginas_por_fragmento=5, logs=""):
84
- logs = log_point("ler_anexo_e_fragmentar() chamado", logs)
85
-
86
- if arquivo is None:
87
- return [], "", logs
88
-
89
- filename = getattr(arquivo, "name", arquivo)
90
-
91
- if not os.path.exists(filename):
92
- return [], f"[ERRO: Arquivo nΓ£o encontrado]", logs
93
-
94
- anexo_info = f"[PDF: {os.path.basename(filename)}]"
95
-
96
- if not filename.lower().endswith(".pdf"):
97
- logs = log_point("Arquivo texto simples detectado", logs)
98
- try:
99
- with open(filename, "r", encoding="utf-8") as f:
100
- texto = f.read()
101
- # Retorna como um ΓΊnico fragmento de texto
102
- return [texto], f"[TXT: {os.path.basename(filename)}]", logs
103
- except:
104
- return [], "[ERRO LEITURA TXT]", logs
105
-
106
- try:
107
- reader = pypdf.PdfReader(filename)
108
- total_pages = len(reader.pages)
109
- logs = log_point(f"PDF carregado: {total_pages} pΓ‘ginas", logs)
110
-
111
- fragments = []
112
- for i in range(0, total_pages, paginas_por_fragmento):
113
- start = i + 1
114
- end = min(i + paginas_por_fragmento, total_pages)
115
-
116
- bloco_texto = ""
117
- for p in range(i, end):
118
- try:
119
- t = reader.pages[p].extract_text() or ""
120
- except Exception as e:
121
- t = f"\n[ERRO_EXTRACT_PAG_{p+1}: {e}]\n"
122
- bloco_texto += f"\n=== PAGINA {p+1}/{total_pages} ===\n{t}\n"
123
-
124
- fragment = (
125
- f"=== FRAG {i//paginas_por_fragmento + 1} "
126
- f"(PÁGS {start}-{end}/{total_pages}) ===\n"
127
- f"{bloco_texto.strip()}"
128
- )
129
- fragments.append(fragment)
130
-
131
- logs = log_point(f"Total de fragmentos criados: {len(fragments)}", logs)
132
- return fragments, anexo_info, logs
133
- except Exception as e:
134
- logs = log_point(f"ERRO PDF: {e}", logs)
135
- return [], f"[ERRO PDF: {str(e)}]", logs
136
-
137
- # ==================== 3. ENGINE DE EXECUÇÃO ====================
138
-
139
- def _extrair_json_possivel(out_raw: str) -> str:
140
- cleaned = out_raw.strip()
141
- idx_abre_col = cleaned.find("[")
142
- idx_abre_obj = cleaned.find("{")
143
-
144
- candidatos = [i for i in [idx_abre_col, idx_abre_obj] if i != -1]
145
- if candidatos:
146
- start = min(candidatos)
147
- cleaned = cleaned[start:]
148
-
149
- cleaned = cleaned.replace("```json", "").replace("```", "")
150
- return cleaned
151
-
152
- def executar_no(timeline, config, fragmento_input=None):
153
- """
154
- FunΓ§Γ£o Worker que serΓ‘ chamada tanto sequencialmente quanto em paralelo.
155
- """
156
- modelo = model_pro if config.get("modelo") == "pro" else model_flash
157
-
158
- if fragmento_input is not None:
159
- input_para_prompt = fragmento_input
160
- else:
161
- input_para_prompt = json.dumps(timeline, ensure_ascii=False, indent=2)
162
-
163
- prompt = (
164
- "--- INPUT PARA O AGENTE ---\n"
165
- f"{input_para_prompt}\n"
166
- "----------------\n"
167
- f"AGENTE: {config['nome']}\n"
168
- f"MISSÃO: {config['missao']}"
169
- )
170
-
171
- try:
172
- # Retry simples para API
173
- for tentativa in range(3):
174
- try:
175
- resp = modelo.generate_content(prompt)
176
- out = resp.text or ""
177
- break
178
- except Exception as e:
179
- if "429" in str(e):
180
- time.sleep(2 * (tentativa + 1))
181
- continue
182
- raise e
183
-
184
- content = out
185
- if config["tipo_saida"] == "json":
186
- cleaned = _extrair_json_possivel(out)
187
- try:
188
- content = json.loads(cleaned)
189
- except:
190
- content = [] # Fallback em caso de erro de parse
191
-
192
- return {"role": "assistant", "agent": config["nome"], "content": content}, None
193
- except Exception as e:
194
- return {"role": "system", "error": str(e)}, str(e)
195
-
196
- # ==================== 4. ORQUESTRADOR ====================
197
-
198
- def orquestrador(texto, arquivo, history, json_config, confext_state):
199
- logs = f"πŸš€ START: {datetime.now().strftime('%H:%M:%S')}\n"
200
- logs = log_point("Orquestrador V44 iniciado", logs)
201
-
202
- # 1. PreparaΓ§Γ£o
203
- if history is None: history = []
204
-
205
- nome_arquivo = os.path.basename(getattr(arquivo, "name", "sem_arquivo")) if arquivo else "sem_arquivo"
206
- hash_op = gerar_hash_arquivo(nome_arquivo + json_config) # Hash baseado no arquivo + protocolo
207
-
208
- # 2. Verifica Cache
209
- cache_existente = carregar_cache(hash_op) if arquivo else None
210
-
211
- if cache_existente:
212
- logs = log_point(f"♻️ Cache encontrado para {nome_arquivo}", logs)
213
- confext_upload = cache_existente["confext_upload"]
214
- timeline = cache_existente.get("timeline", [])
215
- history.append([texto, "βœ… Arquivo carregado do cache! AnΓ‘lise pronta."])
216
- yield history, timeline, logs, confext_upload
217
-
218
- # Se houver texto novo do usuΓ‘rio, seguimos para anΓ‘lise final, senΓ£o paramos
219
- if not texto:
220
- return
221
- else:
222
- # 3. Processamento Normal
223
- fragmentos, anexo_info, logs = ler_anexo_e_fragmentar(
224
- arquivo, paginas_por_fragmento=5, logs=logs
225
- )
226
-
227
- history.append([texto + (" πŸ“Ž" if arquivo else ""), None])
228
- yield history, {}, logs, confext_state
229
-
230
- try:
231
- protocolo = json.loads(json_config)
232
- except Exception as e:
233
- history[-1][1] = "❌ Erro no JSON de Configuração."
234
- yield history, {}, logs, confext_state
235
- return
236
-
237
- timeline = [{"role": "user", "content": texto}]
238
- confext_upload = {
239
- "arquivo": nome_arquivo,
240
- "meta": anexo_info,
241
- "paginas": []
242
- }
243
-
244
- # 4. ExecuΓ§Γ£o Paginador (Paralela)
245
- if protocolo and fragmentos:
246
- cfg_visao = protocolo[0] # Assume que o primeiro Γ© o leitor
247
- logs = log_point(f"Iniciando Leitura Paralela ({MAX_WORKERS} workers) com {cfg_visao['nome']}", logs)
248
- history[-1][1] = f"⏳ Fragmentando e lendo {len(fragmentos)} partes em paralelo..."
249
- yield history, timeline, logs, confext_upload
250
-
251
- resultados_ordenados = [None] * len(fragmentos)
252
-
253
- with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
254
- futures_map = {executor.submit(executar_no, [], cfg_visao, frag): i for i, frag in enumerate(fragmentos)}
255
-
256
- concluidos = 0
257
- for future in as_completed(futures_map):
258
- idx = futures_map[future]
259
- res, erro = future.result()
260
-
261
- if erro:
262
- logs = log_point(f"Erro no frag {idx}: {erro}", logs)
263
- else:
264
- resultados_ordenados[idx] = res["content"]
265
-
266
- concluidos += 1
267
- history[-1][1] = f"⏳ Leitura: {concluidos}/{len(fragmentos)} partes processadas..."
268
- yield history, timeline, logs, confext_upload
269
-
270
- # Consolidar resultados ordenados
271
- for pags in resultados_ordenados:
272
- if pags:
273
- if isinstance(pags, list):
274
- confext_upload["paginas"].extend(pags)
275
- elif isinstance(pags, dict):
276
- confext_upload["paginas"].append(pags)
277
-
278
- logs = log_point(f"Leitura concluΓ­da. Total pΓ‘ginas extraΓ­das: {len(confext_upload['paginas'])}", logs)
279
-
280
- # Salvar Cache apΓ³s a leitura pesada
281
- if arquivo:
282
- salvar_cache(hash_op, {"confext_upload": confext_upload, "timeline": timeline})
283
- logs = log_point("Estado salvo em Cache", logs)
284
-
285
- # Injeta contexto no timeline
286
- timeline.append({
287
- "role": "system",
288
- "agent": "CONFEXT_UPLOAD",
289
- "content": confext_upload
290
- })
291
-
292
- # 5. ExecuΓ§Γ£o dos Agentes de AnΓ‘lise (Sequencial)
293
- restante = protocolo[1:] if protocolo else []
294
-
295
- for cfg in restante:
296
- history[-1][1] = f"βš™οΈ {cfg['nome']} analisando..."
297
- logs = log_point(f"Iniciando agente: {cfg['nome']}", logs)
298
- yield history, timeline, logs, confext_upload
299
-
300
- # Passa timeline atualizada
301
- res, erro = executar_no(timeline, cfg, fragmento_input=None)
302
-
303
- if erro:
304
- logs = log_point(f"Erro agente {cfg['nome']}: {erro}", logs)
305
- else:
306
- timeline.append(res)
307
- if cfg.get("tipo_saida") == "texto":
308
- history[-1][1] = res["content"]
309
-
310
- yield history, timeline, logs, confext_upload
311
-
312
- if not texto and arquivo:
313
- history[-1][1] = "βœ… Documento processado e indexado. Pode fazer perguntas."
314
-
315
- logs = log_point("Processo Finalizado", logs)
316
- yield history, timeline, logs, confext_upload
317
-
318
- # ==================== 5. UI ====================
319
-
320
- def ui_clean():
321
- css = """
322
- footer {display: none !important;}
323
- .contain {border: none !important;}
324
- """
325
-
326
- config_init = carregar_protocolo()
327
-
328
- with gr.Blocks(title="AI Forensics Auto V44", css=css, theme=gr.themes.Soft()) as app:
329
- confext_state = gr.State(value=None)
330
-
331
- with gr.Tabs():
332
- with gr.Tab("πŸ’¬ Investigador"):
333
- chatbot = gr.Chatbot(
334
- label="",
335
- show_label=False,
336
- height=600,
337
- show_copy_button=True,
338
- render_markdown=True,
339
- )
340
-
341
- with gr.Row():
342
- with gr.Column(scale=10):
343
- txt_in = gr.Textbox(
344
- show_label=False,
345
- placeholder="Descreva o caso ou faΓ§a perguntas...",
346
- lines=1,
347
- max_lines=5,
348
- container=False,
349
- )
350
- with gr.Column(scale=1, min_width=50):
351
- file_in = gr.UploadButton(
352
- "πŸ“Ž",
353
- file_types=[".txt", ".md", ".json", ".pdf"],
354
- size="sm",
355
- )
356
- with gr.Column(scale=1, min_width=80):
357
- btn_send = gr.Button("Enviar", variant="primary", size="sm")
358
-
359
- file_status = gr.Markdown("", visible=True)
360
-
361
- def _on_upload(x):
362
- nome = os.path.basename(getattr(x, "name", x))
363
- return f"πŸ“Ž Anexo pronto para anΓ‘lise: {nome}"
364
-
365
- file_in.upload(_on_upload, inputs=file_in, outputs=file_status)
366
-
367
- # --- AQUI ESTÁ A ABA SOLICITADA ---
368
- with gr.Tab("πŸ•΅οΈ Auditoria & Debug"):
369
- gr.Markdown("### 🧠 Processo Interno de Pensamento")
370
- with gr.Row():
371
- out_dna = gr.JSON(label="Timeline da IA (Contexto)")
372
- out_logs = gr.Textbox(label="Logs do Sistema", lines=20)
373
-
374
- gr.Markdown("### πŸ“‚ Dados Estruturados (Confext)")
375
- confext_view = gr.JSON(label="ConteΓΊdo ExtraΓ­do")
376
-
377
- with gr.Tab("βš™οΈ Config"):
378
- with gr.Row():
379
- btn_save = gr.Button("Salvar Config")
380
- lbl_save = gr.Label(show_label=False)
381
- code_json = gr.Code(value=config_init, language="json", label=ARQUIVO_CONFIG)
382
- btn_save.click(salvar_protocolo, code_json, lbl_save)
383
-
384
- def _orq_wrapper(texto, arquivo, history, json_cfg, confext_old):
385
- for h, dna, logs, confext_new in orquestrador(
386
- texto, arquivo, history, json_cfg, confext_old
387
- ):
388
- yield h, dna, logs, confext_new
389
-
390
- triggers = [btn_send.click, txt_in.submit]
391
-
392
- for trig in triggers:
393
- trig(
394
- _orq_wrapper,
395
- inputs=[txt_in, file_in, chatbot, code_json, confext_state],
396
- outputs=[chatbot, out_dna, out_logs, confext_state], # Atualiza aba Debug
397
- ).then(
398
- lambda c: (None, None, "", c)[1:],
399
- inputs=confext_state,
400
- outputs=[txt_in, file_in, file_status, confext_state],
401
- ).then(
402
- lambda c: c,
403
- inputs=confext_state,
404
- outputs=confext_view, # Atualiza visualizador JSON
405
- )
406
-
407
- return app
408
-
409
- if __name__ == "__main__":
410
- ui_clean().launch()