victorafarias commited on
Commit
9ccb4c7
·
1 Parent(s): d23c0dc

Correçõe na conversão posterior do texto para md

Browse files
Files changed (1) hide show
  1. app.py +153 -188
app.py CHANGED
@@ -11,6 +11,8 @@ from html import escape, unescape
11
  import re
12
  from markdown_it import MarkdownIt
13
  from markdown2 import markdown as markdown2_render
 
 
14
 
15
  # Importações do LangChain
16
  from langchain.prompts import PromptTemplate
@@ -70,29 +72,16 @@ def is_html_empty(html: str) -> bool:
70
  # 4. Verifica se o texto restante (após remover espaços nas pontas) está de fato vazio
71
  return not normalized_space.strip()
72
 
73
- def safe_json_dumps(data):
74
- """Função para criar JSON de forma segura, escapando caracteres problemáticos"""
75
- try:
76
- return json.dumps(data, ensure_ascii=False)
77
- except Exception as e:
78
- print(f"Erro ao criar JSON: {e}")
79
- # Fallback: tenta escapar o conteúdo problemático
80
- if 'content' in str(data):
81
- # Substitui o conteúdo por uma versão escapada
82
- safe_data = data.copy() if isinstance(data, dict) else {}
83
- if 'partial_result' in safe_data and 'content' in safe_data['partial_result']:
84
- safe_data['partial_result']['content'] = safe_data['partial_result']['content'][:100] + "... [CONTEÚDO TRUNCADO DEVIDO A ERRO DE ENCODING]"
85
- return json.dumps(safe_data, ensure_ascii=False)
86
- return json.dumps({'error': 'Erro na serialização JSON'})
87
-
88
  @app.route('/')
89
  def index():
90
  """Renderiza a página inicial da aplicação."""
 
91
  return render_template('index.html')
92
 
93
  # ROTA ATUALIZADA: Para converter texto em Markdown sob demanda
94
  @app.route('/convert', methods=['POST'])
95
  def convert():
 
96
  data = request.get_json()
97
  if not data or 'text' not in data:
98
  return jsonify({'error': 'Nenhum texto fornecido'}), 400
@@ -105,195 +94,174 @@ def convert():
105
  @app.route('/process', methods=['POST'])
106
  def process():
