victorafarias commited on
Commit
6d1db38
·
1 Parent(s): f975500

Melhorias no metodo Atomico

Browse files
Files changed (3) hide show
  1. app.py +119 -69
  2. config.py +2 -0
  3. templates/index.html +56 -6
app.py CHANGED
@@ -46,6 +46,36 @@ def log_print(message):
46
  print(f"[DEBUG] {message}", flush=True)
47
  sys.stdout.flush()
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  # Função para renderização com fallback: tenta MarkdownIt, depois markdown2
50
  def render_markdown_cascata(texto: str) -> str:
51
  try:
@@ -97,6 +127,18 @@ def convert():
97
  converted_html = render_markdown_cascata(text_to_convert)
98
  return jsonify({'html': converted_html})
99
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  @app.route('/process', methods=['POST'])
101
  def process():
102
  """Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
@@ -127,19 +169,24 @@ def process():
127
  if current_mode == 'test':
128
  log_print("=== MODO TESTE EXECUTADO ===")
129
  mock_text = form_data.get('mock_text', 'Este é um **texto** de `simulação`.')
130
- yield f"data: {json.dumps({'progress': 100, 'message': 'Simulação concluída!', 'partial_result': {'id': 'grok-output', 'content': mock_text}, 'done': True, 'mode': 'atomic' if processing_mode == 'atomic' else 'hierarchical'})}\n\n"
 
131
  if processing_mode == 'atomic':
132
- yield f"data: {json.dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_text}})}\n\n"
133
- yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': mock_text}})}\n\n"
 
 
134
  else:
135
  if not solicitacao_usuario:
136
  log_print("=== ERRO: SOLICITAÇÃO VAZIA ===")
137
- yield f"data: {json.dumps({'error': 'Solicitação não fornecida.'})}\n\n"
 
138
  return
139
 
140
  try:
141
  log_print("=== INICIANDO PROCESSAMENTO REAL ===")
142
- yield f"data: {json.dumps({'progress': 0, 'message': 'Processando arquivos e extraindo contexto...'})}\n\n"
 
143
  rag_context = get_relevant_context(file_paths, solicitacao_usuario)
144
  log_print(f"=== RAG CONTEXT OBTIDO: {len(rag_context)} chars ===")
145
 
@@ -172,7 +219,8 @@ def process():
172
  models = {'grok': grok_llm, 'sonnet': claude_atomic_llm, 'gemini': gemini_llm}
173
 
174
  prompt = PromptTemplate(template=PROMPT_ATOMICO_INICIAL, input_variables=["solicitacao_usuario", "rag_context"])
175
- yield f"data: {json.dumps({'progress': 15, 'message': 'Iniciando processamento paralelo...'})}\n\n"
 
176
 
177
  for name, llm in models.items():
178
  chain = prompt | llm | output_parser
@@ -186,30 +234,37 @@ def process():
186
  for key, result in results.items():
187
  if result == "Error:EmptyResponse" or "Erro ao processar" in result:
188
  error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
189
- yield f"data: {json.dumps({'error': error_msg})}\n\n"
 
190
  return
191
 
192
- yield f"data: {json.dumps({'progress': 80, 'message': 'Todos os modelos responderam. Formatando saídas...'})}\n\n"
 
193
 
194
  # MUDANÇA: Envia o texto bruto para cada modelo
195
  grok_text = results.get('grok', '')
196
  log_print(f"--- Resposta Bruta do GROK (Atômico) ---\n{grok_text[:200]}...\n--------------------------------------")
197
- yield f"data: {json.dumps({'partial_result': {'id': 'grok-output', 'content': grok_text}})}\n\n"
 
198
 
199
  sonnet_text = results.get('sonnet', '')
200
  log_print(f"--- Resposta Bruta do Sonnet (Atômico) ---\n{sonnet_text[:200]}...\n----------------------------------------")
201
- yield f"data: {json.dumps({'partial_result': {'id': 'sonnet-output', 'content': sonnet_text}})}\n\n"
 
202
 
203
  gemini_text = results.get('gemini', '')
204
  log_print(f"--- Resposta Bruta do Gemini (Atômico) ---\n{gemini_text[:200]}...\n----------------------------------------")
205
- yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': gemini_text}})}\n\n"
 
206
 
207
- yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento Atômico concluído!', 'done': True, 'mode': 'atomic'})}\n\n"
 
208
 
209
  else:
210
  log_print("=== MODO HIERÁRQUICO SELECIONADO ===")
211
  # --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
212
- yield f"data: {json.dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})}\n\n"
 
213
 
214
  log_print("=== PROCESSANDO GROK ===")
215
  prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
@@ -220,11 +275,13 @@ def process():
220
 
221
  if not resposta_grok or not resposta_grok.strip():
222
  log_print("=== ERRO: GROK VAZIO ===")
223
- yield f"data: {json.dumps({'error': 'Falha no serviço GROK: Sem resposta.'})}\n\n"
 
224
  return
225
 
226
  log_print("=== ENVIANDO RESPOSTA GROK PARA FRONTEND ===")
227
- yield f"data: {json.dumps({'progress': 33, 'message': 'Claude Sonnet está processando...', 'partial_result': {'id': 'grok-output', 'content': resposta_grok}})}\n\n"
 
228
 
229
  log_print("=== PROCESSANDO SONNET ===")
230
  prompt_sonnet = PromptTemplate(template=PROMPT_HIERARQUICO_SONNET, input_variables=["solicitacao_usuario", "texto_para_analise"])
@@ -236,52 +293,13 @@ def process():
236
 
237
  if not resposta_sonnet or not resposta_sonnet.strip():
238
  log_print("=== ERRO: SONNET VAZIO ===")
239
- yield f"data: {json.dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta.'})}\n\n"
 
240
  return
241
 
242
- log_print("=== TENTANDO ENVIAR RESPOSTA SONNET ===")
243
-
244
- # Aqui vamos testar se o problema é no JSON
245
- try:
246
- # Primeiro, vamos testar criar o JSON sem enviar
247
- test_data = {
248
- 'progress': 66,
249
- 'message': 'Gemini está processando...',
250
- 'partial_result': {
251
- 'id': 'sonnet-output',
252
- 'content': resposta_sonnet
253
- }
254
- }
255
- test_json = json.dumps(test_data)
256
- log_print(f"=== JSON SONNET CRIADO COM SUCESSO: {len(test_json)} chars ===")
257
-
258
- # Agora vamos enviar
259
- yield f"data: {test_json}\n\n"
260
- log_print("=== JSON SONNET ENVIADO COM SUCESSO ===")
261
-
262
- except Exception as json_error:
263
- log_print(f"=== ERRO JSON SONNET: {json_error} ===")
264
- log_print(f"=== TIPO DA RESPOSTA SONNET: {type(resposta_sonnet)} ===")
265
- log_print(f"=== PRIMEIROS 500 CHARS SONNET: {repr(resposta_sonnet[:500])} ===")
266
-
267
- # Vamos tentar limpar a string
268
- try:
269
- # Remove caracteres problemáticos
270
- cleaned_response = resposta_sonnet.encode('utf-8', errors='ignore').decode('utf-8')
271
- fallback_data = {
272
- 'progress': 66,
273
- 'message': 'Gemini está processando...',
274
- 'partial_result': {
275
- 'id': 'sonnet-output',
276
- 'content': cleaned_response
277
- }
278
- }
279
- yield f"data: {json.dumps(fallback_data)}\n\n"
280
- log_print("=== FALLBACK SONNET ENVIADO ===")
281
- except Exception as fallback_error:
282
- log_print(f"=== FALLBACK TAMBÉM FALHOU: {fallback_error} ===")
283
- yield f"data: {json.dumps({'error': f'Erro ao processar resposta do Sonnet: {str(json_error)}'})}\n\n"
284
- return
285
 
286
  log_print("=== PROCESSANDO GEMINI ===")
287
  prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
@@ -292,30 +310,40 @@ def process():
292
 
293
  if not resposta_gemini or not resposta_gemini.strip():
294
  log_print("=== ERRO: GEMINI VAZIO ===")
295
- yield f"data: {json.dumps({'error': 'Falha no serviço Gemini: Sem resposta.'})}\n\n"
 
296
  return
297
 
298
  log_print("=== ENVIANDO RESPOSTA GEMINI ===")
299
- yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento concluído!', 'partial_result': {'id': 'gemini-output', 'content': resposta_gemini}, 'done': True, 'mode': 'hierarchical'})}\n\n"
 
300
  log_print("=== PROCESSAMENTO COMPLETO ===")
301
 
302
  except Exception as e:
303
  log_print(f"Ocorreu um erro durante o processamento: {e}")
304
  import traceback
305
  log_print(f"Traceback: {traceback.format_exc()}")
306
- yield f"data: {json.dumps({'error': f'Ocorreu um erro inesperado na aplicação: {e}'})}\n\n"
 
307
 
308
  return Response(generate_stream(mode, form_data, temp_file_paths), mimetype='text/event-stream')
309
 
310
  @app.route('/merge', methods=['POST'])
311
  def merge():
312
  """Recebe os textos do modo Atômico e os consolida usando um LLM."""
 
 
313
  data = request.get_json()
 
314
 
315
  def generate_merge_stream():
316
  """Gera a resposta do merge em streaming."""
 
 
317
  try:
318
- yield f"data: {json.dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})}\n\n"
 
 
319
 
320
  output_parser = StrOutputParser()
321
  prompt_merge = PromptTemplate(template=PROMPT_ATOMICO_MERGE, input_variables=["solicitacao_usuario", "texto_para_analise_grok", "texto_para_analise_sonnet", "texto_para_analise_gemini"])
@@ -323,7 +351,9 @@ def merge():
323
  grok_with_max_tokens = grok_llm.bind(max_tokens=20000)
324
  chain_merge = prompt_merge | grok_with_max_tokens | output_parser
325
 
326
- yield f"data: {json.dumps({'progress': 50, 'message': 'Enviando textos para o GROK para consolidação...'})}\n\n"
 
 
327
 
328
  resposta_merge = chain_merge.invoke({
329
  "solicitacao_usuario": data.get('solicitacao_usuario'),
@@ -332,19 +362,39 @@ def merge():
332
  "texto_para_analise_gemini": data.get('gemini_text')
333
  })
334
 
 
 
335
  if not resposta_merge or not resposta_merge.strip():
336
- yield f"data: {json.dumps({'error': 'Falha no serviço de Merge (GROK): Sem resposta.'})}\n\n"
 
337
  return
338
 
339
- log_print(f"--- Resposta Bruta do Merge (GROK) ---\n{resposta_merge}\n------------------------------------")
 
340
  word_count = len(resposta_merge.split())
341
 
342
- # MUDANÇA: Envia o texto bruto do merge em vez de HTML
343
- yield f"data: {json.dumps({'progress': 100, 'message': 'Merge concluído!', 'final_result': {'content': resposta_merge, 'word_count': word_count}, 'done': True})}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
344
 
345
  except Exception as e:
346
  log_print(f"Erro no processo de merge: {e}")
347
- yield f"data: {json.dumps({'error': str(e)})}\n\n"
 
 
 
348
 
349
  return Response(generate_merge_stream(), mimetype='text/event-stream')
350
 
 
46
  print(f"[DEBUG] {message}", flush=True)
47
  sys.stdout.flush()
48
 
49
+ def safe_json_dumps(data):
50
+ """Função para criar JSON de forma segura, com tratamento de strings muito grandes"""
51
+ try:
52
+ json_str = json.dumps(data, ensure_ascii=False)
53
+
54
+ # Se o JSON for muito grande (> 50MB), trunca o conteúdo
55
+ if len(json_str.encode('utf-8')) > 50 * 1024 * 1024:
56
+ log_print(f"JSON muito grande ({len(json_str)} chars), truncando...")
57
+ if 'content' in str(data):
58
+ # Cria uma versão truncada
59
+ truncated_data = data.copy() if isinstance(data, dict) else {}
60
+ if 'final_result' in truncated_data and 'content' in truncated_data['final_result']:
61
+ original_content = truncated_data['final_result']['content']
62
+ truncated_content = original_content[:10000] + "\n\n[CONTEÚDO TRUNCADO DEVIDO AO TAMANHO - Use o botão 'Copiar' para obter o texto completo]"
63
+ truncated_data['final_result']['content'] = truncated_content
64
+ elif 'partial_result' in truncated_data and 'content' in truncated_data['partial_result']:
65
+ original_content = truncated_data['partial_result']['content']
66
+ truncated_content = original_content[:10000] + "\n\n[CONTEÚDO TRUNCADO DEVIDO AO TAMANHO - Use o botão 'Copiar' para obter o texto completo]"
67
+ truncated_data['partial_result']['content'] = truncated_content
68
+
69
+ return json.dumps(truncated_data, ensure_ascii=False)
70
+
71
+ return json_str
72
+ except Exception as e:
73
+ log_print(f"Erro ao criar JSON: {e}")
74
+ return json.dumps({'error': f'Erro na serialização JSON: {str(e)}'})
75
+
76
+ # Variável global para armazenar o conteúdo completo do merge
77
+ merge_full_content = ""
78
+
79
  # Função para renderização com fallback: tenta MarkdownIt, depois markdown2
80
  def render_markdown_cascata(texto: str) -> str:
81
  try:
 
127
  converted_html = render_markdown_cascata(text_to_convert)
128
  return jsonify({'html': converted_html})
129
 
130
+ # NOVA ROTA: Para obter o conteúdo completo do merge
131
+ @app.route('/get-full-content', methods=['POST'])
132
+ def get_full_content():
133
+ global merge_full_content
134
+ data = request.get_json()
135
+ content_type = data.get('type', 'merge')
136
+
137
+ if content_type == 'merge' and merge_full_content:
138
+ return jsonify({'content': merge_full_content})
139
+ else:
140
+ return jsonify({'error': 'Conteúdo não encontrado'}), 404
141
+
142
  @app.route('/process', methods=['POST'])
143
  def process():
144
  """Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
 
