caarleexx commited on
Commit
e4db54e
·
verified ·
1 Parent(s): c37e8e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -37
app.py CHANGED
@@ -3,6 +3,8 @@ import json
3
  import time
4
  import hashlib
5
  from datetime import datetime
 
 
6
  import gradio as gr
7
  import google.generativeai as genai
8
 
@@ -26,6 +28,7 @@ model_pro = genai.GenerativeModel("gemini-pro-latest")
26
  ARQUIVO_CONFIG = "protocolo.json"
27
  PASTA_TRANSCRICOES = "transcricoes"
28
  PAGES_PER_CHUNK = 10
 
29
 
30
  os.makedirs(PASTA_TRANSCRICOES, exist_ok=True)
31
 
@@ -51,7 +54,6 @@ def salvar_protocolo(conteudo):
51
  return "❌ Erro JSON"
52
 
53
  def limpar_nome_arquivo(nome):
54
- # Remove caracteres inválidos para nome de arquivo e garante extensão .json
55
  nome_base = os.path.basename(nome)
56
  nome_limpo = "".join([c for c in nome_base if c.isalnum() or c in (' ', '.', '_', '-')]).strip()
57
  return nome_limpo + ".json"
@@ -129,6 +131,7 @@ def ler_arquivo_texto(arquivo):
129
  # ==================== 3. PIPELINE DE IA ====================
130
 
131
  def transcrever_chunk(chunk_data, config_agentes):
 
132
  modelo = model_flash
133
  try:
134
  if config_agentes and isinstance(config_agentes, list):
@@ -146,9 +149,17 @@ Texto extraído:
146
  Retorne JSON: {{ "transcricao": "...", "objetos": ["..."], "resumo": "..." }}