107
  """Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
108
- form_data = request.form
109
- files = request.files.getlist('files')
110
- mode = form_data.get('mode', 'real')
111
- processing_mode = form_data.get('processing_mode', 'hierarchical')
112
 
113
- temp_file_paths = []
114
- if mode == 'real':
115
- for file in files:
116
- if file and file.filename:
117
- unique_filename = str(uuid.uuid4()) + "_" + os.path.basename(file.filename)
118
- file_path = os.path.join('uploads', unique_filename)
119
- file.save(file_path)
120
- temp_file_paths.append(file_path)
121
-
122
- def generate_stream(current_mode, form_data, file_paths):
123
- """Gera a resposta em streaming para o front-end."""
124
- solicitacao_usuario = form_data.get('solicitacao', '')
125
 
126
- if current_mode == 'test':
127
- mock_text = form_data.get('mock_text', 'Este é um **texto** de `simulação`.')
128
- # MUDANÇA: Envia o texto bruto na simulação
129
- 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'})
130
- yield f"data: {json_data}\n\n"
131
- if processing_mode == 'atomic':
132
- json_data = safe_json_dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_text}})
133
- yield f"data: {json_data}\n\n"
134
- json_data = safe_json_dumps({'partial_result': {'id': 'gemini-output', 'content': mock_text}})
135
- yield f"data: {json_data}\n\n"
136
- else:
137
- if not solicitacao_usuario:
138
- json_data = safe_json_dumps({'error': 'Solicitação não fornecida.'})
139
- yield f"data: {json_data}\n\n"
140
- return
141
-
 
 
 
 
 
 
 
 
 
 
142
  try:
143
- json_data = safe_json_dumps({'progress': 0, 'message': 'Processando arquivos e extraindo contexto...'})
144
- yield f"data: {json_data}\n\n"
145
- rag_context = get_relevant_context(file_paths, solicitacao_usuario)
146
 
147
- output_parser = StrOutputParser()
148
-
149
- if processing_mode == 'atomic':
150
- # --- LÓGICA ATÔMICA (PARALELA) ---
151
- results = {}
152
- threads = []
153
-
154
- def run_chain_with_timeout(chain, inputs, key, timeout=300):
155
- def task():
156
- return chain.invoke(inputs)
157
-
158
- with concurrent.futures.ThreadPoolExecutor() as executor:
159
- future = executor.submit(task)
160
- try:
161
- result = future.result(timeout=timeout)
162
- if not result or not result.strip():
163
- results[key] = "Error:EmptyResponse"
164
- else:
165
- results[key] = result
166
- except concurrent.futures.TimeoutError:
167
- results[key] = f"Erro ao processar {key.upper()}: Tempo limite excedido."
168
- except Exception as e:
169
- results[key] = f"Erro ao processar {key.upper()}: {e}"
170
 
171
- claude_atomic_llm = claude_llm.bind(max_tokens=20000)
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
- json_data = safe_json_dumps({'progress': 15, 'message': 'Iniciando processamento paralelo...'})
176
- yield f"data: {json_data}\n\n"
177
 
178
- for name, llm in models.items():
179
- chain = prompt | llm | output_parser
180
- thread = threading.Thread(target=run_chain_with_timeout, args=(chain, {"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context}, name))
181
- threads.append(thread)
182
- thread.start()
183
 
184
- for thread in threads:
185
- thread.join()
 
 
186
 
187
- for key, result in results.items():
188
- if result == "Error:EmptyResponse" or "Erro ao processar" in result:
189
- error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
190
- json_data = safe_json_dumps({'error': error_msg})
191
- yield f"data: {json_data}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  return
193
 
194
- json_data = safe_json_dumps({'progress': 80, 'message': 'Todos os modelos responderam. Formatando saídas...'})
195
- yield f"data: {json_data}\n\n"
196
-
197
- # MUDANÇA: Envia o texto bruto para cada modelo
198
- grok_text = results.get('grok', '')
199
- print(f"--- Resposta Bruta do GROK (Atômico) ---\n{grok_text}\n--------------------------------------")
200
- json_data = safe_json_dumps({'partial_result': {'id': 'grok-output', 'content': grok_text}})
201
- yield f"data: {json_data}\n\n"
202
-
203
- sonnet_text = results.get('sonnet', '')
204
- print(f"--- Resposta Bruta do Sonnet (Atômico) ---\n{sonnet_text}\n----------------------------------------")
205
- json_data = safe_json_dumps({'partial_result': {'id': 'sonnet-output', 'content': sonnet_text}})
206
- yield f"data: {json_data}\n\n"
207
-
208
- gemini_text = results.get('gemini', '')
209
- print(f"--- Resposta Bruta do Gemini (Atômico) ---\n{gemini_text}\n----------------------------------------")
210
- json_data = safe_json_dumps({'partial_result': {'id': 'gemini-output', 'content': gemini_text}})
211
- yield f"data: {json_data}\n\n"
212
-
213
- json_data = safe_json_dumps({'progress': 100, 'message': 'Processamento Atômico concluído!', 'done': True, 'mode': 'atomic'})
214
- yield f"data: {json_data}\n\n"
215
-
216
- else:
217
- # --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
218
- print("=== INICIANDO MODO HIERÁRQUICO ===")
219
- json_data = safe_json_dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})
220
- yield f"data: {json_data}\n\n"
221
-
222
- prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
223
- chain_grok = prompt_grok | grok_llm | output_parser
224
- resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})
225
-
226
- if not resposta_grok or not resposta_grok.strip():
227
- json_data = safe_json_dumps({'error': 'Falha no serviço GROK: Sem resposta.'})
228
- yield f"data: {json_data}\n\n"
229
- return
230
-
231
- print(f"--- Resposta Bruta do GROK (Hierárquico) ---\nTamanho: {len(resposta_grok)} caracteres\nPrimeiros 200 chars: {resposta_grok[:200]}...\n------------------------------------------")
232
-
233
- # Enviando resposta do GROK
234
- json_data = safe_json_dumps({'progress': 33, 'message': 'Claude Sonnet está processando...', 'partial_result': {'id': 'grok-output', 'content': resposta_grok}})
235
- print(f"=== JSON DO GROK CRIADO COM SUCESSO ===")
236
- yield f"data: {json_data}\n\n"
237
-
238
- print("=== INICIANDO PROCESSAMENTO DO SONNET ===")
239
- prompt_sonnet = PromptTemplate(template=PROMPT_HIERARQUICO_SONNET, input_variables=["solicitacao_usuario", "texto_para_analise"])
240
- claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
241
- chain_sonnet = prompt_sonnet | claude_with_max_tokens | output_parser
242
- resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})
243
-
244
- if not resposta_sonnet or not resposta_sonnet.strip():
245
- json_data = safe_json_dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta.'})
246
- yield f"data: {json_data}\n\n"
247
- return
248
-
249
- print(f"--- Resposta Bruta do Sonnet (Hierárquico) ---\nTamanho: {len(resposta_sonnet)} caracteres\nPrimeiros 200 chars: {resposta_sonnet[:200]}...\n--------------------------------------------")
250
-
251
- # Tentando criar JSON do SONNET
252
- print("=== TENTANDO CRIAR JSON DO SONNET ===")
253
- try:
254
- sonnet_json_data = {'progress': 66, 'message': 'Gemini está processando...', 'partial_result': {'id': 'sonnet-output', 'content': resposta_sonnet}}
255
- json_data = safe_json_dumps(sonnet_json_data)
256
- print(f"=== JSON DO SONNET CRIADO COM SUCESSO ===")
257
- yield f"data: {json_data}\n\n"
258
- except Exception as json_error:
259
- print(f"=== ERRO AO CRIAR JSON DO SONNET: {json_error} ===")
260
- # Fallback: enviar com conteúdo truncado
261
- fallback_data = {'progress': 66, 'message': 'Gemini está processando...', 'partial_result': {'id': 'sonnet-output', 'content': resposta_sonnet[:1000] + "... [TRUNCADO]"}}
262
- json_data = safe_json_dumps(fallback_data)
263
- yield f"data: {json_data}\n\n"
264
-
265
- print("=== INICIANDO PROCESSAMENTO DO GEMINI ===")
266
- prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
267
- chain_gemini = prompt_gemini | gemini_llm | output_parser
268
- resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})
269
-
270
- if not resposta_gemini or not resposta_gemini.strip():
271
- json_data = safe_json_dumps({'error': 'Falha no serviço Gemini: Sem resposta.'})
272
- yield f"data: {json_data}\n\n"
273
- return
274
 
275
- print(f"--- Resposta Bruta do Gemini (Hierárquico) ---\nTamanho: {len(resposta_gemini)} caracteres\nPrimeiros 200 chars: {resposta_gemini[:200]}...\n--------------------------------------------")
276
-
277
- json_data = safe_json_dumps({'progress': 100, 'message': 'Processamento concluído!', 'partial_result': {'id': 'gemini-output', 'content': resposta_gemini}, 'done': True, 'mode': 'hierarchical'})
278
- yield f"data: {json_data}\n\n"
279
 
280
  except Exception as e:
281
- print(f"Ocorreu um erro durante o processamento: {e}")
282
- json_data = safe_json_dumps({'error': f'Ocorreu um erro inesperado na aplicação: {e}'})
283
- yield f"data: {json_data}\n\n"
284
 
285
- return Response(generate_stream(mode, form_data, temp_file_paths), mimetype='text/event-stream')
 
 
 
 
 
286
 
287
  @app.route('/merge', methods=['POST'])
288
  def merge():
289
  """Recebe os textos do modo Atômico e os consolida usando um LLM."""
 
290
  data = request.get_json()
291
 
292
  def generate_merge_stream():
293
  """Gera a resposta do merge em streaming."""
294
  try:
295
- json_data = safe_json_dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})
296
- yield f"data: {json_data}\n\n"
297
 
298
  output_parser = StrOutputParser()
299
  prompt_merge = PromptTemplate(template=PROMPT_ATOMICO_MERGE, input_variables=["solicitacao_usuario", "texto_para_analise_grok", "texto_para_analise_sonnet", "texto_para_analise_gemini"])
@@ -301,8 +269,7 @@ def merge():
301
  grok_with_max_tokens = grok_llm.bind(max_tokens=20000)
302
  chain_merge = prompt_merge | grok_with_max_tokens | output_parser
303
 
304
- json_data = safe_json_dumps({'progress': 50, 'message': 'Enviando textos para o GROK para consolidação...'})
305
- yield f"data: {json_data}\n\n"
306
 
307
  resposta_merge = chain_merge.invoke({
308
  "solicitacao_usuario": data.get('solicitacao_usuario'),
@@ -312,22 +279,20 @@ def merge():
312
  })
313
 
314
  if not resposta_merge or not resposta_merge.strip():
315
- json_data = safe_json_dumps({'error': 'Falha no serviço de Merge (GROK): Sem resposta.'})
316
- yield f"data: {json_data}\n\n"
317
  return
318
 
319
  print(f"--- Resposta Bruta do Merge (GROK) ---\n{resposta_merge}\n------------------------------------")
320
  word_count = len(resposta_merge.split())
321
 
322
- json_data = safe_json_dumps({'progress': 100, 'message': 'Merge concluído!', 'final_result': {'content': resposta_merge, 'word_count': word_count}, 'done': True})
323
- yield f"data: {json_data}\n\n"
324
 
325
  except Exception as e:
326
  print(f"Erro no processo de merge: {e}")
327
- json_data = safe_json_dumps({'error': str(e)})
328
- yield f"data: {json_data}\n\n"
329
 
330
  return Response(generate_merge_stream(), mimetype='text/event-stream')
331
 
332
  if __name__ == '__main__':
 
333
  app.run(debug=True)
 
11
  import re
12
  from markdown_it import MarkdownIt
13
  from markdown2 import markdown as markdown2_render
14
+ import traceback
15
+ import sys
16
 
17
  # Importações do LangChain
18
  from langchain.prompts import PromptTemplate
 
72
  # 4. Verifica se o texto restante (após remover espaços nas pontas) está de fato vazio
73
  return not normalized_space.strip()
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  @app.route('/')
76
  def index():
77
  """Renderiza a página inicial da aplicação."""
78
+ print("=== ROTA INDEX ACESSADA ===")
79
  return render_template('index.html')
80
 
81
  # ROTA ATUALIZADA: Para converter texto em Markdown sob demanda
82
  @app.route('/convert', methods=['POST'])
83
  def convert():
84
+ print("=== ROTA CONVERT ACESSADA ===")
85
  data = request.get_json()
86
  if not data or 'text' not in data:
87
  return jsonify({'error': 'Nenhum texto fornecido'}), 400
 
94
  @app.route('/process', methods=['POST'])
95
  def process():
96
  """Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