169
  if current_mode == 'test':
170
  log_print("=== MODO TESTE EXECUTADO ===")
171
  mock_text = form_data.get('mock_text', 'Este é um **texto** de `simulação`.')
172
+ json_data = safe_json_dumps({'progress': 100, 'message': 'Simulação concluída!', 'partial_result': {'id': 'grok-output', 'content': mock_text}, 'done': True, 'mode': 'atomic' if processing_mode == 'atomic' else 'hierarchical'})
173
+ yield f"data: {json_data}\n\n"
174
  if processing_mode == 'atomic':
175
+ json_data = safe_json_dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_text}})
176
+ yield f"data: {json_data}\n\n"
177
+ json_data = safe_json_dumps({'partial_result': {'id': 'gemini-output', 'content': mock_text}})
178
+ yield f"data: {json_data}\n\n"
179
  else:
180
  if not solicitacao_usuario:
181
  log_print("=== ERRO: SOLICITAÇÃO VAZIA ===")
182
+ json_data = safe_json_dumps({'error': 'Solicitação não fornecida.'})
183
+ yield f"data: {json_data}\n\n"
184
  return
185
 
186
  try:
187
  log_print("=== INICIANDO PROCESSAMENTO REAL ===")
188
+ json_data = safe_json_dumps({'progress': 0, 'message': 'Processando arquivos e extraindo contexto...'})
189
+ yield f"data: {json_data}\n\n"
190
  rag_context = get_relevant_context(file_paths, solicitacao_usuario)
