Update app.py
Browse files
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 |
-
|
|
|
|
| 10 |
|
| 11 |
# =================================================================================
|
| 12 |
# METADADOS DO PROJETO - HACKATHON OAB 2025
|
|
@@ -48,13 +49,22 @@ except ImportError:
|
|
| 48 |
|
| 49 |
# ==================== 1. CONFIGURAÇÃO ====================
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 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 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 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 |
-
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 531 |
timeline_execucao.append({"passo": passo_atual, "tipo": "STOP_USUARIO_REQUERIDO", "agente": nome_agente, "detalhes": stop_response})
|
| 532 |
|
| 533 |
-
|
| 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
|
| 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
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 571 |
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 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 |
-
#
|
| 585 |
-
|
| 586 |
-
history[-1][1]
|
| 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
|
| 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()
|
|
|
|
|
|
| 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()
|