Spaces:
Sleeping
Sleeping
Commit
·
57d2d03
1
Parent(s):
a53c02f
Evoluções Gerais
Browse files
app.py
CHANGED
@@ -41,6 +41,9 @@ app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024
|
|
41 |
# Instancia o conversor de Markdown
|
42 |
md = MarkdownIt()
|
43 |
|
|
|
|
|
|
|
44 |
def log_print(message):
|
45 |
"""Função para garantir que os logs apareçam no container"""
|
46 |
print(f"[DEBUG] {message}", flush=True)
|
@@ -127,6 +130,14 @@ def convert():
|
|
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():
|
@@ -142,6 +153,9 @@ def get_full_content():
|
|
142 |
@app.route('/process', methods=['POST'])
|
143 |
def process():
|
144 |
"""Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
|
|
|
|
|
|
|
145 |
log_print("=== ROTA PROCESS ACESSADA ===")
|
146 |
|
147 |
form_data = request.form
|
@@ -149,7 +163,12 @@ def process():
|
|
149 |
mode = form_data.get('mode', 'real')
|
150 |
processing_mode = form_data.get('processing_mode', 'hierarchical')
|
151 |
|
|
|
|
|
|
|
|
|
152 |
log_print(f"Mode: {mode}, Processing: {processing_mode}")
|
|
|
153 |
|
154 |
temp_file_paths = []
|
155 |
if mode == 'real':
|
@@ -162,6 +181,8 @@ def process():
|
|
162 |
|
163 |
def generate_stream(current_mode, form_data, file_paths):
|
164 |
"""Gera a resposta em streaming para o front-end."""
|
|
|
|
|
165 |
log_print(f"=== GENERATE_STREAM INICIADO - Mode: {current_mode} ===")
|
166 |
|
167 |
solicitacao_usuario = form_data.get('solicitacao', '')
|
@@ -200,13 +221,17 @@ def process():
|
|
200 |
|
201 |
def run_chain_with_timeout(chain, inputs, key, timeout=300):
|
202 |
def task():
|
|
|
|
|
203 |
return chain.invoke(inputs)
|
204 |
|
205 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
206 |
future = executor.submit(task)
|
207 |
try:
|
208 |
result = future.result(timeout=timeout)
|
209 |
-
if
|
|
|
|
|
210 |
results[key] = "Error:EmptyResponse"
|
211 |
else:
|
212 |
results[key] = result
|
@@ -218,11 +243,22 @@ def process():
|
|
218 |
claude_atomic_llm = claude_llm.bind(max_tokens=20000)
|
219 |
models = {'grok': grok_llm, 'sonnet': claude_atomic_llm, 'gemini': gemini_llm}
|
220 |
|
221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
227 |
thread = threading.Thread(target=run_chain_with_timeout, args=(chain, {"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context}, name))
|
228 |
threads.append(thread)
|
@@ -231,6 +267,12 @@ def process():
|
|
231 |
for thread in threads:
|
232 |
thread.join()
|
233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
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."
|
@@ -263,16 +305,41 @@ def process():
|
|
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=
|
271 |
chain_grok = prompt_grok | grok_llm | output_parser
|
272 |
resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})
|
273 |
|
274 |
log_print(f"=== GROK TERMINOU: {len(resposta_grok)} chars ===")
|
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.'})
|
@@ -283,14 +350,24 @@ def process():
|
|
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=
|
288 |
claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
|
289 |
chain_sonnet = prompt_sonnet | claude_with_max_tokens | output_parser
|
290 |
resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})
|
291 |
|
292 |
log_print(f"=== SONNET TERMINOU: {len(resposta_sonnet)} chars ===")
|
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.'})
|
@@ -301,13 +378,23 @@ def process():
|
|
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=
|
306 |
chain_gemini = prompt_gemini | gemini_llm | output_parser
|
307 |
resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})
|
308 |
|
309 |
log_print(f"=== GEMINI TERMINOU: {len(resposta_gemini)} chars ===")
|
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.'})
|
@@ -330,30 +417,43 @@ def process():
|
|
330 |
|
331 |
@app.route('/merge', methods=['POST'])
|
332 |
def merge():
|
333 |
-
"""Recebe os textos do modo Atômico e os consolida usando
|
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"])
|
350 |
|
351 |
-
|
352 |
-
|
|
|
353 |
|
354 |
-
json_data = safe_json_dumps({'progress': 50, 'message': 'Enviando textos para o
|
355 |
yield f"data: {json_data}\n\n"
|
356 |
-
log_print("=== INVOCANDO
|
|
|
|
|
|
|
|
|
|
|
357 |
|
358 |
resposta_merge = chain_merge.invoke({
|
359 |
"solicitacao_usuario": data.get('solicitacao_usuario'),
|
@@ -362,10 +462,15 @@ def merge():
|
|
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 (
|
369 |
yield f"data: {json_data}\n\n"
|
370 |
return
|
371 |
|
|
|
41 |
# Instancia o conversor de Markdown
|
42 |
md = MarkdownIt()
|
43 |
|
44 |
+
# Variável global para controle de interrupção
|
45 |
+
processing_cancelled = False
|
46 |
+
|
47 |
def log_print(message):
|
48 |
"""Função para garantir que os logs apareçam no container"""
|
49 |
print(f"[DEBUG] {message}", flush=True)
|
|
|
130 |
converted_html = render_markdown_cascata(text_to_convert)
|
131 |
return jsonify({'html': converted_html})
|
132 |
|
133 |
+
# NOVA ROTA: Para cancelar processamento
|
134 |
+
@app.route('/cancel', methods=['POST'])
|
135 |
+
def cancel():
|
136 |
+
global processing_cancelled
|
137 |
+
processing_cancelled = True
|
138 |
+
log_print("=== PROCESSAMENTO CANCELADO PELO USUÁRIO ===")
|
139 |
+
return jsonify({'status': 'cancelled'})
|
140 |
+
|
141 |
# NOVA ROTA: Para obter o conteúdo completo do merge
|
142 |
@app.route('/get-full-content', methods=['POST'])
|
143 |
def get_full_content():
|
|
|
153 |
@app.route('/process', methods=['POST'])
|
154 |
def process():
|
155 |
"""Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
|
156 |
+
global processing_cancelled
|
157 |
+
processing_cancelled = False # Reset do flag de cancelamento
|
158 |
+
|
159 |
log_print("=== ROTA PROCESS ACESSADA ===")
|
160 |
|
161 |
form_data = request.form
|
|
|
163 |
mode = form_data.get('mode', 'real')
|
164 |
processing_mode = form_data.get('processing_mode', 'hierarchical')
|
165 |
|
166 |
+
# NOVOS PARÂMETROS: Tamanho do texto
|
167 |
+
min_chars = int(form_data.get('min_chars', 24000))
|
168 |
+
max_chars = int(form_data.get('max_chars', 30000))
|
169 |
+
|
170 |
log_print(f"Mode: {mode}, Processing: {processing_mode}")
|
171 |
+
log_print(f"Tamanho solicitado: {min_chars} - {max_chars} caracteres")
|
172 |
|
173 |
temp_file_paths = []
|
174 |
if mode == 'real':
|
|
|
181 |
|
182 |
def generate_stream(current_mode, form_data, file_paths):
|
183 |
"""Gera a resposta em streaming para o front-end."""
|
184 |
+
global processing_cancelled
|
185 |
+
|
186 |
log_print(f"=== GENERATE_STREAM INICIADO - Mode: {current_mode} ===")
|
187 |
|
188 |
solicitacao_usuario = form_data.get('solicitacao', '')
|
|
|
221 |
|
222 |
def run_chain_with_timeout(chain, inputs, key, timeout=300):
|
223 |
def task():
|
224 |
+
if processing_cancelled:
|
225 |
+
return "CANCELLED"
|
226 |
return chain.invoke(inputs)
|
227 |
|
228 |
with concurrent.futures.ThreadPoolExecutor() as executor:
|
229 |
future = executor.submit(task)
|
230 |
try:
|
231 |
result = future.result(timeout=timeout)
|
232 |
+
if result == "CANCELLED":
|
233 |
+
results[key] = "CANCELLED"
|
234 |
+
elif not result or not result.strip():
|
235 |
results[key] = "Error:EmptyResponse"
|
236 |
else:
|
237 |
results[key] = result
|
|
|
243 |
claude_atomic_llm = claude_llm.bind(max_tokens=20000)
|
244 |
models = {'grok': grok_llm, 'sonnet': claude_atomic_llm, 'gemini': gemini_llm}
|
245 |
|
246 |
+
# Atualizar os prompts com os parâmetros de tamanho
|
247 |
+
updated_prompt_template = PROMPT_ATOMICO_INICIAL.replace(
|
248 |
+
"<minimum>24000</minimum>\n <maximum>30000</maximum>",
|
249 |
+
f"<minimum>{min_chars}</minimum>\n <maximum>{max_chars}</maximum>"
|
250 |
+
)
|
251 |
+
|
252 |
+
prompt = PromptTemplate(template=updated_prompt_template, input_variables=["solicitacao_usuario", "rag_context"])
|
253 |
json_data = safe_json_dumps({'progress': 15, 'message': 'Iniciando processamento paralelo...'})
|
254 |
yield f"data: {json_data}\n\n"
|
255 |
|
256 |
for name, llm in models.items():
|
257 |
+
if processing_cancelled:
|
258 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
259 |
+
yield f"data: {json_data}\n\n"
|
260 |
+
return
|
261 |
+
|
262 |
chain = prompt | llm | output_parser
|
263 |
thread = threading.Thread(target=run_chain_with_timeout, args=(chain, {"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context}, name))
|
264 |
threads.append(thread)
|
|
|
267 |
for thread in threads:
|
268 |
thread.join()
|
269 |
|
270 |
+
# Verificar se foi cancelado
|
271 |
+
if processing_cancelled or any(result == "CANCELLED" for result in results.values()):
|
272 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
273 |
+
yield f"data: {json_data}\n\n"
|
274 |
+
return
|
275 |
+
|
276 |
for key, result in results.items():
|
277 |
if result == "Error:EmptyResponse" or "Erro ao processar" in result:
|
278 |
error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
|
|
|
305 |
else:
|
306 |
log_print("=== MODO HIERÁRQUICO SELECIONADO ===")
|
307 |
# --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
|
308 |
+
|
309 |
+
# Atualizar prompts hierárquicos com parâmetros de tamanho
|
310 |
+
updated_grok_template = PROMPT_HIERARQUICO_GROK.replace(
|
311 |
+
"<minimum>24000</minimum>\n <maximum>30000</maximum>",
|
312 |
+
f"<minimum>{min_chars}</minimum>\n <maximum>{max_chars}</maximum>"
|
313 |
+
)
|
314 |
+
updated_sonnet_template = PROMPT_HIERARQUICO_SONNET.replace(
|
315 |
+
"**Valide se o texto atingiu a quantidade de caracteres mínimas de 24000 e máxima de 30000 caracteres**.",
|
316 |
+
f"**Valide se o texto atingiu a quantidade de caracteres mínimas de {min_chars} e máxima de {max_chars} caracteres**."
|
317 |
+
)
|
318 |
+
updated_gemini_template = PROMPT_HIERARQUICO_GEMINI.replace(
|
319 |
+
"**Valide se o texto atingiu a quantidade de caracteres mínimas de 24000 e máxima de 30000 caracteres**.",
|
320 |
+
f"**Valide se o texto atingiu a quantidade de caracteres mínimas de {min_chars} e máxima de {max_chars} caracteres**."
|
321 |
+
)
|
322 |
+
|
323 |
json_data = safe_json_dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})
|
324 |
yield f"data: {json_data}\n\n"
|
325 |
|
326 |
+
if processing_cancelled:
|
327 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
328 |
+
yield f"data: {json_data}\n\n"
|
329 |
+
return
|
330 |
+
|
331 |
log_print("=== PROCESSANDO GROK ===")
|
332 |
+
prompt_grok = PromptTemplate(template=updated_grok_template, input_variables=["solicitacao_usuario", "rag_context"])
|
333 |
chain_grok = prompt_grok | grok_llm | output_parser
|
334 |
resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})
|
335 |
|
336 |
log_print(f"=== GROK TERMINOU: {len(resposta_grok)} chars ===")
|
337 |
|
338 |
+
if processing_cancelled:
|
339 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
340 |
+
yield f"data: {json_data}\n\n"
|
341 |
+
return
|
342 |
+
|
343 |
if not resposta_grok or not resposta_grok.strip():
|
344 |
log_print("=== ERRO: GROK VAZIO ===")
|
345 |
json_data = safe_json_dumps({'error': 'Falha no serviço GROK: Sem resposta.'})
|
|
|
350 |
json_data = safe_json_dumps({'progress': 33, 'message': 'Claude Sonnet está processando...', 'partial_result': {'id': 'grok-output', 'content': resposta_grok}})
|
351 |
yield f"data: {json_data}\n\n"
|
352 |
|
353 |
+
if processing_cancelled:
|
354 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
355 |
+
yield f"data: {json_data}\n\n"
|
356 |
+
return
|
357 |
+
|
358 |
log_print("=== PROCESSANDO SONNET ===")
|
359 |
+
prompt_sonnet = PromptTemplate(template=updated_sonnet_template, input_variables=["solicitacao_usuario", "texto_para_analise"])
|
360 |
claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
|
361 |
chain_sonnet = prompt_sonnet | claude_with_max_tokens | output_parser
|
362 |
resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})
|
363 |
|
364 |
log_print(f"=== SONNET TERMINOU: {len(resposta_sonnet)} chars ===")
|
365 |
|
366 |
+
if processing_cancelled:
|
367 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
368 |
+
yield f"data: {json_data}\n\n"
|
369 |
+
return
|
370 |
+
|
371 |
if not resposta_sonnet or not resposta_sonnet.strip():
|
372 |
log_print("=== ERRO: SONNET VAZIO ===")
|
373 |
json_data = safe_json_dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta.'})
|
|
|
378 |
json_data = safe_json_dumps({'progress': 66, 'message': 'Gemini está processando...', 'partial_result': {'id': 'sonnet-output', 'content': resposta_sonnet}})
|
379 |
yield f"data: {json_data}\n\n"
|
380 |
|
381 |
+
if processing_cancelled:
|
382 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
383 |
+
yield f"data: {json_data}\n\n"
|
384 |
+
return
|
385 |
+
|
386 |
log_print("=== PROCESSANDO GEMINI ===")
|
387 |
+
prompt_gemini = PromptTemplate(template=updated_gemini_template, input_variables=["solicitacao_usuario", "texto_para_analise"])
|
388 |
chain_gemini = prompt_gemini | gemini_llm | output_parser
|
389 |
resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})
|
390 |
|
391 |
log_print(f"=== GEMINI TERMINOU: {len(resposta_gemini)} chars ===")
|
392 |
|
393 |
+
if processing_cancelled:
|
394 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
395 |
+
yield f"data: {json_data}\n\n"
|
396 |
+
return
|
397 |
+
|
398 |
if not resposta_gemini or not resposta_gemini.strip():
|
399 |
log_print("=== ERRO: GEMINI VAZIO ===")
|
400 |
json_data = safe_json_dumps({'error': 'Falha no serviço Gemini: Sem resposta.'})
|
|
|
417 |
|
418 |
@app.route('/merge', methods=['POST'])
|
419 |
def merge():
|
420 |
+
"""Recebe os textos do modo Atômico e os consolida usando Claude Sonnet."""
|
421 |
+
global merge_full_content, processing_cancelled
|
422 |
+
processing_cancelled = False # Reset do flag
|
423 |
|
424 |
data = request.get_json()
|
425 |
log_print("=== ROTA MERGE ACESSADA ===")
|
426 |
+
log_print("=== USANDO CLAUDE SONNET PARA MERGE ===")
|
427 |
|
428 |
def generate_merge_stream():
|
429 |
"""Gera a resposta do merge em streaming."""
|
430 |
+
global merge_full_content, processing_cancelled
|
431 |
|
432 |
try:
|
433 |
log_print("=== INICIANDO MERGE STREAM ===")
|
434 |
json_data = safe_json_dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})
|
435 |
yield f"data: {json_data}\n\n"
|
436 |
|
437 |
+
if processing_cancelled:
|
438 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
439 |
+
yield f"data: {json_data}\n\n"
|
440 |
+
return
|
441 |
+
|
442 |
output_parser = StrOutputParser()
|
443 |
prompt_merge = PromptTemplate(template=PROMPT_ATOMICO_MERGE, input_variables=["solicitacao_usuario", "texto_para_analise_grok", "texto_para_analise_sonnet", "texto_para_analise_gemini"])
|
444 |
|
445 |
+
# MUDANÇA: Usar Claude Sonnet para o merge
|
446 |
+
claude_with_max_tokens = claude_llm.bind(max_tokens=100000)
|
447 |
+
chain_merge = prompt_merge | claude_with_max_tokens | output_parser
|
448 |
|
449 |
+
json_data = safe_json_dumps({'progress': 50, 'message': 'Enviando textos para o Claude Sonnet para consolidação...'})
|
450 |
yield f"data: {json_data}\n\n"
|
451 |
+
log_print("=== INVOCANDO CLAUDE SONNET PARA MERGE ===")
|
452 |
+
|
453 |
+
if processing_cancelled:
|
454 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
455 |
+
yield f"data: {json_data}\n\n"
|
456 |
+
return
|
457 |
|
458 |
resposta_merge = chain_merge.invoke({
|
459 |
"solicitacao_usuario": data.get('solicitacao_usuario'),
|
|
|
462 |
"texto_para_analise_gemini": data.get('gemini_text')
|
463 |
})
|
464 |
|
465 |
+
log_print(f"=== MERGE CLAUDE SONNET CONCLUÍDO: {len(resposta_merge)} chars ===")
|
466 |
+
|
467 |
+
if processing_cancelled:
|
468 |
+
json_data = safe_json_dumps({'error': 'Processamento cancelado pelo usuário.'})
|
469 |
+
yield f"data: {json_data}\n\n"
|
470 |
+
return
|
471 |
|
472 |
if not resposta_merge or not resposta_merge.strip():
|
473 |
+
json_data = safe_json_dumps({'error': 'Falha no serviço de Merge (Claude Sonnet): Sem resposta.'})
|
474 |
yield f"data: {json_data}\n\n"
|
475 |
return
|
476 |
|