191
  log_print(f"=== RAG CONTEXT OBTIDO: {len(rag_context)} chars ===")
192
 
 
219
  models = {'grok': grok_llm, 'sonnet': claude_atomic_llm, 'gemini': gemini_llm}
220
 
221
  prompt = PromptTemplate(template=PROMPT_ATOMICO_INICIAL, input_variables=["solicitacao_usuario", "rag_context"])
222
+ json_data = safe_json_dumps({'progress': 15, 'message': 'Iniciando processamento paralelo...'})
223
+ yield f"data: {json_data}\n\n"
224
 
225
  for name, llm in models.items():
226
  chain = prompt | llm | output_parser
 
234
  for key, result in results.items():
235
  if result == "Error:EmptyResponse" or "Erro ao processar" in result:
236
  error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
237
+ json_data = safe_json_dumps({'error': error_msg})
238
+ yield f"data: {json_data}\n\n"
239
  return
240
 
241
+ json_data = safe_json_dumps({'progress': 80, 'message': 'Todos os modelos responderam. Formatando saídas...'})
242
+ yield f"data: {json_data}\n\n"
243
 
244
  # MUDANÇA: Envia o texto bruto para cada modelo
245
  grok_text = results.get('grok', '')
246
  log_print(f"--- Resposta Bruta do GROK (Atômico) ---\n{grok_text[:200]}...\n--------------------------------------")
