caarleexx commited on
Commit
4298dfd
·
verified ·
1 Parent(s): bc7c243

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -100
app.py CHANGED
@@ -141,134 +141,90 @@ def sanitizar_texto(texto: str) -> str:
141
  # CORREÇÃO DA FUNÇÃO DE CHAMADA DA API (google-genai v1.0+)
142
  # ===========================================================================
143
 
144
- def chamar_gemini_json(model_name: str, prompt: str, temperatura: float = 0.4, max_tokens: int = 8192) -> Dict:
145
- prompt_completo = f"{prompt}\n\n---\n\n**INSTRUÇÃO OBRIGATÓRIA: Sua resposta DEVE ser um único e válido objeto JSON.**"
146
- print(f"\n{'='*25} 💬 API INPUT PARA [{model_name}] {'='*25}\n{prompt_completo}\n{'='*78}\n")
 
 
 
 
 
 
 
 
147
 
148
  try:
149
- # 1. Monta a estrutura de `contents`
150
- contents = [
151
- types.Content(
152
- role="user",
153
- parts=[types.Part.from_text(text=prompt_completo)],
154
- ),
155
- ]
156
 
157
- # 2. Define as ferramentas (Google Search)
158
  tools = [
159
  types.Tool(google_search=types.GoogleSearch()),
160
  ]
161
 
162
- # 3. Monta a configuração
163
- # NOTA: Verifique se o modelo suporta 'thinking_config'. Se der erro, remova essa linha.
164
- # Models como gemini-2.0-flash-thinking suportam. O 2.5-pro padrão pode não suportar.
165
- thinking_config = None
166
- if "thinking" in model_name: # Lógica opcional para ativar apenas se for modelo thinking
167
- thinking_config = types.ThinkingConfig(thinking_budget=8192)
168
-
169
  generate_content_config = types.GenerateContentConfig(
170
- temperature=temperatura,
171
  max_output_tokens=max_tokens,
172
- thinking_config=thinking_config, # Descomente se tiver certeza que o modelo suporta
173
- tools=tools,
174
- response_mime_type="application/json" # Força JSON nativo se o modelo suportar
175
  )
176
 
177
- # 4. Chamada de API Corrigida
178
- # A nova SDK usa client.models.generate_content_stream
179
- # O parâmetro de configuração chama-se 'config', não 'generation_config'
 
 
 
 
 
 
180
  stream = CLIENT.models.generate_content_stream(
181
  model=model_name,
182
  contents=contents,
183
  config=generate_content_config,
184
  )
185
 
186
- # Agrega a resposta do stream
187
  resposta_bruta = ""
188
  for chunk in stream:
 
189
  if chunk.text:
190
  resposta_bruta += chunk.text
 
 
 
191
 
192
- print(f"\n{'='*25} 📥 API RAW OUTPUT DE [{model_name}] {'='*25}\n{resposta_bruta}\n{'='*78}\n")
 
 
 
193
 
194
- resposta_sanitizada = sanitizar_texto(resposta_bruta)
195
-
196
- if not resposta_sanitizada:
197
- logger.log("A API retornou uma resposta vazia. Causa provável: Filtros de segurança.", "WARN")
198
- return {"erro": "API_EMPTY_RESPONSE", "causa_provavel": "Filtro de segurança do modelo."}
199
-
200
- # Tenta extrair JSON (caso o response_mime_type não seja estritamente respeitado ou venha markdown)
201
- match = re.search(r'\{.*\}', resposta_sanitizada, re.DOTALL)
202
- if not match:
203
- # Tenta parsear direto caso a string inteira seja o JSON
204
- try:
205
- return json.loads(resposta_sanitizada)
206
- except:
207
- return {"erro": "JSON_NOT_FOUND", "detalhes": "Nenhum objeto JSON encontrado na resposta da API.", "raw": resposta_sanitizada}
208
-
209
- return json.loads(match.group(0))
210
 
211
  except Exception as e:
212
- logger.log(f"Falha na chamada da API ou no parse do JSON: {e}", "ERROR")
213
- return {"erro": "API_CALL_OR_PARSE_FAILED", "detalhes": str(e)}
214
-
215
-
216
-
217
- def chamar_gemini_json1(model_name: str, prompt: str, temperatura: float = 0.4, max_tokens: int = 92048) -> Dict:
218
- prompt_completo = f"{prompt}\n\n---\n\n**INSTRUÇÃO OBRIGATÓRIA: Sua resposta DEVE ser um único e válido objeto JSON.**"
219
- print(f"\n{'='*25} 💬 API INPUT PARA [{model_name}] {'='*25}\n{prompt_completo}\n{'='*78}\n")
220
 
221
- try:
222
- # 1. Monta a estrutura de `contents`
223
- contents = [
224
- types.Content(
225
- role="user",
226
- parts=[types.Part.from_text(text=prompt_completo)],
227
- ),
228
- ]
229
-
230
- # 2. Define as ferramentas a serem usadas
231
- tools = [
232
- types.Tool(google_search=types.GoogleSearch()),
233
- ]
234
-
235
- # 3. Monta a configuração da geração de conteúdo
236
- # A sintaxe do 'thinking_config' foi corrigida.
237
- generate_content_config = types.GenerateContentConfig(
238
- temperature=temperatura,
239
- max_output_tokens=max_tokens,
240
- thinking_config=types.ThinkingConfig(thinking_budget=8192), # Habilitar se o modelo suportar
241
- tools=tools,
242
- )
243
-
244
- # 4. Chama o método de stream e agrega os chunks
245
- stream = CLIENT.generate_content(
246
- model=f"{model_name}", # O nome do modelo agora precisa do prefixo 'models/'
247
- contents=contents,
248
- generation_config=generate_content_config,
249
- stream=True
250
- )
251
 
252
- # Agrega a resposta do stream em uma única string
253
- resposta_bruta = "".join(chunk.text for chunk in stream)
254
-
255
- print(f"\n{'='*25} 📥 API RAW OUTPUT DE [{model_name}] {'='*25}\n{resposta_bruta}\n{'='*78}\n")
256
-
257
- resposta_sanitizada = sanitizar_texto(resposta_bruta)
258
 
259
- if not resposta_sanitizada:
260
- logger.log("A API retornou uma resposta vazia. Causa provável: Filtros de segurança.", "WARN")
261
- return {"erro": "API_EMPTY_RESPONSE", "causa_provavel": "Filtro de segurança do modelo."}
262
-
263
- match = re.search(r'\{.*\}', resposta_sanitizada, re.DOTALL)
264
- if not match:
265
- return {"erro": "JSON_NOT_FOUND", "detalhes": "Nenhum objeto JSON encontrado na resposta da API."}
266
-
267
- return json.loads(match.group(0))
268
-
269
- except Exception as e:
270
- logger.log(f"Falha na chamada da API ou no parse do JSON: {e}", "ERROR")
271
- return {"erro": "API_CALL_OR_PARSE_FAILED", "detalhes": str(e)}
272
 
273
  def criar_dna() -> Dict:
274
  return { "historico_chat": [], "meta": {"total_turnos": 0}, "pipeline_state": { "status": "completed", "paused_at_step": None, "saved_data": {} } }
 
141
  # CORREÇÃO DA FUNÇÃO DE CHAMADA DA API (google-genai v1.0+)
142
  # ===========================================================================
143
 
144
+ # ============================================================================
145
+ # FUNÇÃO CORRIGIDA: REMOVIDO response_mime_type PARA COMPATIBILIDADE COM TOOLS
146
+ # ============================================================================
147
+
148
+ def chamar_gemini_json(model_name: str, prompt: str, temperatura: float = 0.7, max_tokens: int = 12000) -> Dict:
149
+ # 1. Ajuste de Prompt
150
+ # Modelos "Thinking" funcionam melhor quando explicamos o que queremos,
151
+ # mas mantemos a proibição de Markdown no output final para facilitar o regex.
152
+ prompt_completo = f"{prompt}\n\n---\n\n**INSTRUÇÃO CRÍTICA: Ao final do seu raciocínio, sua resposta FINAL deve ser estritamente um único objeto JSON válido. Não use blocos de código markdown"
153
+
154
+ print(f"\n{'='*25} 💬 API INPUT PARA [{model_name}] {'='*25}\n{prompt_completo[:300]}...\n{'='*78}\n")
155
 
