[FIXED] Corregir error de conexión con la API de Perplexity en rate_update.py
- Mejorar la función query_perplexity con mejor manejo de errores y debugging - Actualizar el modelo por defecto de 'o1' a 'sonar' que es compatible con la API - Expandir get_perplexity_api_key para buscar la clave en múltiples ubicaciones - Implementar sistema de control de errores consecutivos para detener el proceso - Agregar validación del formato de la API key - Mejorar registro de depuración para identificar problemas de conexión - Incluir pausa entre solicitudes tras errores para evitar limitaciones de la API 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
233fda5661
commit
1617385d3d
1 changed files with 110 additions and 16 deletions
|
@ -18,6 +18,7 @@ import requests
|
|||
import sys
|
||||
import subprocess
|
||||
import importlib.util
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# Configuración de logging
|
||||
|
@ -91,11 +92,47 @@ def get_ai_model():
|
|||
return "o1"
|
||||
|
||||
def get_perplexity_api_key():
|
||||
"""Obtener la clave API de Perplexity desde una variable de entorno."""
|
||||
"""Obtener la clave API de Perplexity desde una variable de entorno o archivo."""
|
||||
# Intentar obtener la clave de la variable de entorno
|
||||
api_key = os.environ.get('PERPLEXITY_API_KEY')
|
||||
|
||||
# Si no está en la variable de entorno, intentar cargarla desde un archivo
|
||||
if not api_key:
|
||||
logger.error("No se encontró la clave API de Perplexity. Establezca la variable de entorno PERPLEXITY_API_KEY.")
|
||||
# Rutas posibles para el archivo de API key
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/.perplexity/api_key'),
|
||||
os.path.expanduser('~/.config/perplexity/api_key'),
|
||||
os.path.join(BASE_DIR, 'bin', 'config', 'perplexity_api_key')
|
||||
]
|
||||
|
||||
for path in possible_paths:
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
logger.info(f"Intentando cargar API key desde: {path}")
|
||||
with open(path, 'r') as f:
|
||||
api_key = f.read().strip()
|
||||
if api_key:
|
||||
logger.info("API key de Perplexity cargada desde archivo.")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning(f"Error al leer el archivo de API key {path}: {e}")
|
||||
continue
|
||||
|
||||
# Si seguimos sin tener API key, mostrar error y salir
|
||||
if not api_key:
|
||||
logger.error("No se encontró la clave API de Perplexity.")
|
||||
logger.error("Opciones para configurarla:")
|
||||
logger.error("1. Establecer la variable de entorno PERPLEXITY_API_KEY")
|
||||
logger.error("2. Crear un archivo ~/.perplexity/api_key con la clave")
|
||||
logger.error("3. Crear un archivo ~/.config/perplexity/api_key con la clave")
|
||||
logger.error("4. Crear un archivo bin/config/perplexity_api_key en el directorio del proyecto")
|
||||
sys.exit(1)
|
||||
|
||||
# Validar formato básico de la API key
|
||||
if not api_key.startswith('pplx-'):
|
||||
logger.warning("El formato de la API key de Perplexity parece incorrecto.")
|
||||
logger.warning("Las API keys de Perplexity suelen comenzar con 'pplx-'.")
|
||||
|
||||
return api_key
|
||||
|
||||
def query_perplexity(prompt, model="o1"):
|
||||
|
@ -108,6 +145,12 @@ def query_perplexity(prompt, model="o1"):
|
|||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# Verificar los modelos disponibles en Perplexity - 2025
|
||||
# Modelos válidos: sonar, mistral-7b, llama-3-sonar-small, llama-3-sonar-medium, llama-3-70b, mixtral-8x7b, codellama-70b
|
||||
# Si 'o1' no funciona, probar con 'sonar' o 'mistral-7b'
|
||||
if model == "o1":
|
||||
model = "sonar" # Usar sonar como fallback en caso de que o1 no esté disponible
|
||||
|
||||
data = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
|
@ -124,22 +167,52 @@ def query_perplexity(prompt, model="o1"):
|
|||
}
|
||||
|
||||
try:
|
||||
logger.info(f"Enviando consulta a Perplexity usando modelo: {model}")
|
||||
logger.info(f"Prompt: {prompt}")
|
||||
|
||||
# Convertir el diccionario a JSON para obtener la representación exacta que se enviará
|
||||
request_body = json.dumps(data, indent=2)
|
||||
logger.info(f"Cuerpo de la solicitud: {request_body}")
|
||||
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
response.raise_for_status()
|
||||
|
||||
# Mostrar información de respuesta
|
||||
logger.info(f"Código de estado HTTP: {response.status_code}")
|
||||
logger.info(f"Respuesta (raw): {response.text[:200]}...") # Mostrar los primeros 200 caracteres
|
||||
|
||||
# Si la respuesta no es 200, mostrar el error completo
|
||||
if response.status_code != 200:
|
||||
logger.error(f"Error de API Perplexity ({response.status_code}): {response.text}")
|
||||
# Intentar analizar el error para obtener más información
|
||||
try:
|
||||
error_data = response.json()
|
||||
error_message = error_data.get("error", {}).get("message", "Mensaje de error no disponible")
|
||||
logger.error(f"Mensaje de error: {error_message}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# Continuar si la respuesta es exitosa
|
||||
result = response.json()
|
||||
|
||||
# Extraer solo el valor numérico de la respuesta
|
||||
content = result.get('choices', [{}])[0].get('message', {}).get('content', '')
|
||||
logger.info(f"Contenido de la respuesta: {content}")
|
||||
|
||||
# Intentar encontrar un número con 2 decimales en la respuesta
|
||||
match = re.search(r'\$?(\d+\.\d{2})', content)
|
||||
if match:
|
||||
return float(match.group(1))
|
||||
value = float(match.group(1))
|
||||
logger.info(f"Valor extraído: {value}")
|
||||
return value
|
||||
|
||||
# Si no encuentra un formato exacto, intentar convertir toda la respuesta a float
|
||||
try:
|
||||
# Eliminar cualquier símbolo de moneda y espacios
|
||||
cleaned_content = re.sub(r'[^\d.]', '', content)
|
||||
return round(float(cleaned_content), 2)
|
||||
value = round(float(cleaned_content), 2)
|
||||
logger.info(f"Valor limpiado y extraído: {value}")
|
||||
return value
|
||||
except ValueError:
|
||||
logger.error(f"No se pudo extraer un valor numérico de la respuesta: {content}")
|
||||
return None
|
||||
|
@ -147,6 +220,9 @@ def query_perplexity(prompt, model="o1"):
|
|||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error al conectar con la API de Perplexity: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Error inesperado en la consulta a Perplexity: {e}")
|
||||
return None
|
||||
|
||||
# Variable global para indicar si pycountry está disponible
|
||||
pycountry_available = False
|
||||
|
@ -289,7 +365,17 @@ def update_rate_files():
|
|||
rate_files = glob.glob(str(CONFIG_DIR / '*.rate'))
|
||||
logger.info(f"Encontrados {len(rate_files)} archivos de tarifas para actualizar.")
|
||||
|
||||
# Control de errores para limitar los intentos de API
|
||||
consecutive_errors = 0
|
||||
max_consecutive_errors = 3
|
||||
|
||||
for rate_file in rate_files:
|
||||
# Si hemos tenido demasiados errores consecutivos, detener el proceso
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
logger.error(f"Se detectaron {consecutive_errors} errores consecutivos. Deteniendo el proceso.")
|
||||
logger.error("Verifique su conexión a internet y la API key de Perplexity.")
|
||||
break
|
||||
|
||||
programmer_type, region_code = parse_rate_filename(rate_file)
|
||||
|
||||
if programmer_type is None:
|
||||
|
@ -309,17 +395,25 @@ def update_rate_files():
|
|||
logger.info(f"Consultando tarifa para {programmer_type}" +
|
||||
(f" en región {region_code}" if region_code else ""))
|
||||
|
||||
# Consultar a Perplexity
|
||||
rate = query_perplexity(prompt, model)
|
||||
|
||||
if rate is not None:
|
||||
# Guardar el resultado en el archivo
|
||||
with open(rate_file, 'w') as f:
|
||||
f.write(f"{rate:.2f}\n")
|
||||
logger.info(f"Actualizado {os.path.basename(rate_file)} con valor: {rate:.2f}")
|
||||
else:
|
||||
logger.error(f"No se pudo obtener la tarifa para {programmer_type}" +
|
||||
(f" en región {region_code}" if region_code else ""))
|
||||
try:
|
||||
# Consultar a Perplexity
|
||||
rate = query_perplexity(prompt, model)
|
||||
|
||||
if rate is not None:
|
||||
# Guardar el resultado en el archivo
|
||||
with open(rate_file, 'w') as f:
|
||||
f.write(f"{rate:.2f}\n")
|
||||
logger.info(f"Actualizado {os.path.basename(rate_file)} con valor: {rate:.2f}")
|
||||
consecutive_errors = 0 # Reiniciar contador de errores tras un éxito
|
||||
else:
|
||||
logger.error(f"No se pudo obtener la tarifa para {programmer_type}" +
|
||||
(f" en región {region_code}" if region_code else ""))
|
||||
consecutive_errors += 1
|
||||
except Exception as e:
|
||||
logger.error(f"Error al actualizar {os.path.basename(rate_file)}: {e}")
|
||||
consecutive_errors += 1
|
||||
# Pequeña pausa tras un error para evitar sobrecargar la API
|
||||
time.sleep(2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
|
Loading…
Reference in a new issue