247
+ json_data = safe_json_dumps({'partial_result': {'id': 'grok-output', 'content': grok_text}})
248
+ yield f"data: {json_data}\n\n"
249
 
250
  sonnet_text = results.get('sonnet', '')
251
  log_print(f"--- Resposta Bruta do Sonnet (Atômico) ---\n{sonnet_text[:200]}...\n----------------------------------------")
252
+ json_data = safe_json_dumps({'partial_result': {'id': 'sonnet-output', 'content': sonnet_text}})
253
+ yield f"data: {json_data}\n\n"
254
 
255
  gemini_text = results.get('gemini', '')
256
  log_print(f"--- Resposta Bruta do Gemini (Atômico) ---\n{gemini_text[:200]}...\n----------------------------------------")
257
+ json_data = safe_json_dumps({'partial_result': {'id': 'gemini-output', 'content': gemini_text}})
258
+ yield f"data: {json_data}\n\n"
259
 
260
+ json_data = safe_json_dumps({'progress': 100, 'message': 'Processamento Atômico concluído!', 'done': True, 'mode': 'atomic'})
261
+ yield f"data: {json_data}\n\n"
262
 
263
  else:
264
  log_print("=== MODO HIERÁRQUICO SELECIONADO ===")
265
  # --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
266
+ json_data = safe_json_dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})
267
+ yield f"data: {json_data}\n\n"
268
 
