Spaces:
Sleeping
Sleeping
Commit
·
a81bbe2
1
Parent(s):
0c6d84c
Correções e evoluções
Browse files
app.py
CHANGED
@@ -24,14 +24,20 @@ from rag_processor import get_relevant_context
|
|
24 |
|
25 |
app = Flask(__name__)
|
26 |
|
|
|
|
|
|
|
|
|
27 |
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024
|
28 |
|
29 |
@app.route('/')
|
30 |
def index():
|
|
|
31 |
return render_template('index.html')
|
32 |
|
33 |
@app.route('/process', methods=['POST'])
|
34 |
def process():
|
|
|
35 |
form_data = request.form
|
36 |
files = request.files.getlist('files')
|
37 |
mode = form_data.get('mode', 'real')
|
@@ -41,15 +47,18 @@ def process():
|
|
41 |
if mode == 'real':
|
42 |
for file in files:
|
43 |
if file and file.filename:
|
44 |
-
|
|
|
45 |
file_path = os.path.join('uploads', unique_filename)
|
46 |
file.save(file_path)
|
47 |
temp_file_paths.append(file_path)
|
48 |
|
49 |
def generate_stream(current_mode, form_data, file_paths):
|
|
|
50 |
solicitacao_usuario = form_data.get('solicitacao', '')
|
51 |
|
52 |
if current_mode == 'test':
|
|
|
53 |
mock_text = form_data.get('mock_text', 'Este é um texto de simulação.')
|
54 |
mock_html = markdown2.markdown(mock_text, extras=["fenced-code-blocks", "tables"])
|
55 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Simulação concluída!', 'partial_result': {'id': 'grok-output', 'content': mock_html}, 'done': True, 'mode': 'atomic' if processing_mode == 'atomic' else 'hierarchical'})}\n\n"
|
@@ -57,6 +66,7 @@ def process():
|
|
57 |
yield f"data: {json.dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_html}})}\n\n"
|
58 |
yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': mock_html}})}\n\n"
|
59 |
else:
|
|
|
60 |
if not solicitacao_usuario:
|
61 |
yield f"data: {json.dumps({'error': 'Solicitação não fornecida.'})}\n\n"
|
62 |
return
|
@@ -66,10 +76,12 @@ def process():
|
|
66 |
rag_context = get_relevant_context(file_paths, solicitacao_usuario)
|
67 |
|
68 |
if processing_mode == 'atomic':
|
|
|
69 |
results = {}
|
70 |
threads = []
|
71 |
|
72 |
def run_chain_with_timeout(chain, inputs, key, timeout=300):
|
|
|
73 |
def task():
|
74 |
return chain.invoke(inputs)['text']
|
75 |
|
@@ -77,6 +89,7 @@ def process():
|
|
77 |
future = executor.submit(task)
|
78 |
try:
|
79 |
result = future.result(timeout=timeout)
|
|
|
80 |
if not result or not result.strip():
|
81 |
results[key] = "Error:EmptyResponse"
|
82 |
else:
|
@@ -99,11 +112,12 @@ def process():
|
|
99 |
for thread in threads:
|
100 |
thread.join()
|
101 |
|
|
|
102 |
for key, result in results.items():
|
103 |
if result == "Error:EmptyResponse" or "Erro ao processar" in result:
|
104 |
error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
|
105 |
yield f"data: {json.dumps({'error': error_msg})}\n\n"
|
106 |
-
return
|
107 |
|
108 |
yield f"data: {json.dumps({'progress': 80, 'message': 'Todos os modelos responderam. Formatando saída...'})}\n\n"
|
109 |
|
@@ -115,13 +129,19 @@ def process():
|
|
115 |
yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': gemini_html}})}\n\n"
|
116 |
|
117 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento Atômico concluído!', 'done': True, 'mode': 'atomic'})}\n\n"
|
|
|
118 |
else:
|
119 |
# --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
|
120 |
-
yield f"data: {json.dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação
|
121 |
prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
|
122 |
chain_grok = LLMChain(llm=grok_llm, prompt=prompt_grok)
|
123 |
resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})['text']
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
125 |
grok_html = markdown2.markdown(resposta_grok, extras=["fenced-code-blocks", "tables"])
|
126 |
yield f"data: {json.dumps({'progress': 33, 'message': 'Agora, o Claude Sonnet está aprofundando o texto...', 'partial_result': {'id': 'grok-output', 'content': grok_html}})}\n\n"
|
127 |
|
@@ -129,14 +149,24 @@ def process():
|
|
129 |
claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
|
130 |
chain_sonnet = LLMChain(llm=claude_with_max_tokens, prompt=prompt_sonnet)
|
131 |
resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})['text']
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
133 |
sonnet_html = markdown2.markdown(resposta_sonnet, extras=["fenced-code-blocks", "tables"])
|
134 |
-
yield f"data: {json.dumps({'progress': 66, 'message': '
|
135 |
|
136 |
prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
|
137 |
chain_gemini = LLMChain(llm=gemini_llm, prompt=prompt_gemini)
|
138 |
resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})['text']
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
140 |
gemini_html = markdown2.markdown(resposta_gemini, extras=["fenced-code-blocks", "tables"])
|
141 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento concluído!', 'partial_result': {'id': 'gemini-output', 'content': gemini_html}, 'done': True, 'mode': 'hierarchical'})}\n\n"
|
142 |
|
@@ -149,9 +179,11 @@ def process():
|
|
149 |
# --- ROTA PARA O MERGE ---
|
150 |
@app.route('/merge', methods=['POST'])
|
151 |
def merge():
|
|
|
152 |
data = request.get_json()
|
153 |
|
154 |
def generate_merge_stream():
|
|
|
155 |
try:
|
156 |
yield f"data: {json.dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})}\n\n"
|
157 |
|
@@ -168,8 +200,10 @@ def merge():
|
|
168 |
"texto_para_analise_gemini": data.get('gemini_text')
|
169 |
})['text']
|
170 |
|
|
|
171 |
if not resposta_merge or not resposta_merge.strip():
|
172 |
-
|
|
|
173 |
|
174 |
word_count = len(resposta_merge.split())
|
175 |
merge_html = markdown2.markdown(resposta_merge, extras=["fenced-code-blocks", "tables"])
|
@@ -183,4 +217,5 @@ def merge():
|
|
183 |
return Response(generate_merge_stream(), mimetype='text/event-stream')
|
184 |
|
185 |
if __name__ == '__main__':
|
|
|
186 |
app.run(debug=True)
|
|
|
24 |
|
25 |
app = Flask(__name__)
|
26 |
|
27 |
+
# Garante que o diretório de uploads exista
|
28 |
+
if not os.path.exists('uploads'):
|
29 |
+
os.makedirs('uploads')
|
30 |
+
|
31 |
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024
|
32 |
|
33 |
@app.route('/')
|
34 |
def index():
|
35 |
+
"""Renderiza a página inicial da aplicação."""
|
36 |
return render_template('index.html')
|
37 |
|
38 |
@app.route('/process', methods=['POST'])
|
39 |
def process():
|
40 |
+
"""Processa a solicitação do usuário nos modos Hierárquico ou Atômico."""
|
41 |
form_data = request.form
|
42 |
files = request.files.getlist('files')
|
43 |
mode = form_data.get('mode', 'real')
|
|
|
47 |
if mode == 'real':
|
48 |
for file in files:
|
49 |
if file and file.filename:
|
50 |
+
# Cria um nome de arquivo único para evitar conflitos
|
51 |
+
unique_filename = str(uuid.uuid4()) + "_" + os.path.basename(file.filename)
|
52 |
file_path = os.path.join('uploads', unique_filename)
|
53 |
file.save(file_path)
|
54 |
temp_file_paths.append(file_path)
|
55 |
|
56 |
def generate_stream(current_mode, form_data, file_paths):
|
57 |
+
"""Gera a resposta em streaming para o front-end."""
|
58 |
solicitacao_usuario = form_data.get('solicitacao', '')
|
59 |
|
60 |
if current_mode == 'test':
|
61 |
+
# Lógica para o modo de teste/simulação
|
62 |
mock_text = form_data.get('mock_text', 'Este é um texto de simulação.')
|
63 |
mock_html = markdown2.markdown(mock_text, extras=["fenced-code-blocks", "tables"])
|
64 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Simulação concluída!', 'partial_result': {'id': 'grok-output', 'content': mock_html}, 'done': True, 'mode': 'atomic' if processing_mode == 'atomic' else 'hierarchical'})}\n\n"
|
|
|
66 |
yield f"data: {json.dumps({'partial_result': {'id': 'sonnet-output', 'content': mock_html}})}\n\n"
|
67 |
yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': mock_html}})}\n\n"
|
68 |
else:
|
69 |
+
# Lógica para o modo real
|
70 |
if not solicitacao_usuario:
|
71 |
yield f"data: {json.dumps({'error': 'Solicitação não fornecida.'})}\n\n"
|
72 |
return
|
|
|
76 |
rag_context = get_relevant_context(file_paths, solicitacao_usuario)
|
77 |
|
78 |
if processing_mode == 'atomic':
|
79 |
+
# --- LÓGICA ATÔMICA (PARALELA) ---
|
80 |
results = {}
|
81 |
threads = []
|
82 |
|
83 |
def run_chain_with_timeout(chain, inputs, key, timeout=300):
|
84 |
+
"""Executa uma chain com timeout e trata respostas vazias."""
|
85 |
def task():
|
86 |
return chain.invoke(inputs)['text']
|
87 |
|
|
|
89 |
future = executor.submit(task)
|
90 |
try:
|
91 |
result = future.result(timeout=timeout)
|
92 |
+
# Validação de resposta vazia
|
93 |
if not result or not result.strip():
|
94 |
results[key] = "Error:EmptyResponse"
|
95 |
else:
|
|
|
112 |
for thread in threads:
|
113 |
thread.join()
|
114 |
|
115 |
+
# Verifica se alguma thread falhou ou retornou vazio
|
116 |
for key, result in results.items():
|
117 |
if result == "Error:EmptyResponse" or "Erro ao processar" in result:
|
118 |
error_msg = result if "Erro ao processar" in result else f"Falha no serviço {key.upper()}: Sem resposta."
|
119 |
yield f"data: {json.dumps({'error': error_msg})}\n\n"
|
120 |
+
return # Interrompe todo o processo
|
121 |
|
122 |
yield f"data: {json.dumps({'progress': 80, 'message': 'Todos os modelos responderam. Formatando saída...'})}\n\n"
|
123 |
|
|
|
129 |
yield f"data: {json.dumps({'partial_result': {'id': 'gemini-output', 'content': gemini_html}})}\n\n"
|
130 |
|
131 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento Atômico concluído!', 'done': True, 'mode': 'atomic'})}\n\n"
|
132 |
+
|
133 |
else:
|
134 |
# --- LÓGICA HIERÁRQUICA (SEQUENCIAL) ---
|
135 |
+
yield f"data: {json.dumps({'progress': 15, 'message': 'O GROK está processando sua solicitação...'})}\n\n"
|
136 |
prompt_grok = PromptTemplate(template=PROMPT_HIERARQUICO_GROK, input_variables=["solicitacao_usuario", "rag_context"])
|
137 |
chain_grok = LLMChain(llm=grok_llm, prompt=prompt_grok)
|
138 |
resposta_grok = chain_grok.invoke({"solicitacao_usuario": solicitacao_usuario, "rag_context": rag_context})['text']
|
139 |
+
|
140 |
+
# ✅ VALIDAÇÃO APRIMORADA
|
141 |
+
if not resposta_grok or not resposta_grok.strip():
|
142 |
+
yield f"data: {json.dumps({'error': 'Falha no serviço GROK: Sem resposta. O processo foi interrompido.'})}\n\n"
|
143 |
+
return
|
144 |
+
|
145 |
grok_html = markdown2.markdown(resposta_grok, extras=["fenced-code-blocks", "tables"])
|
146 |
yield f"data: {json.dumps({'progress': 33, 'message': 'Agora, o Claude Sonnet está aprofundando o texto...', 'partial_result': {'id': 'grok-output', 'content': grok_html}})}\n\n"
|
147 |
|
|
|
149 |
claude_with_max_tokens = claude_llm.bind(max_tokens=20000)
|
150 |
chain_sonnet = LLMChain(llm=claude_with_max_tokens, prompt=prompt_sonnet)
|
151 |
resposta_sonnet = chain_sonnet.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_grok})['text']
|
152 |
+
|
153 |
+
# ✅ VALIDAÇÃO APRIMORADA
|
154 |
+
if not resposta_sonnet or not resposta_sonnet.strip():
|
155 |
+
yield f"data: {json.dumps({'error': 'Falha no serviço Claude Sonnet: Sem resposta. O processo foi interrompido.'})}\n\n"
|
156 |
+
return
|
157 |
+
|
158 |
sonnet_html = markdown2.markdown(resposta_sonnet, extras=["fenced-code-blocks", "tables"])
|
159 |
+
yield f"data: {json.dumps({'progress': 66, 'message': 'Revisão final com o Gemini...'})}\n\n"
|
160 |
|
161 |
prompt_gemini = PromptTemplate(template=PROMPT_HIERARQUICO_GEMINI, input_variables=["solicitacao_usuario", "texto_para_analise"])
|
162 |
chain_gemini = LLMChain(llm=gemini_llm, prompt=prompt_gemini)
|
163 |
resposta_gemini = chain_gemini.invoke({"solicitacao_usuario": solicitacao_usuario, "texto_para_analise": resposta_sonnet})['text']
|
164 |
+
|
165 |
+
# ✅ VALIDAÇÃO APRIMORADA
|
166 |
+
if not resposta_gemini or not resposta_gemini.strip():
|
167 |
+
yield f"data: {json.dumps({'error': 'Falha no serviço Gemini: Sem resposta. O processo foi interrompido.'})}\n\n"
|
168 |
+
return
|
169 |
+
|
170 |
gemini_html = markdown2.markdown(resposta_gemini, extras=["fenced-code-blocks", "tables"])
|
171 |
yield f"data: {json.dumps({'progress': 100, 'message': 'Processamento concluído!', 'partial_result': {'id': 'gemini-output', 'content': gemini_html}, 'done': True, 'mode': 'hierarchical'})}\n\n"
|
172 |
|
|
|
179 |
# --- ROTA PARA O MERGE ---
|
180 |
@app.route('/merge', methods=['POST'])
|
181 |
def merge():
|
182 |
+
"""Recebe os textos do modo Atômico e os consolida usando um LLM."""
|
183 |
data = request.get_json()
|
184 |
|
185 |
def generate_merge_stream():
|
186 |
+
"""Gera a resposta do merge em streaming."""
|
187 |
try:
|
188 |
yield f"data: {json.dumps({'progress': 0, 'message': 'Iniciando o processo de merge...'})}\n\n"
|
189 |
|
|
|
200 |
"texto_para_analise_gemini": data.get('gemini_text')
|
201 |
})['text']
|
202 |
|
203 |
+
# ✅ VALIDAÇÃO APRIMORADA
|
204 |
if not resposta_merge or not resposta_merge.strip():
|
205 |
+
yield f"data: {json.dumps({'error': 'Falha no serviço de Merge (GROK): Sem resposta.'})}\n\n"
|
206 |
+
return
|
207 |
|
208 |
word_count = len(resposta_merge.split())
|
209 |
merge_html = markdown2.markdown(resposta_merge, extras=["fenced-code-blocks", "tables"])
|
|
|
217 |
return Response(generate_merge_stream(), mimetype='text/event-stream')
|
218 |
|
219 |
if __name__ == '__main__':
|
220 |
+
# Executa a aplicação Flask em modo de depuração
|
221 |
app.run(debug=True)
|