97
+ print("=== ROTA PROCESS ACESSADA ===")
98
+ print(f"Method: {request.method}")
99
+ print(f"Content-Type: {request.content_type}")
 
100
 
101
+ try:
102
+ form_data = request.form
103
+ print(f"Form data keys: {list(form_data.keys())}")
104
+ print(f"Form data: {dict(form_data)}")
 
 
 
 
 
 
 
 
105
 
106
+ files = request.files.getlist('files')
107
+ print(f"Files: {len(files)} arquivo(s)")
108
+
109
+ mode = form_data.get('mode', 'real')
110
+ processing_mode = form_data.get('processing_mode', 'hierarchical')
111
+
112
+ print(f"Mode: {mode}")
113
+ print(f"Processing mode: {processing_mode}")
114
+
115
+ temp_file_paths = []
116
+ if mode == 'real':
117
+ for file in files:
118
+ if file and file.filename:
119
+ print(f"Processando arquivo: {file.filename}")
120
+ unique_filename = str(uuid.uuid4()) + "_" + os.path.basename(file.filename)
121
+ file_path = os.path.join('uploads', unique_filename)
122
+ file.save(file_path)
123
+ temp_file_paths.append(file_path)
124
+ print(f"Arquivo salvo em: {file_path}")
125
+
126
+ print("=== INICIANDO GENERATOR STREAM ===")
127
+
128
+ def generate_stream(current_mode, form_data, file_paths):
129
+ """Gera a resposta em streaming para o front-end."""
130
+ print(f"=== DENTRO DO GENERATE_STREAM - Mode: {current_mode} ===")
131
+
132
  try:
133
+ solicitacao_usuario = form_data.get('solicitacao', '')
134
+ print(f"Solicitação do usuário: {solicitacao_usuario[:100]}...")
 
135
 
136
+ if current_mode == 'test':
137
+ print("=== MODO TESTE ===")
138
+ mock_text = form_data.get('mock_text', 'Este é um **texto** de `simulação`.')
139
+ 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"
140
+ if processing_mode == 'atomic':
141
+ yield f"data: {json.dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_text}})}\n\n"
142
+ yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': mock_text}})}\n\n"
143
+ else:
144
+ print("=== MODO REAL ===")
145
+ if not solicitacao_usuario:
146
+ print("=== ERRO: SOLICITAÇÃO VAZIA ===")
147
+ yield f"data: {json.dumps({'error': 'Solicitação não fornecida.'})}\n\n"
148
+ return
 
 
 
 
 
 
 
 
 
 
149
 
150
+ print("=== PROCESSANDO ARQUIVOS E CONTEXTO ===")
151
+ yield f"data: {json.dumps({'progress': 0, 'message': 'Processando arquivos e extraindo contexto...'})}\n\n"
152
 
153
+ rag_context = get_relevant_context(file_paths, solicitacao_usuario)
154
+ print(f"RAG Context obtido: {len(rag_context)} caracteres")
 