269
  log_print("=== PROCESSANDO GROK ===")
270
  prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
 
275
 
276
  if not resposta_grok or not resposta_grok.strip():
277
  log_print("=== ERRO: GROK VAZIO ===")
278
+ json_data = safe_json_dumps({'error': 'Falha no serviço GROK: Sem resposta.'})
279
+ yield f"data: {json_data}\n\n"
280
  return
281
 
282
  log_print("=== ENVIANDO RESPOSTA GROK PARA FRONTEND ===")
283
+ json_data = safe_json_dumps({'progress': 33, 'message': 'Claude Sonnet está processando...', 'partial_result': {'id': 'grok-output', 'content': resposta_grok}})
284
+ yield f"data: {json_data}\n\n"
285
 
286
  log_print("=== PROCESSANDO SONNET ===")
287
  prompt_sonnet = PromptTemplate(template=PROMPT_HIERARQUICO_SONNET, input_variables=["solicitacao_usuario", "texto_para_analise"])
 
293
 
294
  if not resposta_sonnet or not resposta_sonnet.strip():
295
  log_print("=== ERRO: SONNET VAZIO ===")
296
+ json_data = safe_json_dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta.'})
297
+ yield f"data: {json_data}\n\n"
298
  return
299
 
300
+ log_print("=== ENVIANDO RESPOSTA SONNET ===")
301
+ json_data = safe_json_dumps({'progress': 66, 'message': 'Gemini está processando...', 'partial_result': {'id': 'sonnet-output', 'content': resposta_sonnet}})
302
+ yield f"data: {json_data}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
  log_print("=== PROCESSANDO GEMINI ===")
305
  prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
 
310
 
311
  if not resposta_gemini or not resposta_gemini.strip():
312
  log_print("=== ERRO: GEMINI VAZIO ===")
313
+ json_data = safe_json_dumps({'error': 'Falha no serviço Gemini: Sem resposta.'})
314
+ yield f"data: {json_data}\n\n"
315
  return
316
 
317
  log_print("=== ENVIANDO RESPOSTA GEMINI ===")
318
+ json_data = safe_json_dumps({'progress': 100, 'message': 'Processamento concluído!', 'partial_result': {'id': 'gemini-output', 'content': resposta_gemini}, 'done': True, 'mode': 'hierarchical'})
319
+ yield f"data: {json_data}\n\n"
320
  log_print("=== PROCESSAMENTO COMPLETO ===")
321
 
322
  except Exception as e:
323
  log_print(f"Ocorreu um erro durante o processamento: {e}")
324
  import traceback
325
  log_print(f"Traceback: {traceback.format_exc()}")
326
+ json_data = safe_json_dumps({'error': f'Ocorreu um erro inesperado na aplicação: {e}'})
327
+ yield f"data: {json_data}\n\n"
328
 
329
  return Response(generate_stream(mode, form_data, temp_file_paths), mimetype='text/event-stream')
330
 
331
  @app.route('/merge', methods=['POST'])
332
  def merge():
333
  """Recebe os textos do modo Atômico e os consolida usando um LLM."""
334
+ global merge_full_content
335
+
336
  data = request.get_json()
337
+ log_print("=== ROTA MERGE ACESSADA ===")
338
 
339
  def generate_merge_stream():
340
  """Gera a resposta do merge em streaming."""
341
+ global merge_full_content
342
+
343
  try:
344
+ log_print("=== INICIANDO MERGE STREAM ===")
345
+ json_data = safe_json_dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})
346
+ yield f"data: {json_data}\n\n"
347
 
348
  output_parser = StrOutputParser()
349
  prompt_merge = PromptTemplate(template=PROMPT_ATOMICO_MERGE, input_variables=["solicitacao_usuario", "texto_para_analise_grok", "texto_para_analise_sonnet", "texto_para_analise_gemini"])
 
351
  grok_with_max_tokens = grok_llm.bind(max_tokens=20000)
352
  chain_merge = prompt_merge | grok_with_max_tokens | output_parser
353
 
354
+ json_data = safe_json_dumps({'progress': 50, 'message': 'Enviando textos para o GROK para consolidação...'})
355
+ yield f"data: {json_data}\n\n"
356
+ log_print("=== INVOCANDO CHAIN DE MERGE ===")
357
 
358
  resposta_merge = chain_merge.invoke({
359
  "solicitacao_usuario": data.get('solicitacao_usuario'),
 
362
  "texto_para_analise_gemini": data.get('gemini_text')
363
  })
364
 
365
+ log_print(f"=== MERGE CONCLUÍDO: {len(resposta_merge)} chars ===")
366
+
367
  if not resposta_merge or not resposta_merge.strip():
368
+ json_data = safe_json_dumps({'error': 'Falha no serviço de Merge (GROK): Sem resposta.'})
369
+ yield f"data: {json_data}\n\n"
370
  return
371
 
372
+ # Armazena o conteúdo completo na variável global
373
+ merge_full_content = resposta_merge
374
  word_count = len(resposta_merge.split())
375
 
376
+ log_print(f"=== CRIANDO JSON DE RESPOSTA DO MERGE ===")
377
+ # Usa a função safe para evitar problemas com JSON muito grande
378
+ json_data = safe_json_dumps({
379
+ 'progress': 100,
380
+ 'message': 'Merge concluído!',
381
+ 'final_result': {
382
+ 'content': resposta_merge,
383
+ 'word_count': word_count
384
+ },
385
+ 'done': True
386
+ })
387
+
388
+ log_print(f"=== JSON CRIADO: {len(json_data)} chars ===")
389
+ yield f"data: {json_data}\n\n"
390
+ log_print("=== MERGE STREAM FINALIZADO ===")
391
 
392
  except Exception as e:
393
  log_print(f"Erro no processo de merge: {e}")
394
+ import traceback
395
+ log_print(f"Traceback: {traceback.format_exc()}")
396
+ json_data = safe_json_dumps({'error': str(e)})
397
+ yield f"data: {json_data}\n\n"
398
 
399
  return Response(generate_merge_stream(), mimetype='text/event-stream')
400
 
config.py CHANGED
@@ -89,6 +89,7 @@ PROMPT_HIERARQUICO_SONNET = """
89
 
90
  <restricoes>
91
  <proibido>fazer reduções do texto</proibido>
 
92
  <proibido>fazer resumos</proibido>
93
  <proibido>encurtar o conteúdo original</proibido>
94
  <proibido>usar expressões características de IA como "Não é mera..., mas é..."</proibido>
@@ -152,6 +153,7 @@ PROMPT_HIERARQUICO_GEMINI = """
152
  <proibido>fazer reduções do texto</proibido>
153
  <proibido>fazer resumos</proibido>
154
  <proibido>encurtar o conteúdo original</proibido>
 
155
  <proibido>usar expressões características de IA como "Não é mera..., mas é..."</proibido>
156
  </restricoes>
157
 
 
89
 
90
  <restricoes>
91
  <proibido>fazer reduções do texto</proibido>
92
+ <proibido>elaborar um texto com mais de 30000 caracteres</proibido>
93
  <proibido>fazer resumos</proibido>
94
  <proibido>encurtar o conteúdo original</proibido>
95
  <proibido>usar expressões características de IA como "Não é mera..., mas é..."</proibido>
 
153
  <proibido>fazer reduções do texto</proibido>
154
  <proibido>fazer resumos</proibido>
155
  <proibido>encurtar o conteúdo original</proibido>
156
+ <proibido>elaborar um texto com mais de 30000 caracteres</proibido>
157
  <proibido>usar expressões características de IA como "Não é mera..., mas é..."</proibido>
158
  </restricoes>
159
 
templates/index.html CHANGED
@@ -276,6 +276,18 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
276
  // Processa qualquer dado restante no buffer
277
  if (buffer.trim()) {
278
  debugLog(`Buffer final: ${buffer}`);
 
 
 
 
 
 
 
 
 
 
 
 
279
  }
280
 
281
  } catch (error) {
@@ -301,7 +313,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
301
  sonnet_text: rawTexts['sonnet-output'] || '',
302
  gemini_text: rawTexts['gemini-output'] || '',
303
  };
304
- debugLog(`Payload do merge: ${JSON.stringify({...payload, grok_length: payload.grok_text.length, sonnet_length: payload.sonnet_text.length, gemini_length: payload.gemini_text.length})}`);
305
 
306
  try {
307
  const response = await fetch('/merge', {
@@ -311,25 +323,62 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
311
  });
312
  if (!response.ok || !response.body) throw new Error(`Erro na resposta do servidor: ${response.statusText}`);
313
 
 
314
  const reader = response.body.getReader();
315
  const decoder = new TextDecoder();
 
 
316
  while (true) {
317
  const { done, value } = await reader.read();
318
- if (done) break;
 
 
 
 
319
  const chunk = decoder.decode(value, { stream: true });
320
- const lines = chunk.split('\n\n');
 
 
 
 
 
 
321
  lines.forEach(line => {
322
  if (line.startsWith('data: ')) {
323
- const jsonData = line.substring(6);
324
- if (jsonData.trim()) {
 
325
  try {
326
  const data = JSON.parse(jsonData);
 
327
  processStreamData(data, true); // true = é merge
328
- } catch (e) { console.error("Erro ao parsear JSON do merge:", jsonData); }
 
 
 
329
  }
330
  }
331
  });
332
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  } catch (error) {
334
  debugLog(`ERRO NO MERGE: ${error.message}`);
335
  showError("A conexão falhou ao tentar processar o merge.");
@@ -467,5 +516,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
467
  }
468
  }
469
  </script>
 
470
  </body>
471
  </html>
 
276
  // Processa qualquer dado restante no buffer
277
  if (buffer.trim()) {
278
  debugLog(`Buffer final: ${buffer}`);
279
+ // Tenta processar o buffer final como uma linha completa
280
+ if (buffer.startsWith('data: ')) {
281
+ const jsonData = buffer.substring(6).trim();
282
+ if (jsonData) {
283
+ try {
284
+ const data = JSON.parse(jsonData);
285
+ processStreamData(data, false);
286
+ } catch (e) {
287
+ console.error("Erro ao parsear JSON do buffer final:", jsonData.substring(0, 200));
288
+ }
289
+ }
290
+ }
291
  }
292
 
293
  } catch (error) {
 
313
  sonnet_text: rawTexts['sonnet-output'] || '',
314
  gemini_text: rawTexts['gemini-output'] || '',
315
  };
316
+ debugLog(`Payload do merge preparado com textos de tamanho: G=${payload.grok_text.length}, S=${payload.sonnet_text.length}, G=${payload.gemini_text.length}`);
317
 
318
  try {
319
  const response = await fetch('/merge', {
 
323
  });
324
  if (!response.ok || !response.body) throw new Error(`Erro na resposta do servidor: ${response.statusText}`);
325
 
326
+ debugLog("=== MERGE FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
327
  const reader = response.body.getReader();
328
  const decoder = new TextDecoder();
329
+ let buffer = ''; // Buffer para dados incompletos do merge
330
+
331
  while (true) {
332
  const { done, value } = await reader.read();
333
+ if (done) {
334
+ debugLog("=== MERGE STREAM FINALIZADO ===");
335
+ break;
336
+ }
337
+
338
  const chunk = decoder.decode(value, { stream: true });
339
+ buffer += chunk;
340
+ debugLog(`Merge chunk recebido: ${chunk.length} chars`);
341
+
342
+ // Processa todas as linhas completas no buffer
343
+ let lines = buffer.split('\n\n');
344
+ buffer = lines.pop(); // Mantém a última linha (possivelmente incompleta) no buffer
345
+
346
  lines.forEach(line => {
347
  if (line.startsWith('data: ')) {
348
+ const jsonData = line.substring(6).trim();
349
+ if (jsonData) {
350
+ debugLog(`Merge JSON recebido: ${jsonData.substring(0, 200)}...`);
351
  try {
352
  const data = JSON.parse(jsonData);
353
+ debugLog(`Merge dados processados: ${JSON.stringify({progress: data.progress, message: data.message, hasFinalResult: !!data.final_result, error: data.error})}`);
354
  processStreamData(data, true); // true = é merge
355
+ } catch (e) {
356
+ console.error("Erro ao parsear JSON do merge:", jsonData.substring(0, 500));
357
+ console.error("Erro:", e);
358
+ }
359
  }
360
  }
361
  });
362
  }
363
+
364
+ // Processa qualquer dado restante no buffer do merge
365
+ if (buffer.trim()) {
366
+ debugLog(`Merge buffer final: ${buffer.substring(0, 200)}...`);
367
+ if (buffer.startsWith('data: ')) {
368
+ const jsonData = buffer.substring(6).trim();
369
+ if (jsonData) {
370
+ try {
371
+ const data = JSON.parse(jsonData);
372
+ debugLog("Processando buffer final do merge");
373
+ processStreamData(data, true);
374
+ } catch (e) {
375
+ console.error("Erro ao parsear JSON do merge buffer final:", jsonData.substring(0, 500));
376
+ console.error("Erro:", e);
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
  } catch (error) {
383
  debugLog(`ERRO NO MERGE: ${error.message}`);
384
  showError("A conexão falhou ao tentar processar o merge.");
 
516
  }
517
  }
518
  </script>
519
+
520
  </body>
521
  </html>