caarleexx commited on
Commit
72c4fc4
·
verified ·
1 Parent(s): 3155dd0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -72
app.py CHANGED
@@ -6,7 +6,8 @@ from datetime import datetime
6
  from concurrent.futures import ThreadPoolExecutor, as_completed
7
 
8
  import gradio as gr
9
- import google.generativeai as genai
 
10
 
11
  # =================================================================================
12
  # METADADOS DO PROJETO - HACKATHON OAB 2025
@@ -48,13 +49,22 @@ except ImportError:
48
 
49
  # ==================== 1. CONFIGURAÇÃO ====================
50
 
51
- api_key = os.getenv("GOOGLE_API_KEY", "SUA_API_KEY_AQUI")
52
- if api_key and api_key != "SUA_API_KEY_AQUI":
53
- genai.configure(api_key=api_key)
54
-
55
- # Modelos do Gemini
56
- model_flash = genai.GenerativeModel("gemini-flash-latest")
57
- model_pro = genai.GenerativeModel("gemini-pro-latest")
 
 
 
 
 
 
 
 
 
58
 
59
  ARQUIVO_CONFIG = "protocolo.json"
60
  ARQUIVO_CONTEXTO_ANTECIPATORIO = "data.txt" # Arquivo com regras/verdade factual
@@ -166,14 +176,11 @@ def processar_pdf_completo(arquivo_pdf):
166
  # ==================== 3. PIPELINE DE IA ====================
167
 
168
  def transcrever_chunk(chunk_data, config_agentes):
169
- # (Implementação existente)
170
- modelo = model_flash
171
- try:
172
- if config_agentes and isinstance(config_agentes, list):
173
- if config_agentes[0].get("modelo") == "pro":
174
- modelo = model_pro
175
- except:
176
- pass
177
 
178
  prompt = f"""
179
  ANÁLISE DE DOCUMENTO (OCR/LEITURA):
@@ -181,22 +188,29 @@ Transcreva e estruture o conteúdo das páginas {chunk_data['paginas']}.
181
  Texto extraído:
182
  {chunk_data['texto']}
183
 
184
- Retorne JSON: {{ "transcricao": "...", "objetos": ["..."], "resumo": "..." }}
185
  """
186
  try:
187
  for tentativa in range(3):
188
  try:
189
- resposta = modelo.generate_content(prompt)
190
- texto_resp = resposta.text.replace("```json", "").replace("```", "")
191
- return json.loads(texto_resp.strip()), None
 
 
 
 
 
 
192
  except Exception as inner_e:
193
- if "429" in str(inner_e):
194
  time.sleep(2 * (tentativa + 1))
195
  continue
196
  raise inner_e
197
  except Exception as e:
198
  return None, str(e)
199
 
 
200
  # ==================== 4. GERENCIADOR DE ARQUIVOS ====================
201
 
202
  class GerenciadorArquivos:
@@ -255,6 +269,11 @@ def automacao_upload_processamento(files, history, config_json):
255
  # (Função de processamento de arquivos)
256
  if not files:
257
  return history
 
 
 
 
 
258
 
259
  try:
260
  config_agentes = json.loads(config_json)
@@ -394,6 +413,11 @@ def automacao_upload_processamento(files, history, config_json):
394
 
395
 
396
  def chat_orquestrador(message, history, config_json, pipeline_state, incluir_passo_antecipatorio):
 
 
 
 
 
397
  if pipeline_state.get("is_paused"):
398
  # (Lógica de continuação após STOP)
399
  history.append([message, None])