155
 
156
+ output_parser = StrOutputParser()
 
 
 
 
157
 
158
+ if processing_mode == 'atomic':
159
+ print("=== MODO ATÔMICO ===")
160
+ # Lógica atômica permanece a mesma...
161
+ pass
162
 
163
+ else:
164
+ print("=== MODO HIERÁRQUICO INICIADO ===")
165
+
166
+ # GROK
167
+ print("=== PROCESSANDO GROK ===")
168
+ yield f"data: {json.dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})}\n\n"
169
+
170
+ prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
171
+ chain_grok = prompt_grok | grok_llm | output_parser
172
+
173
+ print("=== INVOCANDO GROK ===")
174
+ resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})
175
+ print(f"=== GROK RESPONDEU: {len(resposta_grok)} caracteres ===")
176
+
177
+ if not resposta_grok or not resposta_grok.strip():
178
+ print("=== ERRO: GROK SEM RESPOSTA ===")
179
+ yield f"data: {json.dumps({'error': 'Falha no serviço GROK: Sem resposta.'})}\n\n"
180
+ return
181
+
182
+ print("=== ENVIANDO RESPOSTA DO GROK ===")
183
+ yield f"data: {json.dumps({'progress': 33, 'message': 'Claude Sonnet está processando...', 'partial_result': {'id': 'grok-output', 'content': resposta_grok}})}\n\n"
184
+
185
+ # SONNET
186
+ print("=== PROCESSANDO SONNET ===")
187
+ prompt_sonnet = PromptTemplate(template=PROMPT_HIERARQUICO_SONNET, input_variables=["solicitacao_usuario", "texto_para_analise"])
188
+ claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
189
+ chain_sonnet = prompt_sonnet | claude_with_max_tokens | output_parser
190
+
191
+ print("=== INVOCANDO SONNET ===")
192
+ resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})
193
+ print(f"=== SONNET RESPONDEU: {len(resposta_sonnet)} caracteres ===")
194
+
195
+ if not resposta_sonnet or not resposta_sonnet.strip():
196
+ print("=== ERRO: SONNET SEM RESPOSTA ===")
197
+ yield f"data: {json.dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta.'})}\n\n"
198
  return
199
 
