[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 sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Configuración de logging
|
# Configuración de logging
|
||||||
|
@ -91,11 +92,47 @@ def get_ai_model():
|
||||||
return "o1"
|
return "o1"
|
||||||
|
|
||||||
def get_perplexity_api_key():
|
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')
|
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:
|
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)
|
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
|
return api_key
|
||||||
|
|
||||||
def query_perplexity(prompt, model="o1"):
|
def query_perplexity(prompt, model="o1"):
|
||||||
|
@ -108,6 +145,12 @@ def query_perplexity(prompt, model="o1"):
|
||||||
"Content-Type": "application/json"
|
"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 = {
|
data = {
|
||||||
"model": model,
|
"model": model,
|
||||||
"messages": [
|
"messages": [
|
||||||
|
@ -124,22 +167,52 @@ def query_perplexity(prompt, model="o1"):
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
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 = 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()
|
result = response.json()
|
||||||
|
|
||||||
# Extraer solo el valor numérico de la respuesta
|
# Extraer solo el valor numérico de la respuesta
|
||||||
content = result.get('choices', [{}])[0].get('message', {}).get('content', '')
|
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
|
# Intentar encontrar un número con 2 decimales en la respuesta
|
||||||
match = re.search(r'\$?(\d+\.\d{2})', content)
|
match = re.search(r'\$?(\d+\.\d{2})', content)
|
||||||
if match:
|
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
|
# Si no encuentra un formato exacto, intentar convertir toda la respuesta a float
|
||||||
try:
|
try:
|
||||||
# Eliminar cualquier símbolo de moneda y espacios
|
# Eliminar cualquier símbolo de moneda y espacios
|
||||||
cleaned_content = re.sub(r'[^\d.]', '', content)
|
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:
|
except ValueError:
|
||||||
logger.error(f"No se pudo extraer un valor numérico de la respuesta: {content}")
|
logger.error(f"No se pudo extraer un valor numérico de la respuesta: {content}")
|
||||||
return None
|
return None
|
||||||
|
@ -147,6 +220,9 @@ def query_perplexity(prompt, model="o1"):
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logger.error(f"Error al conectar con la API de Perplexity: {e}")
|
logger.error(f"Error al conectar con la API de Perplexity: {e}")
|
||||||
return None
|
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
|
# Variable global para indicar si pycountry está disponible
|
||||||
pycountry_available = False
|
pycountry_available = False
|
||||||
|
@ -289,7 +365,17 @@ def update_rate_files():
|
||||||
rate_files = glob.glob(str(CONFIG_DIR / '*.rate'))
|
rate_files = glob.glob(str(CONFIG_DIR / '*.rate'))
|
||||||
logger.info(f"Encontrados {len(rate_files)} archivos de tarifas para actualizar.")
|
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:
|
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)
|
programmer_type, region_code = parse_rate_filename(rate_file)
|
||||||
|
|
||||||
if programmer_type is None:
|
if programmer_type is None:
|
||||||
|
@ -309,17 +395,25 @@ def update_rate_files():
|
||||||
logger.info(f"Consultando tarifa para {programmer_type}" +
|
logger.info(f"Consultando tarifa para {programmer_type}" +
|
||||||
(f" en región {region_code}" if region_code else ""))
|
(f" en región {region_code}" if region_code else ""))
|
||||||
|
|
||||||
# Consultar a Perplexity
|
try:
|
||||||
rate = query_perplexity(prompt, model)
|
# Consultar a Perplexity
|
||||||
|
rate = query_perplexity(prompt, model)
|
||||||
if rate is not None:
|
|
||||||
# Guardar el resultado en el archivo
|
if rate is not None:
|
||||||
with open(rate_file, 'w') as f:
|
# Guardar el resultado en el archivo
|
||||||
f.write(f"{rate:.2f}\n")
|
with open(rate_file, 'w') as f:
|
||||||
logger.info(f"Actualizado {os.path.basename(rate_file)} con valor: {rate:.2f}")
|
f.write(f"{rate:.2f}\n")
|
||||||
else:
|
logger.info(f"Actualizado {os.path.basename(rate_file)} con valor: {rate:.2f}")
|
||||||
logger.error(f"No se pudo obtener la tarifa para {programmer_type}" +
|
consecutive_errors = 0 # Reiniciar contador de errores tras un éxito
|
||||||
(f" en región {region_code}" if region_code else ""))
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue