[REFACTORED] Cambiar obtención de tarifas por tipo de programador
- Reorganizar la lógica para crear un archivo por tipo de programador - Simplificar la estructura de archivos usando [tipo].rate - Crear tarifas solo para tipos de programadores que no tengan archivo existente - Mejorar prompts para obtener específicamente tarifas por hora - Añadir función get_programmer_types para centralizar la lista de profesionales - Optimizar la función get_fallback_rate para soportar nueva estructura - Aplicar consistentemente el límite de 200 USD/hora para todas las tarifas 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
935831aa5e
commit
fb00f958dc
1 changed files with 99 additions and 72 deletions
|
@ -487,7 +487,7 @@ Responde solo con el valor numérico con dos decimales."""
|
|||
|
||||
return prompt
|
||||
|
||||
def get_fallback_rate(programmer_type, region_code):
|
||||
def get_fallback_rate(programmer_type, region_code=None):
|
||||
"""
|
||||
Proporciona una tarifa de respaldo cuando la API falla o no está disponible.
|
||||
Usa estimaciones basadas en datos de mercado generales.
|
||||
|
@ -514,6 +514,11 @@ def get_fallback_rate(programmer_type, region_code):
|
|||
# Valor por defecto si el tipo no está en la lista
|
||||
base_rate = base_rates.get(programmer_type, 35.00)
|
||||
|
||||
# Si no se proporciona región o es None, devolver la tarifa base global (promedio mundial)
|
||||
if region_code is None:
|
||||
return base_rate
|
||||
|
||||
# Si se proporciona región, aplicar factor regional
|
||||
# Factores de ajuste por región
|
||||
region_factors = {
|
||||
'ww': 1.0, # Promedio mundial
|
||||
|
@ -535,10 +540,6 @@ def get_fallback_rate(programmer_type, region_code):
|
|||
'it': 1.3 # Italia
|
||||
}
|
||||
|
||||
# Si no hay región, devolver la tarifa base
|
||||
if region_code is None:
|
||||
return base_rate
|
||||
|
||||
# Aplicar factor regional
|
||||
factor = region_factors.get(region_code, 1.0)
|
||||
adjusted_rate = base_rate * factor
|
||||
|
@ -553,8 +554,31 @@ def show_result(message):
|
|||
if SHOW_RESULTS:
|
||||
print(message)
|
||||
|
||||
def get_programmer_types():
|
||||
"""Retorna una lista de todos los tipos de programadores soportados."""
|
||||
return [
|
||||
'bash',
|
||||
'python',
|
||||
'fullstack',
|
||||
'frontend',
|
||||
'backend',
|
||||
'devops',
|
||||
'mobile',
|
||||
'java',
|
||||
'php',
|
||||
'ruby',
|
||||
'dotnet',
|
||||
'data',
|
||||
'ml',
|
||||
'cloud',
|
||||
'odoo'
|
||||
]
|
||||
|
||||
def update_rate_files():
|
||||
"""Actualiza los archivos de tarifas con datos de Perplexity."""
|
||||
"""
|
||||
Actualiza los archivos de tarifas con datos de Perplexity.
|
||||
Crea un archivo por tipo de programador con la tarifa por hora general.
|
||||
"""
|
||||
# Obtener modelo configurado
|
||||
model = get_ai_model()
|
||||
logger.info(f"Usando modelo de IA: {model}")
|
||||
|
@ -571,43 +595,39 @@ def update_rate_files():
|
|||
# Crear la carpeta rates si no existe
|
||||
os.makedirs(RATES_DIR, exist_ok=True)
|
||||
|
||||
# Buscar todos los archivos .rate en la nueva ubicación
|
||||
rate_files = glob.glob(str(RATES_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
|
||||
rate_threshold = 200.00 # Umbral para considerar una tarifa como excesiva
|
||||
|
||||
# Obtener la lista de tipos de programadores
|
||||
programmer_types = get_programmer_types()
|
||||
logger.info(f"Procesando {len(programmer_types)} tipos de programadores.")
|
||||
|
||||
# Procesar cada tipo de programador
|
||||
for programmer_type in programmer_types:
|
||||
# Comprobar si ya existe el archivo para este tipo de programador
|
||||
rate_file = RATES_DIR / f"{programmer_type}.rate"
|
||||
|
||||
# Si el archivo ya existe, saltamos este tipo
|
||||
if rate_file.exists():
|
||||
logger.info(f"El archivo {rate_file} ya existe. Saltando.")
|
||||
continue
|
||||
|
||||
for rate_file in rate_files:
|
||||
# Si hemos tenido demasiados errores consecutivos, cambiar a modo de respaldo
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
logger.error(f"Se detectaron {consecutive_errors} errores consecutivos. Cambiando a modo de respaldo.")
|
||||
api_available = False
|
||||
|
||||
programmer_type, region_code = parse_rate_filename(rate_file)
|
||||
|
||||
if programmer_type is None:
|
||||
logger.warning(f"No se pudo analizar el nombre del archivo: {rate_file}, saltando.")
|
||||
continue
|
||||
|
||||
# Si es un archivo especial como kdevs.rate
|
||||
if region_code is None:
|
||||
prompt = generate_prompt(programmer_type, None)
|
||||
# Saltamos los archivos especiales que no necesitan actualización
|
||||
if prompt is None:
|
||||
logger.info(f"Saltando archivo especial: {os.path.basename(rate_file)}")
|
||||
continue
|
||||
else:
|
||||
prompt = generate_prompt(programmer_type, region_code)
|
||||
# Generar el prompt para la consulta
|
||||
prompt = generate_prompt_base(programmer_type)
|
||||
|
||||
# Variable para almacenar la tarifa
|
||||
rate = None
|
||||
|
||||
# Si la API está disponible, intentar consultarla
|
||||
if api_available:
|
||||
logger.info(f"Consultando tarifa para {programmer_type}" +
|
||||
(f" en región {region_code}" if region_code else ""))
|
||||
logger.info(f"Consultando tarifa para {programmer_type}")
|
||||
|
||||
try:
|
||||
# Consultar a Perplexity
|
||||
|
@ -616,69 +636,76 @@ def update_rate_files():
|
|||
if rate is not None:
|
||||
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 ""))
|
||||
logger.error(f"No se pudo obtener la tarifa para {programmer_type}")
|
||||
consecutive_errors += 1
|
||||
except Exception as e:
|
||||
logger.error(f"Error al actualizar {os.path.basename(rate_file)}: {e}")
|
||||
logger.error(f"Error al consultar tarifa para {programmer_type}: {e}")
|
||||
consecutive_errors += 1
|
||||
# Pequeña pausa tras un error para evitar sobrecargar la API
|
||||
time.sleep(2)
|
||||
|
||||
# Si la API falló o no está disponible, usar valor de respaldo
|
||||
if rate is None:
|
||||
logger.warning(f"Usando valor de respaldo para {programmer_type}" +
|
||||
(f" en región {region_code}" if region_code else ""))
|
||||
rate = get_fallback_rate(programmer_type, region_code)
|
||||
logger.warning(f"Usando valor de respaldo para {programmer_type}")
|
||||
rate = get_fallback_rate(programmer_type, None)
|
||||
logger.info(f"Valor de respaldo generado: {rate:.2f}")
|
||||
|
||||
# Comprobar si la tarifa es superior a 200.00 USD/hora
|
||||
rate_threshold = 200.00
|
||||
# Comprobar si la tarifa es superior al umbral establecido
|
||||
if rate > rate_threshold:
|
||||
# Buscar todas las tarifas del mismo tipo de programador para calcular el promedio
|
||||
same_type_rates = []
|
||||
for other_file in rate_files:
|
||||
other_type, other_region = parse_rate_filename(other_file)
|
||||
|
||||
# Solo considerar archivos del mismo tipo con regiones válidas
|
||||
if other_type == programmer_type and other_region is not None and other_region != region_code:
|
||||
try:
|
||||
# Leer el archivo para obtener la tarifa actual
|
||||
with open(other_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read().strip()
|
||||
if content:
|
||||
other_rate = float(content)
|
||||
# Solo incluir tarifas que no excedan el umbral
|
||||
if other_rate <= rate_threshold:
|
||||
same_type_rates.append(other_rate)
|
||||
logger.info(f"Considerando tarifa válida para promedio: {other_rate:.2f} de {other_region}")
|
||||
else:
|
||||
logger.info(f"Ignorando tarifa para promedio (excede umbral): {other_rate:.2f} de {other_region}")
|
||||
except (ValueError, FileNotFoundError):
|
||||
continue
|
||||
|
||||
# Si encontramos tarifas del mismo tipo, calcular el promedio
|
||||
if same_type_rates:
|
||||
avg_rate = sum(same_type_rates) / len(same_type_rates)
|
||||
# El promedio nunca excederá el umbral ya que solo incluimos tarifas válidas
|
||||
logger.warning(f"Tarifa {rate:.2f} supera el umbral de {rate_threshold}. Usando promedio de {len(same_type_rates)} tarifas válidas: {avg_rate:.2f}")
|
||||
rate = round(avg_rate, 2)
|
||||
else:
|
||||
# Si no hay otras tarifas para calcular el promedio, usar un valor de fallback
|
||||
# Asegurarnos que el valor nunca exceda el umbral
|
||||
fallback_rate = min(rate_threshold, get_fallback_rate(programmer_type, region_code) * 1.5)
|
||||
logger.warning(f"Tarifa {rate:.2f} supera el umbral de {rate_threshold}. No hay datos para promedio. Usando valor ajustado: {fallback_rate:.2f}")
|
||||
rate = round(fallback_rate, 2)
|
||||
logger.warning(f"Tarifa {rate:.2f} supera el umbral de {rate_threshold}. Ajustando...")
|
||||
rate = min(rate, rate_threshold)
|
||||
|
||||
# Guardar el resultado en el archivo - solo el valor numérico con dos decimales, sin salto de línea
|
||||
with open(rate_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"{rate:.2f}")
|
||||
|
||||
# Registrar en el log y mostrar el resultado en la consola
|
||||
result_message = f"Tarifa para {programmer_type}" + (f" en región {region_code}" if region_code else "") + f": {rate:.2f} USD"
|
||||
logger.info(f"Actualizado {os.path.basename(rate_file)} con valor: {rate:.2f}")
|
||||
result_message = f"Tarifa para {programmer_type}: {rate:.2f} USD/hora"
|
||||
logger.info(f"Creado archivo {rate_file} con valor: {rate:.2f}")
|
||||
show_result(result_message)
|
||||
|
||||
# Pequeña pausa para no sobrecargar la API
|
||||
time.sleep(1)
|
||||
|
||||
def generate_prompt_base(programmer_type):
|
||||
"""
|
||||
Genera un prompt para consultar la tarifa base por hora para un tipo de programador.
|
||||
"""
|
||||
programmer_map = {
|
||||
'bash': 'programador de scripts Bash/Shell',
|
||||
'python': 'desarrollador Python',
|
||||
'fullstack': 'desarrollador Full Stack',
|
||||
'frontend': 'desarrollador Frontend',
|
||||
'backend': 'desarrollador Backend',
|
||||
'devops': 'ingeniero DevOps',
|
||||
'mobile': 'desarrollador de aplicaciones móviles',
|
||||
'java': 'desarrollador Java',
|
||||
'php': 'desarrollador PHP',
|
||||
'ruby': 'desarrollador Ruby',
|
||||
'dotnet': 'desarrollador .NET',
|
||||
'data': 'científico de datos',
|
||||
'ml': 'ingeniero de Machine Learning',
|
||||
'cloud': 'arquitecto Cloud',
|
||||
'odoo': 'desarrollador Odoo'
|
||||
}
|
||||
|
||||
programmer_description = programmer_map.get(programmer_type, f'desarrollador {programmer_type}')
|
||||
|
||||
# Usamos el año actual para la consulta principal
|
||||
current_year = time.strftime("%Y")
|
||||
prompt = f"""¿Cuál es la tarifa POR HORA promedio mundial en dólares estadounidenses (USD) para un {programmer_description} en {current_year}?
|
||||
|
||||
Si no tienes datos del {current_year}, usa la información más reciente y haz una estimación aproximada.
|
||||
|
||||
IMPORTANTE:
|
||||
1. Necesito ESPECÍFICAMENTE el valor POR HORA, no mensual ni anual.
|
||||
2. Si encuentras información en otros períodos de tiempo (mensual, anual, etc.), conviértela a tarifa POR HORA.
|
||||
3. Busca un promedio mundial, con conocimiento de valores actuales de mercado.
|
||||
|
||||
Responde ÚNICAMENTE con el valor numérico con dos decimales."""
|
||||
|
||||
return prompt
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parámetros de línea de comandos para controlar el comportamiento
|
||||
import argparse
|
||||
|
|
Loading…
Reference in a new issue