@@ -468,16 +492,17 @@ def chat_orquestrador(message, history, config_json, pipeline_state, incluir_pas
468
 
469
 
470
  def executar_pipeline(history, timeline_execucao, agentes_a_executar, pipeline_state):
471
- # (Implementação de execução da pipeline e simulação de digitação)
472
  passo_atual = len(timeline_execucao) + 1
473
 
474
  for i, cfg in enumerate(agentes_a_executar):
475
  nome_agente = cfg.get("nome", "Agente")
476
- modelo_agente = model_pro if cfg.get("modelo") == "pro" else model_flash
 
477
  tipo_saida = cfg.get("tipo_saida", "json")
478
 
479
  msg_atual = history[-1][1] or ""
480
- history[-1][1] = msg_atual + f"⏳ **{nome_agente}** está analisando...\n"
481
  yield history, timeline_execucao, pipeline_state
482
 
483
  prompt_agente = f"""
@@ -486,17 +511,23 @@ def executar_pipeline(history, timeline_execucao, agentes_a_executar, pipeline_s
486
  -----------------
487
  Sua Identidade: {nome_agente}
488
  Sua Missão Específica Agora: {cfg['missao']}
489
- Se o tipo de saída exigido for 'json', sua resposta DEVE ser APENAS o JSON.
490
  """
491
 
492
  try:
493
  inicio = time.time()
494
- resp = modelo_agente.generate_content(prompt_agente)
495
- texto_resp = resp.text
496
- duracao = time.time() - inicio
497
-
498
  # --- LÓGICA DE DETECÇÃO DE SAÍDA NÃO-JSON (STOP ou Relatório) ---
499
  if tipo_saida == "json":
 
 
 
 
 
 
 
 
 
500
  try:
501
  # Tenta interpretar a resposta como JSON
502
  texto_json_limpo = texto_resp.replace("```json", "").replace("```", "").strip()
@@ -505,102 +536,88 @@ Se o tipo de saída exigido for 'json', sua resposta DEVE ser APENAS o JSON.
505
  # 1. LÓGICA DE PAUSA (STOP)
506
  if resposta_json.get("PROXIMA_ACAO") == "PERGUNTAR_USUARIO" and resposta_json.get("DUVIDA_DETECTADA") == True:
507
 
508
- # VERIFICA REGRAS DE STOP - Fora de escopo?
509
  if "STOP:" in texto_resp:
510
- # Caso de STOP por fora do escopo
511
- stop_message = texto_resp.split("STOP:")[1].strip() if "STOP:" in texto_resp else "Análise interrompida por dúvida crítica ou tema fora de escopo."
512
  history[-1][1] = stop_message
513
  yield history, timeline_execucao, pipeline_state
514
  return
515
 
516
- # Caso de STOP por dúvida crítica (regra do protocolo da Fase 0)
517
  perguntas = resposta_json.get("TESTE_REFLEXAO", {}).get("perguntas", [])
518
 
519
- # Formata a resposta STOP
520
- stop_response = "STOP: preciso que você esclareça até 3 pontos antes de continuar:\n\n"
521
- for idx, p in enumerate(perguntas):
522
- if idx < 3:
523
- stop_response += f"{idx + 1}) {p}\n"
524
-
525
- # Salva o estado atual da pipeline
526
  pipeline_state["is_paused"] = True
527
  pipeline_state["timeline"] = timeline_execucao
528
  pipeline_state["remaining_agents"] = agentes_a_executar[i+1:]
529
 
530
- # Adiciona o passo à auditoria e ao chat
531
  timeline_execucao.append({"passo": passo_atual, "tipo": "STOP_USUARIO_REQUERIDO", "agente": nome_agente, "detalhes": stop_response})
532
 
533
- # Prepara a mensagem para o usuário no chat
534
- msg_para_usuario = f"**{nome_agente}** precisa de mais informações. Por favor, responda aos pontos abaixo para seguirmos com a análise:\n\n"
535
  for idx, p in enumerate(perguntas):
536
  if idx < 3:
537
  msg_para_usuario += f"**{idx + 1})** {p}\n"
538
 
539
- msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
540
  history[-1][1] = msg_atual + msg_para_usuario
541
 
542
  yield history, timeline_execucao, pipeline_state
543
- return # Sai da função para esperar o input do usuário
544
 
545
  # 2. Resposta JSON normal
546
  texto_para_auditoria = texto_json_limpo
547
  timeline_execucao.append({"passo": passo_atual, "tipo": "resposta_agente", "agente": nome_agente, "resposta": texto_para_auditoria})
548
 
549
- msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
550
  novo_trecho = f"✅ **[{nome_agente}]** concluiu sua análise em ({duracao:.1f}s). (JSON para Auditoria)\n"
551
  history[-1][1] = msg_atual + novo_trecho
552
  yield history, timeline_execucao, pipeline_state
553
 
554
  except json.JSONDecodeError:
555
- # Se deveria ser JSON e não é, trata como erro na auditoria
556
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_resposta_json", "agente": nome_agente, "resposta_raw": texto_resp, "erro": "Esperado JSON, mas recebeu texto não-JSON."})
557
- msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
558
  history[-1][1] = msg_atual + f"❌ **[{nome_agente}]** falhou: Resposta não era JSON válido. ({duracao:.1f}s).\n"
559
  yield history, timeline_execucao, pipeline_state
560
 
561
  elif tipo_saida == "texto":
562
- # Lógica para a Fase 7 (RELATORIO_VALOR_VIDA)
563
-
564
- # Adiciona o passo à auditoria
565
- timeline_execucao.append({"passo": passo_atual, "tipo": "relatorio_final", "agente": nome_agente, "relatorio": texto_resp})
 
 
 
 
566
 
567
- # Simula a digitação envolvente
568
  msg_final = f"**[RELATÓRIO FINAL DO {nome_agente}]**\n\n"
569
  history[-1][1] = msg_final
570
- yield history, timeline_execucao, pipeline_state
571
 
572
- # Divide o texto do relatório em blocos (parágrafos ou seções)
573
- blocos = texto_resp.split('\n\n')
574
-
575
- for bloco in blocos:
576
- if bloco.strip():
577
- # Simula a digitação bloco por bloco
578
- msg_final += f"{bloco}\n\n"
579
- history[-1][1] = msg_final
580
- # Pequena pausa para o efeito dinâmico
581
- time.sleep(0.5)
582
  yield history, timeline_execucao, pipeline_state
583
-
584
- # Finalização da digitação no chat
585
- msg_final += "\n\n--- FIM DO RELATÓRIO DE VALORAÇÃO ---"
586
- history[-1][1] = msg_final
587
  yield history, timeline_execucao, pipeline_state
588
 
589
  else:
590
- # Caso de tipo_saida não definido (erro)
591
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_config", "agente": nome_agente, "erro": "Tipo de saída não reconhecido ('json' ou 'texto')."})
592
  msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
593
- history[-1][1] = msg_atual + f"\n❌ Erro de Configuração em {nome_agente}: Tipo de saída inválido. ({duracao:.1f}s).\n"
594
  yield history, timeline_execucao, pipeline_state
595
 
596
  except Exception as e:
597
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_agente", "agente": nome_agente, "erro": str(e)})
598
  msg_atual = history[-1][1]
599
- history[-1][1] = msg_atual.replace(f"⏳ **{nome_agente}** está analisando...\n", "") + f"\n❌ Erro em {nome_agente}: {str(e)}\n"
600
  yield history, timeline_execucao, pipeline_state
601
 
602
  passo_atual += 1
603
 
 
604
  # ==================== 6. UI (Gradio) ====================
605
 
606
  def ui_v29_stop_logic():
@@ -687,4 +704,5 @@ def ui_v29_stop_logic():
687
  return app
688
 
689
  if __name__ == "__main__":
690
- ui_v29_stop_logic().launch()
 
 
6
  from concurrent.futures import ThreadPoolExecutor, as_completed
7
 
8
  import gradio as gr
9
+ # ### ALTERADO ### - Import da Groq
10
+ from groq import Groq
11
 
12
  # =================================================================================
13
  # METADADOS DO PROJETO - HACKATHON OAB 2025
 
49
 
50
  # ==================== 1. CONFIGURAÇÃO ====================
51
 
52
+ # ### ALTERADO ### - Configuração da API da Groq
53
+ # Certifique-se de ter a variável de ambiente GROQ_API_KEY definida.
54
+ try:
55
+ groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
56
+ print("✅ Cliente Groq inicializado com sucesso.")
57
+ except Exception as e:
58
+ groq_client = None
59
+ print(f"❌ Erro ao inicializar o cliente Groq. Verifique sua API Key: {e}")
60
+
61
+ # ### ALTERADO ### - Mapeamento de modelos para a Groq
62
+ # "flash" -> Modelo rápido para tarefas de sumarização e extração.
63
+ # "pro" -> Modelo mais robusto para análise e raciocínio complexo.
64
+ GROQ_MODELS = {
65
+ "flash": "llama3-8b-8192",
66
+ "pro": "llama3-70b-8192"
67
+ }
68
 
69
  ARQUIVO_CONFIG = "protocolo.json"
70
  ARQUIVO_CONTEXTO_ANTECIPATORIO = "data.txt" # Arquivo com regras/verdade factual
 
176
  # ==================== 3. PIPELINE DE IA ====================
177
 
178
  def transcrever_chunk(chunk_data, config_agentes):
179
+ # ### ALTERADO ### - Função adaptada para usar a API da Groq
180
+ if not groq_client:
181
+ return None, "Cliente Groq não inicializado."
182
+
183
+ modelo_groq = GROQ_MODELS["flash"] # Usa sempre o modelo mais rápido para transcrição
 
 
 
184
 
185
  prompt = f"""
186
  ANÁLISE DE DOCUMENTO (OCR/LEITURA):
 
188
  Texto extraído:
189
  {chunk_data['texto']}
190
 
191
+ Retorne APENAS o JSON com a seguinte estrutura: {{ "transcricao": "...", "objetos": ["..."], "resumo": "..." }}
192
  """
193
  try:
194
  for tentativa in range(3):
195
  try:
196
+ chat_completion = groq_client.chat.completions.create(
197
+ messages=[{"role": "user", "content": prompt}],
198
+ model=modelo_groq,
199
+ temperature=0.1,
200
+ max_tokens=4096,
201
+ )
202
+ texto_resp = chat_completion.choices[0].message.content
203
+ texto_limpo = texto_resp.replace("```json", "").replace("```", "").strip()
204
+ return json.loads(texto_limpo), None
205
  except Exception as inner_e:
206
+ if "429" in str(inner_e): # Lida com Rate Limiting
207
  time.sleep(2 * (tentativa + 1))
208
  continue
209
  raise inner_e
210
  except Exception as e:
211
  return None, str(e)
212
 
213
+
214
  # ==================== 4. GERENCIADOR DE ARQUIVOS ====================
215
 
216
  class GerenciadorArquivos:
 
269
  # (Função de processamento de arquivos)
270
  if not files:
271
  return history
272
+
273
+ if not groq_client:
274
+ history.append([None, "⚠️ **SISTEMA:** Cliente Groq não configurado. Verifique a API Key e reinicie a aplicação."])
275
+ yield history
276
+ return
277
 
278
  try:
279
  config_agentes = json.loads(config_json)
 
413
 
414
 
415
  def chat_orquestrador(message, history, config_json, pipeline_state, incluir_passo_antecipatorio):
416
+ if not groq_client:
417
+ history.append([message, "⚠️ **SISTEMA:** Cliente Groq não configurado. Verifique a API Key e reinicie a aplicação."])
418
+ yield history, [], pipeline_state
419
+ return
420
+
421
  if pipeline_state.get("is_paused"):
422
  # (Lógica de continuação após STOP)
423
  history.append([message, None])
 
492
 
493
 
494
  def executar_pipeline(history, timeline_execucao, agentes_a_executar, pipeline_state):
495
+ # ### ALTERADO ### - Lógica de execução da pipeline adaptada para Groq e streaming real
496
  passo_atual = len(timeline_execucao) + 1
497
 
498
  for i, cfg in enumerate(agentes_a_executar):
499
  nome_agente = cfg.get("nome", "Agente")
500
+ # Seleciona o modelo Groq com base na configuração do agente
501
+ modelo_agente = GROQ_MODELS.get(cfg.get("modelo"), GROQ_MODELS["flash"])
502
  tipo_saida = cfg.get("tipo_saida", "json")
503
 
504
  msg_atual = history[-1][1] or ""
505
+ history[-1][1] = msg_atual + f"⏳ **{nome_agente}** está analisando (modelo: `{modelo_agente}`)...\n"
506
  yield history, timeline_execucao, pipeline_state
507
 
508
  prompt_agente = f"""
 
511
  -----------------
512
  Sua Identidade: {nome_agente}
513
  Sua Missão Específica Agora: {cfg['missao']}
514
+ Se o tipo de saída exigido for 'json', sua resposta DEVE ser APENAS o JSON. Se for 'texto', responda de forma discursiva.
515
  """
516
 
517
  try:
518
  inicio = time.time()
519
+
 
 
 
520
  # --- LÓGICA DE DETECÇÃO DE SAÍDA NÃO-JSON (STOP ou Relatório) ---
521
  if tipo_saida == "json":
522
+ chat_completion = groq_client.chat.completions.create(
523
+ messages=[{"role": "user", "content": prompt_agente}],
524
+ model=modelo_agente,
525
+ temperature=0.1,
526
+ max_tokens=8192,
527
+ )
528
+ texto_resp = chat_completion.choices[0].message.content
529
+ duracao = time.time() - inicio
530
+
531
  try:
532
  # Tenta interpretar a resposta como JSON
533
  texto_json_limpo = texto_resp.replace("```json", "").replace("```", "").strip()
 
536
  # 1. LÓGICA DE PAUSA (STOP)
537
  if resposta_json.get("PROXIMA_ACAO") == "PERGUNTAR_USUARIO" and resposta_json.get("DUVIDA_DETECTADA") == True:
538
 
 
539
  if "STOP:" in texto_resp:
540
+ stop_message = texto_resp.split("STOP:")[1].strip() if "STOP:" in texto_resp else "Análise interrompida por dúvida crítica."
 
541
  history[-1][1] = stop_message
542
  yield history, timeline_execucao, pipeline_state
543
  return
544
 
 
545
  perguntas = resposta_json.get("TESTE_REFLEXAO", {}).get("perguntas", [])
546
 
 
 
 
 
 
 
 
547
  pipeline_state["is_paused"] = True
548
  pipeline_state["timeline"] = timeline_execucao
549
  pipeline_state["remaining_agents"] = agentes_a_executar[i+1:]
550
 
551
+ stop_response = "STOP: preciso que você esclareça pontos antes de continuar."
552
  timeline_execucao.append({"passo": passo_atual, "tipo": "STOP_USUARIO_REQUERIDO", "agente": nome_agente, "detalhes": stop_response})
553
 
554
+ msg_para_usuario = f"**{nome_agente}** precisa de mais informações. Por favor, responda aos pontos abaixo:\n\n"
 
555
  for idx, p in enumerate(perguntas):
556
  if idx < 3:
557
  msg_para_usuario += f"**{idx + 1})** {p}\n"
558
 
559
+ msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando (modelo: `{modelo_agente}`)...\n", "")
560
  history[-1][1] = msg_atual + msg_para_usuario
561
 
562
  yield history, timeline_execucao, pipeline_state
563
+ return
564
 
565
  # 2. Resposta JSON normal
566
  texto_para_auditoria = texto_json_limpo
567
  timeline_execucao.append({"passo": passo_atual, "tipo": "resposta_agente", "agente": nome_agente, "resposta": texto_para_auditoria})
568
 
569
+ msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando (modelo: `{modelo_agente}`)...\n", "")
570
  novo_trecho = f"✅ **[{nome_agente}]** concluiu sua análise em ({duracao:.1f}s). (JSON para Auditoria)\n"
571
  history[-1][1] = msg_atual + novo_trecho
572
  yield history, timeline_execucao, pipeline_state
573
 
574
  except json.JSONDecodeError:
 
575
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_resposta_json", "agente": nome_agente, "resposta_raw": texto_resp, "erro": "Esperado JSON, mas recebeu texto não-JSON."})
576
+ msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando (modelo: `{modelo_agente}`)...\n", "")
577
  history[-1][1] = msg_atual + f"❌ **[{nome_agente}]** falhou: Resposta não era JSON válido. ({duracao:.1f}s).\n"
578
  yield history, timeline_execucao, pipeline_state
579
 
580
  elif tipo_saida == "texto":
581
+ # Lógica para a Fase 7 com STREAMING REAL
582
+ stream = groq_client.chat.completions.create(
583
+ messages=[{"role": "user", "content": prompt_agente}],
584
+ model=modelo_agente,
585
+ temperature=0.7,
586
+ max_tokens=8192,
587
+ stream=True,
588
+ )
589
 
 
590
  msg_final = f"**[RELATÓRIO FINAL DO {nome_agente}]**\n\n"
591
  history[-1][1] = msg_final
592
+ relatorio_completo = ""
593
 
594
+ for chunk in stream:
595
+ delta = chunk.choices[0].delta.content or ""
596
+ if delta:
597
+ relatorio_completo += delta
598
+ history[-1][1] = msg_final + relatorio_completo
 
 
 
 
 
599
  yield history, timeline_execucao, pipeline_state
600
+
601
+ # Adiciona o passo completo à auditoria no final
602
+ timeline_execucao.append({"passo": passo_atual, "tipo": "relatorio_final", "agente": nome_agente, "relatorio": relatorio_completo})
603
+ history[-1][1] += "\n\n--- FIM DO RELATÓRIO DE VALORAÇÃO ---"
604
  yield history, timeline_execucao, pipeline_state
605
 
606
  else:
 
607
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_config", "agente": nome_agente, "erro": "Tipo de saída não reconhecido ('json' ou 'texto')."})
608
  msg_atual = history[-1][1].replace(f"⏳ **{nome_agente}** está analisando...\n", "")
609
+ history[-1][1] = msg_atual + f"\n❌ Erro de Configuração em {nome_agente}: Tipo de saída inválido.\n"
610
  yield history, timeline_execucao, pipeline_state
611
 
612
  except Exception as e:
613
  timeline_execucao.append({"passo": passo_atual, "tipo": "erro_agente", "agente": nome_agente, "erro": str(e)})
614
  msg_atual = history[-1][1]
615
+ history[-1][1] = msg_atual.replace(f"⏳ **{nome_agente}** está analisando (modelo: `{modelo_agente}`)...\n", "") + f"\n❌ Erro em {nome_agente}: {str(e)}\n"
616
  yield history, timeline_execucao, pipeline_state
617
 
618
  passo_atual += 1
619
 
620
+
621
  # ==================== 6. UI (Gradio) ====================
622
 
623
  def ui_v29_stop_logic():
 
704
  return app
705
 
706
  if __name__ == "__main__":
707
+ app = ui_v29_stop_logic()
708
+ app.launch()