147
  """
148
  try:
149
- resposta = modelo.generate_content(prompt)
150
- texto_resp = resposta.text.replace("```json", "").replace("```", "")
151
- return json.loads(texto_resp.strip()), None
 
 
 
 
 
 
 
 
152
  except Exception as e:
153
  return None, str(e)
154
 
@@ -178,13 +189,13 @@ class GerenciadorArquivos:
178
  prompt += f"\n[ARQUIVO: {nome}]\n"
179
 
180
  if isinstance(trans, dict) and "chunks_processados" in trans:
 
181
  for chunk in trans["chunks_processados"]:
182
  if chunk.get("status") == "OK":
183
  resumo = chunk.get('resumo', '')
184
  resumo = str(resumo) if resumo else ""
185
  prompt += f"Páginas {chunk['paginas']}: {resumo}\n"
186
 
187
- # Correção do erro de slice
188
  texto_full = chunk.get('transcricao', '')
189
  if texto_full:
190
  texto_seguro = str(texto_full)
@@ -236,27 +247,21 @@ def automacao_upload_processamento(files, history, config_json):
236
  caminho_cache = os.path.join(PASTA_TRANSCRICOES, nome_cache)
237
 
238
  if os.path.exists(caminho_cache):
239
- # Se já existe, carrega e pula o processamento
240
  try:
241
  with open(caminho_cache, "r", encoding="utf-8") as cache_file:
242
  dados_cache = json.load(cache_file)
243
-
244
  item["transcricao"] = dados_cache
245
  item["status"] = "processado"
246
-
247
- # Simula estrutura de processamento básico se for PDF para manter compatibilidade
248
  if nome.lower().endswith('.pdf') and "chunks_processados" in dados_cache:
249
- item["processado"] = {"tipo": "pdf", "chunks": []} # Simplificado pois já temos a transcrição
250
-
251
- history.append([None, f"♻️ **Cache Encontrado:** `{nome}` já foi processado anteriormente. Carregando..."])
252
  yield history
253
- continue # Pula para o próximo arquivo
254
  except Exception as e:
255
- history.append([None, f"⚠️ Erro ao ler cache de `{nome}`: {e}. Reprocessando..."])
256
-
257
- # --- FIM VERIFICAÇÃO ---
258
 
259
- history.append([None, f"⚙️ **Processando (Nova Transcrição):** `{nome}`..."])
260
  yield history
261
 
262
  if nome.lower().endswith('.pdf'):
@@ -274,44 +279,67 @@ def automacao_upload_processamento(files, history, config_json):
274
  item["processado"] = pdf_proc
275
  chunks = pdf_proc["chunks"]
276
  total_chunks = len(chunks)
277
- chunks_processados = []
278
 
279
- history.append([None, f"���� `{nome}` fragmentado em {total_chunks} partes. Iniciando IA..."])
 
 
 
280
  yield history
281
 
282
- for i, chunk in enumerate(chunks):
283
- res, err = transcrever_chunk(chunk, config_agentes)
284
- if err:
285
- chunks_processados.append({"status": "ERRO", "paginas": chunk["paginas"]})
286
- else:
287
- chunks_processados.append({
288
- "status": "OK",
289
- "paginas": chunk["paginas"],
290
- "transcricao": res.get("transcricao"),
291
- "resumo": res.get("resumo")
292
- })
293
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  dados_finais = {
295
  "arquivo": nome,
296
  "data_processamento": str(datetime.now()),
297
- "chunks_processados": chunks_processados
298
  }
299
 
300
  item["transcricao"] = dados_finais
301
  item["status"] = "processado"
302
 
303
- # --- SALVAR NO CACHE ---
304
  try:
305
  with open(caminho_cache, "w", encoding="utf-8") as f_out:
306
  json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
307
  history.append([None, f"💾 `{nome}` processado e salvo no cache."])
308
  except Exception as e:
309
  history.append([None, f"⚠️ Erro ao salvar cache: {e}"])
310
- # -----------------------
311
-
312
  yield history
313
 
314
  else:
 
315
  res = ler_arquivo_texto(item["arquivo"])
316
  if res:
317
  item["processado"] = res
@@ -319,7 +347,6 @@ def automacao_upload_processamento(files, history, config_json):
319
  item["transcricao"] = dados_finais
320
  item["status"] = "processado"
321
 
322
- # Salvar Cache Texto
323
  with open(caminho_cache, "w", encoding="utf-8") as f_out:
324
  json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
325
 
@@ -333,7 +360,6 @@ def automacao_upload_processamento(files, history, config_json):
333
 
334
 
335
  def chat_orquestrador(message, history, config_json):
336
- # 1. Montar Prompt (Agora com correção no Gerenciador)
337
  try:
338
  prompt_contexto = gerenciador.gerar_prompt_com_transcricoes(message)
339
  except Exception as e:
 
3
  import time
4
  import hashlib
5
  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
 
 
28
  ARQUIVO_CONFIG = "protocolo.json"
29
  PASTA_TRANSCRICOES = "transcricoes"
30
  PAGES_PER_CHUNK = 10
31
+ MAX_WORKERS = 5 # Limite de chamadas paralelas
32
 
33
  os.makedirs(PASTA_TRANSCRICOES, exist_ok=True)
34
 
 
54
  return "❌ Erro JSON"
55
 
56
  def limpar_nome_arquivo(nome):
 
57
  nome_base = os.path.basename(nome)
58
  nome_limpo = "".join([c for c in nome_base if c.isalnum() or c in (' ', '.', '_', '-')]).strip()
59
  return nome_limpo + ".json"
 
131
  # ==================== 3. PIPELINE DE IA ====================
132
 
133
  def transcrever_chunk(chunk_data, config_agentes):
134
+ # Função auxiliar para ser executada na thread
135
  modelo = model_flash
136
  try:
137
  if config_agentes and isinstance(config_agentes, list):
 
149
  Retorne JSON: {{ "transcricao": "...", "objetos": ["..."], "resumo": "..." }}
150
  """
151
  try:
152
+ # Retry simples em caso de erro 429 (rate limit)
153
+ for tentativa in range(3):
154
+ try:
155
+ resposta = modelo.generate_content(prompt)
156
+ texto_resp = resposta.text.replace("```json", "").replace("```", "")
157
+ return json.loads(texto_resp.strip()), None
158
+ except Exception as inner_e:
159
+ if "429" in str(inner_e):
160
+ time.sleep(2 * (tentativa + 1))
161
+ continue
162
+ raise inner_e
163
  except Exception as e:
164
  return None, str(e)
165
 
 
189
  prompt += f"\n[ARQUIVO: {nome}]\n"
190
 
191
  if isinstance(trans, dict) and "chunks_processados" in trans:
192
+ # Como garantimos a ordem na lista chunks_processados, iteramos normalmente
193
  for chunk in trans["chunks_processados"]:
194
  if chunk.get("status") == "OK":
195
  resumo = chunk.get('resumo', '')
196
  resumo = str(resumo) if resumo else ""
197
  prompt += f"Páginas {chunk['paginas']}: {resumo}\n"
198
 
 
199
  texto_full = chunk.get('transcricao', '')