156
  try:
157
+ # 2. Configurar o Thinking
158
+ # include_thoughts=True fará com que o pensamento apareça no output (ajuda a debugar),
159
+ # mas o seu regex filtrará para pegar só o JSON depois.
160
+ thinking_config = types.ThinkingConfig(
161
+ include_thoughts=True
162
+ )
 
163
 
164
+ # 3. Ferramentas (Google Search)
165
  tools = [
166
  types.Tool(google_search=types.GoogleSearch()),
167
  ]
168
 
169
+ # 4. Configuração da Geração
170
+ # ATENÇÃO: Modelos "Thinking" exigem temperatura maior ou igual a 0.7 para serem criativos no raciocínio.
171
+ # removemos 'response_mime_type' pois conflita com Tools e Thinking juntos.
 
 
 
 
172
  generate_content_config = types.GenerateContentConfig(
173
+ temperature=0.7, # Thinking requer > 0
174
  max_output_tokens=max_tokens,
175
+ thinking_config=thinking_config,
176
+ tools=tools
 
177
  )
178
 
179
+ # 5. Configurar Conteúdo
180
+ contents = [
181
+ types.Content(
182
+ role="user",
183
+ parts=[types.Part.from_text(text=prompt_completo)],
184
+ ),
185
+ ]
186
+
187
+ # 6. Chamada API
188
  stream = CLIENT.models.generate_content_stream(
189
  model=model_name,
190
  contents=contents,
191
  config=generate_content_config,
192
  )
193
 
194
+ # 7. Agregar Resposta
195
  resposta_bruta = ""
196
  for chunk in stream:
197
+ # Captura partes de texto e partes de pensamento (se retornadas como texto no stream)
198
  if chunk.text:
199
  resposta_bruta += chunk.text
200
+ # Modelos Thinking as vezes retornam "executable_code", garantimos pegar apenas texto
201
+
202
+ print(f"\n{'='*25} 📥 API RAW OUTPUT (Thinking + JSON) {'='*25}\n{resposta_bruta[:500]}... [conteudo longo] ...\n{'='*78}\n")
203
 
204
+ # 8. Sanitização e Extração
205
+ # Como o modelo vai "pensar" primeiro, o texto vai começar com o raciocínio e terminar com o JSON.
206
+ # O Regex aqui é CRUCIAL.
207
+ match = re.search(r'(\{.*\})', resposta_bruta, re.DOTALL)
208
 
209
+ if match:
210
+ json_str = match.group(0)
211
+ return json.loads(json_str)
212
+ else:
213
+ # Fallback: Tenta limpar chars estranhos caso o regex falhe
214
+ logger.log("Regex primário falhou, tentando limpeza forçada...", "WARN")
215
+ resposta_sanitizada = sanitizar_texto(resposta_bruta)
216
+ match_retry = re.search(r'(\{.*\})', resposta_sanitizada, re.DOTALL)
217
+ if match_retry:
218
+ return json.loads(match_retry.group(0))
219
+
220
+ return {"erro": "JSON_NOT_FOUND", "raw": resposta_bruta[:500], "detalhes": "O modelo pensou mas não entregou o JSON no formato esperado."}
 
 
 
 
221
 
222
  except Exception as e:
223
+ logger.log(f"Falha na chamada da API com Thinking: {e}", "ERROR")
224
+ return {"erro": "API_CALL_FAILED", "detalhes": str(e)}
 
 
 
 
 
 
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
 
 
 
 
 
 
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
  def criar_dna() -> Dict:
230
  return { "historico_chat": [], "meta": {"total_turnos": 0}, "pipeline_state": { "status": "completed", "paused_at_step": None, "saved_data": {} } }