200
+ print("=== ENVIANDO RESPOSTA DO SONNET ===")
201
+ try:
202
+ sonnet_response = {
203
+ 'progress': 66,
204
+ 'message': 'Gemini está processando...',
205
+ 'partial_result': {
206
+ 'id': 'sonnet-output',
207
+ 'content': resposta_sonnet
208
+ }
209
+ }
210
+ yield f"data: {json.dumps(sonnet_response)}\n\n"
211
+ print("=== SONNET JSON ENVIADO COM SUCESSO ===")
212
+ except Exception as json_error:
213
+ print(f"=== ERRO NO JSON DO SONNET: {json_error} ===")
214
+ # Fallback
215
+ fallback_response = {
216
+ 'progress': 66,
217
+ 'message': 'Gemini está processando...',
218
+ 'partial_result': {
219
+ 'id': 'sonnet-output',
220
+ 'content': f"ERRO DE ENCODING: {str(json_error)}"
221
+ }
222
+ }
223
+ yield f"data: {json.dumps(fallback_response)}\n\n"
224
+
225
+ # GEMINI
226
+ print("=== PROCESSANDO GEMINI ===")
227
+ prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
228
+ chain_gemini = prompt_gemini | gemini_llm | output_parser
229
+
230
+ print("=== INVOCANDO GEMINI ===")
231
+ resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})
232
+ print(f"=== GEMINI RESPONDEU: {len(resposta_gemini)} caracteres ===")
233
+
234
+ if not resposta_gemini or not resposta_gemini.strip():
235
+ print("=== ERRO: GEMINI SEM RESPOSTA ===")
236
+ yield f"data: {json.dumps({'error': 'Falha no serviço Gemini: Sem resposta.'})}\n\n"
237
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ print("=== ENVIANDO RESPOSTA DO GEMINI ===")
240
+ 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"
241
+ print("=== PROCESSAMENTO HIERÁRQUICO CONCLUÍDO ===")
 
242
 
243
  except Exception as e:
244
+ print(f"=== ERRO GERAL NO GENERATE_STREAM: {e} ===")
245
+ print(f"=== TRACEBACK: {traceback.format_exc()} ===")
246
+ yield f"data: {json.dumps({'error': f'Erro inesperado: {str(e)}'})}\n\n"
247
 
248
+ return Response(generate_stream(mode, form_data, temp_file_paths), mimetype='text/event-stream')
249
+
250
+ except Exception as e:
251
+ print(f"=== ERRO NA ROTA PROCESS: {e} ===")
252
+ print(f"=== TRACEBACK: {traceback.format_exc()} ===")
253
+ return Response(f"data: {json.dumps({'error': f'Erro na rota process: {str(e)}'})}\n\n", mimetype='text/event-stream')
254
 
255
  @app.route('/merge', methods=['POST'])
256
  def merge():
257
  """Recebe os textos do modo Atômico e os consolida usando um LLM."""
258
+ print("=== ROTA MERGE ACESSADA ===")
259
  data = request.get_json()
260
 
261
  def generate_merge_stream():
262
  """Gera a resposta do merge em streaming."""
263
  try:
264
+ yield f"data: {json.dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})}\n\n"
 
265
 
266
  output_parser = StrOutputParser()
267
  prompt_merge = PromptTemplate(template=PROMPT_ATOMICO_MERGE, input_variables=["solicitacao_usuario", "texto_para_analise_grok", "texto_para_analise_sonnet", "texto_para_analise_gemini"])
 
269
  grok_with_max_tokens = grok_llm.bind(max_tokens=20000)
270
  chain_merge = prompt_merge | grok_with_max_tokens | output_parser
271
 
272
+ yield f"data: {json.dumps({'progress': 50, 'message': 'Enviando textos para o GROK para consolidação...'})}\n\n"
 
273
 
274
  resposta_merge = chain_merge.invoke({
275
  "solicitacao_usuario": data.get('solicitacao_usuario'),
 
279
  })
280
 
281
  if not resposta_merge or not resposta_merge.strip():
282
+ yield f"data: {json.dumps({'error': 'Falha no serviço de Merge (GROK): Sem resposta.'})}\n\n"
 
283
  return
284
 
285
  print(f"--- Resposta Bruta do Merge (GROK) ---\n{resposta_merge}\n------------------------------------")
286
  word_count = len(resposta_merge.split())
287
 
288
+ yield f"data: {json.dumps({'progress': 100, 'message': 'Merge concluído!', 'final_result': {'content': resposta_merge, 'word_count': word_count}, 'done': True})}\n\n"
 
289
 
290
  except Exception as e:
291
  print(f"Erro no processo de merge: {e}")
292
+ yield f"data: {json.dumps({'error': str(e)})}\n\n"
 
293
 
294
  return Response(generate_merge_stream(), mimetype='text/event-stream')
295
 
296
  if __name__ == '__main__':
297
+ print("=== INICIANDO SERVIDOR FLASK ===")
298
  app.run(debug=True)