200
  if texto_full:
201
  texto_seguro = str(texto_full)
 
247
  caminho_cache = os.path.join(PASTA_TRANSCRICOES, nome_cache)
248
 
249
  if os.path.exists(caminho_cache):
 
250
  try:
251
  with open(caminho_cache, "r", encoding="utf-8") as cache_file:
252
  dados_cache = json.load(cache_file)
 
253
  item["transcricao"] = dados_cache
254
  item["status"] = "processado"
 
 
255
  if nome.lower().endswith('.pdf') and "chunks_processados" in dados_cache:
256
+ item["processado"] = {"tipo": "pdf", "chunks": []}
257
+ history.append([None, f"♻️ **Cache Encontrado:** `{nome}` já foi processado. Carregando..."])
 
258
  yield history
259
+ continue
260
  except Exception as e:
261
+ history.append([None, f"⚠️ Erro cache `{nome}`: {e}. Reprocessando..."])
262
+ # ---------------------------
 
263
 
264
+ history.append([None, f"⚙️ **Processando:** `{nome}`..."])
265
  yield history
266
 
267
  if nome.lower().endswith('.pdf'):
 
279
  item["processado"] = pdf_proc
280
  chunks = pdf_proc["chunks"]
281
  total_chunks = len(chunks)
 
282
 
283
+ # Inicializa lista com o tamanho exato para garantir a ordem
284
+ chunks_ordenados = [None] * total_chunks
285
+
286
+ history.append([None, f"📄 `{nome}` fragmentado em {total_chunks} partes. Iniciando IA (Paralelo: {MAX_WORKERS} threads)..."])
287
  yield history
288
 
289
+ # --- PROCESSAMENTO PARALELO ---
290
+ with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
291
+ # Dicionário para mapear Future -> Índice Original
292
+ futures_map = {}
293
+
294
+ # Submeter todas as tarefas
295
+ for i, chunk in enumerate(chunks):
296
+ future = executor.submit(transcrever_chunk, chunk, config_agentes)
297
+ futures_map[future] = i
298
+
299
+ # Coletar resultados conforme ficam prontos
300
+ concluidos = 0
301
+ for future in as_completed(futures_map):
302
+ index_original = futures_map[future]
303
+ res, err = future.result()
304
+
305
+ if err:
306
+ chunks_ordenados[index_original] = {"status": "ERRO", "paginas": chunks[index_original]["paginas"]}
307
+ else:
308
+ chunks_ordenados[index_original] = {
309
+ "status": "OK",
310
+ "paginas": chunks[index_original]["paginas"],
311
+ "transcricao": res.get("transcricao"),
312
+ "resumo": res.get("resumo")
313
+ }
314
+
315
+ concluidos += 1
316
+ # Atualiza a UI a cada 2 chunks ou no final para não flodar
317
+ if concluidos % 2 == 0 or concluidos == total_chunks:
318
+ msg_base = f"📄 `{nome}`: Processando partes... ({concluidos}/{total_chunks})"
319
+ history[-1][1] = msg_base
320
+ yield history
321
+ # ------------------------------
322
+
323
  dados_finais = {
324
  "arquivo": nome,
325
  "data_processamento": str(datetime.now()),
326
+ "chunks_processados": chunks_ordenados # Agora contém a lista na ordem correta
327
  }
328
 
329
  item["transcricao"] = dados_finais
330
  item["status"] = "processado"
331
 
 
332
  try:
333
  with open(caminho_cache, "w", encoding="utf-8") as f_out:
334
  json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
335
  history.append([None, f"💾 `{nome}` processado e salvo no cache."])
336
  except Exception as e:
337
  history.append([None, f"⚠️ Erro ao salvar cache: {e}"])
338
+
 
339
  yield history
340
 
341
  else:
342
+ # Processamento de Texto Simples (não precisa de paralelismo pois é 1 chunk)
343
  res = ler_arquivo_texto(item["arquivo"])
344
  if res:
345
  item["processado"] = res
 
347
  item["transcricao"] = dados_finais
348
  item["status"] = "processado"
349
 
 
350
  with open(caminho_cache, "w", encoding="utf-8") as f_out:
351
  json.dump(dados_finais, f_out, indent=2, ensure_ascii=False)
352
 
 
360
 
361
 
362
  def chat_orquestrador(message, history, config_json):
 
363
  try:
364
  prompt_contexto = gerenciador.gerar_prompt_com_transcricoes(message)
365
  except Exception as e: