diff --git a/Agente_de_datos_de_ventas.py b/Agente_de_datos_de_ventas.py new file mode 100644 index 0000000..7d67b0c --- /dev/null +++ b/Agente_de_datos_de_ventas.py @@ -0,0 +1,378 @@ +# Constantes ########################################################################## + +PROMPT = "Dame los datos de ventas de los ultimos 3 meses del centro de Alcobendas" + + +# Librerias de Agno ################################################################ + +from agno.agent import Agent +from agno.models.openai import OpenAIChat + +# Prefect imports ################################################################## + +from prefect import task, flow +from prefect.logging import get_run_logger +from prefect.filesystems import LocalFileSystem + +local_file_system_block = LocalFileSystem.load("localfile") + +# Cargar variables de entorno ###################################################### + +import os +from dotenv import load_dotenv + +load_dotenv() + +openai_api_key = os.getenv("OPENAI_API_KEY") + + +# Imports adicionales ######################################################### + +import traceback +from uuid import uuid4 + +# Importaciones de Archivos ######################################################### + +from jinja2 import Template +import yaml +import json + + +# Utils ################################################################################### + +from utils.conseguir_datos_bq import consultar_bigquery_paginado +from utils.transformar_datos import limpiar_datos_para_json + + + + +####################################################### +# Agentes ########################################## + + +with open("agents/Generador_sql_ventas.yaml", "r", encoding="utf-8") as f: + experto_sql_ventas = yaml.safe_load(f) + +with open("agents/Analizador_de_datos_de_ventas.yaml", "r", encoding="utf-8") as f: + analizador_de_datos_de_ventas = yaml.safe_load(f) + + + + +######################################################### +# Datos extras ########################## + +with open("schemas_bbdd/Objeto_ventas.json", "r", encoding="utf-8") as f: + schema_json_ventas = json.load(f) + +with open("detalles_para_agentes/centros_disponibles.md", "r", encoding="utf-8") as f: + detalles_centros = f.read() + +############################################################# +# Datos añadidos a los agentes ########################## + +schema_str_ventas = json.dumps(schema_json_ventas, indent=2, ensure_ascii=False) +contexto_ventas = { + "esquema_ventas": schema_str_ventas, + "centros_disponibles": detalles_centros +} + +contexto_ventas_analizador = { + "centros_disponibles": detalles_centros +} + +# Datos para Generador de SQL + +template_ventas = Template(experto_sql_ventas["system_message"]) +rendered_yaml_ventas_sql = template_ventas.render(esquema_ventas=contexto_ventas) +experto_sql_ventas["system_message"] = rendered_yaml_ventas_sql + + +# Datos para Analizador de datos de ventas + +template_ventas_analizador = Template(analizador_de_datos_de_ventas["system_message"]) +rendered_yaml_ventas_analisis = template_ventas_analizador.render(esquema_ventas=contexto_ventas_analizador) +analizador_de_datos_de_ventas["system_message"] = rendered_yaml_ventas_analisis + + + + + + + + + + + + + + + +############################################################### +# Tareas ########################################################## + + + +# Generar SQL ventas ###################################### + +@task(name="Convierte_prompt_a_sql", log_prints=True) +def Convierte_prompt_a_sql(prompt_de_usuario: str): + prefect_logger = get_run_logger() + + prefect_logger.debug("Creando el agente con OpenAI") + + agente = Agent( + model=OpenAIChat(id="gpt-4o-mini", api_key=openai_api_key), + name=experto_sql_ventas["name"], + description=experto_sql_ventas["description"], + system_message=experto_sql_ventas["system_message"], + debug_mode=True, + ) + + prefect_logger.debug("Agente creado correctamente") + prefect_logger.debug("Enviando el prompt al agente") + + try: + resultado = agente.run(f"devuelve el sql para el siguiente requerimiento: {prompt_de_usuario}") + prefect_logger.debug("Prompt enviado correctamente") + + # Imprime la respuesta del agente en el log Prefect + prefect_logger.info("=== RESPUESTA DEL AGENTE ===") + prefect_logger.info(str(resultado.content.strip())) # imprime texto generado + prefect_logger.info("============================") + + # Si debug_mode está activado, puedes imprimir los logs internos del agente + if hasattr(agente, "messages"): + prefect_logger.debug("=== LOG INTERNO DEL AGENTE ===") + for msg in agente.messages: + prefect_logger.debug(f"{msg.role}: {msg.content}") + prefect_logger.debug("===============================") + + prefect_logger.debug("Ejecución completada correctamente ✅") + return resultado.content.strip() + + except Exception as e: + prefect_logger.error("Error al enviar el prompt al agente") + traceback_str = traceback.format_exc() + prefect_logger.error(f"Traceback: {traceback_str}") + raise e + + + + + + + + + + +# Conseguir datos de ventas a partir del SQL generado ############################## + +@task(name="Consigue_datos_ventas", log_prints=True) +def Consigue_datos_ventas(sql_generado_por_agente: str, num_pagina_deseada: int = 1): + prefect_logger = get_run_logger() + prefect_logger.info("Iniciando consulta de datos de ventas en BigQuery...") + + try: + paginas = consultar_bigquery_paginado(sql_generado_por_agente) + + total_paginas = 0 + resultados_finales = [] + + for pagina in paginas: + total_paginas += 1 + if total_paginas == num_pagina_deseada: + prefect_logger.info(f"✅ Página {num_pagina_deseada} obtenida con {len(pagina)} filas") + resultados_finales = pagina + + if not resultados_finales: + prefect_logger.warning(f"⚠️ No se encontró la página {num_pagina_deseada}. Total de páginas disponibles: {total_paginas}") + return { + "pagina": num_pagina_deseada, + "total_paginas": total_paginas, + "datos": [], + "mensaje": f"No se encontró la página {num_pagina_deseada}" + } + + # Mostrar ejemplo en logs + ejemplo = resultados_finales[:5] + # prefect_logger.info("Ejemplo de datos obtenidos:") + # for fila in ejemplo: + # prefect_logger.info(str(fila)) + + prefect_logger.info( + f"Consulta completada ✅ Página devuelta: {num_pagina_deseada} / {total_paginas} " + f"con {len(resultados_finales)} filas" + ) + + datos_ventas = { + "pagina": num_pagina_deseada, + "total_paginas": total_paginas, + "filas_en_pagina": len(resultados_finales), + "datos": resultados_finales, + "ejemplo": ejemplo, + "descripcion": ( + f"Página {num_pagina_deseada} de {total_paginas}. " + f"Contiene {len(resultados_finales)} filas. " + "Los datos son los resultados de la consulta SQL proporcionada." + ) + } + + prefect_logger.info(datos_ventas) + + # 📦 Devolver información accesible al agente + return datos_ventas + + except Exception as e: + prefect_logger.error("❌ Error durante la consulta en BigQuery") + traceback_str = traceback.format_exc() + prefect_logger.error(f"Traceback: {traceback_str}") + raise e + + + + + + + + + + + + + + +# Analizador de resultados ########################################################## +@task(name="Analizador_de_resultados", log_prints=True) +def Analizador_de_resultados(datos_ventas: dict, prompt_de_usuario: str): + prefect_logger = get_run_logger() + prefect_logger.info("Iniciando análisis de resultados de ventas...") + + try: + agente = Agent( + model=OpenAIChat(id="gpt-4o-mini", api_key=openai_api_key), + name=analizador_de_datos_de_ventas["name"], + description=analizador_de_datos_de_ventas["description"], + system_message=analizador_de_datos_de_ventas["system_message"], + debug_mode=True, + ) + + prefect_logger.info("Agente Analizador_de_datos creado correctamente ✅") + + pagina_actual = datos_ventas.get("pagina", 1) + total_paginas = datos_ventas.get("total_paginas", 1) + filas_en_pagina = datos_ventas.get("filas_en_pagina", 0) + + # 🔧 Limpieza de Decimals antes del dump + datos_limpiados = limpiar_datos_para_json(datos_ventas.get("datos", [])) + datos_json = json.dumps(datos_limpiados, indent=2, ensure_ascii=False) + + prompt_agente = f""" +Analiza los siguientes datos de ventas y responde al requerimiento del usuario. + +🧠 Prompt del usuario: +{prompt_de_usuario} + +📄 Información de la página actual: +- Página actual: {pagina_actual} / {total_paginas} +- Filas en esta página: {filas_en_pagina} + +📊 Datos de esta página: +{datos_json} + +""" + + resultado = agente.run(prompt_agente) + + prefect_logger.info("✅ Análisis completado correctamente") + prefect_logger.info("=== RESULTADO DEL ANÁLISIS ===") + prefect_logger.info(resultado.content.strip()) + prefect_logger.info("===============================") + + return { + "prompt_usuario": prompt_de_usuario, + "pagina_analizada": pagina_actual, + "total_paginas": total_paginas, + "filas_analizadas": filas_en_pagina, + "analisis": resultado.content.strip(), + "datos_analizados": datos_limpiados, + } + + except Exception as e: + prefect_logger.error("❌ Error durante el análisis de resultados") + traceback_str = traceback.format_exc() + prefect_logger.error(f"Traceback: {traceback_str}") + raise e + + + + + + + + + + + + +# FLUJO PRINCIPAL ########################################################## + +@flow(name="Agente_ventas", result_storage=local_file_system_block, log_prints=True) # type: ignore +def Agente_ventas(PROMPT, num_pagina: int = 1): + prefect_logger = get_run_logger() + prefect_logger.info("🚀 Iniciando flujo Agente_ventas...") + + # 🧠 1. Generar SQL a partir del prompt + resultado_analisis = Convierte_prompt_a_sql.submit(PROMPT).result() + + # 🧮 2. Intentar obtener los datos (máximo 3 intentos) + for intento in range(3): + try: + datos_ventas = Consigue_datos_ventas.submit( + resultado_analisis, num_pagina_deseada=num_pagina + ).result() + break # ✅ Si la consulta fue exitosa, salir del bucle + + except Exception as e: + # 🧰 En caso de error, regenerar el SQL con información del fallo + resultado_analisis = Convierte_prompt_a_sql.submit(f""" +El SQL generado previamente daba error. El SQL era: {resultado_analisis}. +El error fue: {str(e)}. +Corrige el SQL para que no dé error y vuelve a generarlo. +El prompt del usuario era: {PROMPT} +""").result() + + if intento == 2: + raise e # ❌ Si falla 3 veces, detener el flujo + + # 🧾 3. Logs de diagnóstico + print("=== SQL GENERADO ===") + print(resultado_analisis) + print("====================") + print("=== DATOS DE VENTAS OBTENIDOS ===") + print(datos_ventas) + print("==================================") + + # 📊 4. Analizar los resultados con el agente analista + prefect_logger.info(f"🔎 Analizando los resultados de la página {num_pagina}...") + + analisis = Analizador_de_resultados.submit( + datos_ventas, PROMPT + ).result() + + # 🧠 5. Mostrar el análisis final + print(f"=== ANÁLISIS DE PÁGINA {num_pagina} ===") + print(analisis["analisis"]) + print("===================================") + + # ✅ 6. Devolver resultados combinados + return { + "sql_generado": resultado_analisis, + "analisis": analisis, + } + + +# Ejecución directa del flujo +if __name__ == "__main__": + # num_pagina=1 → analiza solo una página + Agente_ventas(PROMPT, num_pagina=1) \ No newline at end of file diff --git a/Router_de_agentes.py b/Router_de_agentes.py new file mode 100644 index 0000000..dae44af --- /dev/null +++ b/Router_de_agentes.py @@ -0,0 +1,44 @@ +# Constantes ########################################################################## + +PROMPT = "Dame la venta total del centro de velez malaga de marzo de 2023 desglosado por factura" + + +# Librerias de Agno ################################################################ + +from agno.agent import Agent +from agno.models.openai import OpenAIChat + +# Prefect imports ################################################################## + +from prefect import task, flow +from prefect.logging import get_run_logger +from prefect.filesystems import LocalFileSystem + +local_file_system_block = LocalFileSystem.load("localfile") + +# Cargar variables de entorno ###################################################### + +import os +from dotenv import load_dotenv + +load_dotenv() + +openai_api_key = os.getenv("OPENAI_API_KEY") + + +# Imports adicionales ######################################################### + +import traceback +from uuid import uuid4 + +# Importaciones de Archivos ######################################################### + +from jinja2 import Template +import yaml +import json + + +# Utils ################################################################################### + +from utils.conseguir_datos_bq import consultar_bigquery_paginado +from utils.transformar_datos import convertir_decimales diff --git a/agents/Analizador_de_datos_de_ventas.yaml b/agents/Analizador_de_datos_de_ventas.yaml new file mode 100644 index 0000000..8f293d3 --- /dev/null +++ b/agents/Analizador_de_datos_de_ventas.yaml @@ -0,0 +1,32 @@ +name: Analizador de datos de ventas +description: Agente que analiza los datos recibidos y genera un informe en texto claro y estructurado, sin generar código ni SQL. +system_message: > + Eres un analista de datos de ventas con más de 200 años de experiencia combinada en análisis, interpretación y comunicación de información comercial. + Tu objetivo es examinar los datos proporcionados y redactar un informe narrativo que describa los patrones, tendencias y observaciones más relevantes. + + ➤ **Tu estilo** + - Redactas informes claros, concisos y bien estructurados. + - Usas un tono profesional y analítico, pero fácil de entender. + - No generas ni mencionas código, consultas SQL, funciones, ni instrucciones técnicas. + - No inventas datos ni haces suposiciones fuera del conjunto recibido. + + ➤ **Tu enfoque** + - Observa distribuciones, totales, promedios, máximos/mínimos, y comparaciones si son posibles con los datos entregados. + - Resume las diferencias entre centros, periodos o categorías si los datos lo permiten. + - Si detectas valores anómalos o inconsistencias, menciónalos brevemente. + - Evita hacer recomendaciones o predicciones: tu labor es descriptiva, no prescriptiva. + + ➤ **Información de contexto** + Los centros disponibles para las ventas son: + {{centros_disponibles}} + (columna: "Centros___Centro_NavId__name") + + ➤ **Tu salida esperada** + Genera **únicamente** un informe de texto (sin tablas ni gráficos), que: + - Resuma los hallazgos principales. + - Destaque las observaciones relevantes. + - Organice la información en secciones o párrafos temáticos. + - Sea comprensible para directivos o analistas sin conocimiento técnico. + + ⚠️ **No generes SQL, código, fórmulas ni pseudocódigo.** + ⚠️ **Usa únicamente la información presente en los datos recibidos.** diff --git a/agents/Generador_sql_ventas.yaml b/agents/Generador_sql_ventas.yaml new file mode 100644 index 0000000..a184455 --- /dev/null +++ b/agents/Generador_sql_ventas.yaml @@ -0,0 +1,25 @@ +name: Consultor SQL de Ventas +description: Agente que solamente tiene que generar una consulta SQL para analizar datos de ventas +system_message: > + Eres un consultor SQL con 200 años de experiencia en análisis de datos de ventas. + Te especializas en extraer, transformar y cargar datos para generar informes + que ayuden a mejorar las estrategias de ventas + Agrupas efectivamente los datos y generas informes claros y concisos. + Recupera y analiza el dataset de ventas almacenado en la base de datos como autingo-159109.rag_datasets.Objeto_Ventas. + No hagas Joins, consigue todos los datos desdes la tabla "autingo-159109.rag_datasets.Objeto_Ventas" + No des explicaciones ni pasos intermedios, solo la consulta SQL. + + Los centros disponibles para las ventas son: + {{centros_disponibles}} + en la columna "Centros___Centro_NavId__name" + + Si no se te especifica que uses un centro de Cristales o Glass no lo uses. + Utiliza su centro principal sin Cristales o Glass. + + Utiliza SQL para BigQuery para extraer la información relevante y generar un informe inicial de los datos. + Utiliza este esquema para recuperar los datos de la tabla "autingo-159109.rag_datasets.Objeto_Ventas" + + COLUMNAS: + {{esquema_ventas}} + + La salida esperada es una consulta SQL generada entre ```sql SELECT ... ``` sin mas explicaciones. \ No newline at end of file diff --git a/observability_stack/victoria-data/flock.lock b/agents/Router_de_agente.yaml similarity index 100% rename from observability_stack/victoria-data/flock.lock rename to agents/Router_de_agente.yaml diff --git a/detalles_para_agentes/centros_disponibles.md b/detalles_para_agentes/centros_disponibles.md new file mode 100644 index 0000000..8663c18 --- /dev/null +++ b/detalles_para_agentes/centros_disponibles.md @@ -0,0 +1,154 @@ +Avda. Toreros +Fuengirola +Alcala Henares CRISTALES +San Fernando CRISTALES +Leganes CRISTALES +Alcobendas CRISTALES +Las Rozas +Alfafar +Huelva +MT Maria Auxiliadora CRISTALES +Almeria +Gta. Cadiz +MT Cornella +Pinto +Parla +Vaguada +Islazul +MT Vigo +MT Pintor Sorolla - Colon +MT Alexandre Rosello +MT Diagonal +Santa Engracia CRISTALES +MT San Jose de Valderas CRISTALES +Goya GLASS +MT Avenida de Francia CRISTALES +Vallecas +MT Puerto Venecia +MT Bahia de Santander +MT Sanchinarro +Cornella +Vallecas CRISTALES +Elche +Villalba +MT Avenida de España +MT Gran Via +Cordoba +MT Princesa +Majadahonda +Store +La Red +Finestrat +MT Castellana +MT Nuevo Centro +MT El Corte Ingles Cartagena +MT Goya +Aurgi Asociados +Xativa +Cornella 2 +Villalba CRISTALES +Torrevieja +Zaragoza +La Maquinista +MT Monasterio +Puerto de Santa Maria +MT San Juan de Aznalfarache +MT Malaga +MT Pozuelo +MT Monasterio CRISTALES +MT Girocentre +Santa Engracia +MT Nervion +Linares +Badajoz +San Sebastian CRISTALES +MT Gijon +MT Avenida de la Libertad +MT Talavera de la Reina +Rivas +Pola de Siero +Mostoles +MT Gran Casa +MT Compostela CRISTALES +Serrano +MT Ramon y Cajal +San Sebastian +Goya +MT Ronda de Cordoba +Granada +MT Paseo de Morella +MT Ctra de Madrid-Irun km. 236 CRISTALES +Hospitalet +Alcorcon +MT Costa de Marbella +Puerto de Santa Maria CRISTALES +MT Conquistadores +Malaga +Olias del Rey +MT Ctra de Madrid-Irun km. 236 +Alcobendas +MT Bahia de Cadiz +MT Jerez +Leganes +SP EL ESCORIAL +MT Avenida de Francia +Mataro +Torremolinos +Gava +MT Costa Mijas +MT Compostela +MT El Bercial +MT Mendez Alvaro +Vall D'Uixo +Villanueva de la Serena +Zaragoza CRISTALES +MT Bahia de Malaga +MT Ciudad de Elche +AUR ALICANTE AV. NOVELDA +MT Xanadu +MT Web +Les Franqueses +Chamartin +MT Alcala de Henares +Aluche +MT Cornella CRISTALES +Las Rozas CRISTALES +Sant Celoni +MT Ramon y Cajal CRISTALES +MT San Jose de Valderas +San Fernando +Tucan +MT Campo de las Naciones +Aurgi Web +Barbera +San Juan CRISTALES +MT Pozuelo CRISTALES +MT Siete Palmas +Sabadell +Roquetas +Sant Adria +MT Jaen +Alcala Henares +MT El Bercial CRISTALES +Denia +Unidad Movil Madrid Glass +Aurgi Asociados Gruas +Velez Malaga +Jerez +Arganda +Emilio Muñoz +MT Alicante +Granollers +Sant Boi +MT Bahia de Algeciras +Oficinas Centrales +MT Maria Auxiliadora +Barbera CRISTALES +MT Tres de Mayo +Autingo +Sant Cugat +SP JAVEA +Store CRISTALES +San Juan +Marques Vadillo +Valdemoro \ No newline at end of file diff --git a/primera_ejecucion_de_un_agente.py b/examples/primera_ejecucion_de_un_agente.py similarity index 97% rename from primera_ejecucion_de_un_agente.py rename to examples/primera_ejecucion_de_un_agente.py index b6739cf..d49651c 100644 --- a/primera_ejecucion_de_un_agente.py +++ b/examples/primera_ejecucion_de_un_agente.py @@ -19,12 +19,13 @@ load_dotenv() openai_api_key = os.getenv("OPENAI_API_KEY") -prefect_logger = get_run_logger() - # Definir tareas ############################################### @task(name="Inicializar agente financiero", log_prints=True) def inicializar_agente_financiero(): + prefect_logger = get_run_logger() + + prefect_logger.debug(f"Inicializando el agente financiero") # Aquí se podría agregar la lógica para inicializar el agente return "Agente financiero inicializado" @@ -32,9 +33,9 @@ def inicializar_agente_financiero(): @task(name="Analizar datos financieros", log_prints=True) def analizar_datos_financieros(datos: str): - prefect_logger = get_run_logger() + prefect_logger.debug(f"Creando el agente con OpenAI") prefect_logger.debug(f"analizando los datos: {datos}") diff --git a/ver_los_prompts_de_un_agente.py b/examples/ver_los_prompts_de_un_agente.py similarity index 97% rename from ver_los_prompts_de_un_agente.py rename to examples/ver_los_prompts_de_un_agente.py index 4691388..135bf88 100644 --- a/ver_los_prompts_de_un_agente.py +++ b/examples/ver_los_prompts_de_un_agente.py @@ -121,7 +121,7 @@ def averiguar_el_prompt_que_seenvia_a_openai(prompt_de_usuario: str): # Definir el flujo principal ######################################### -@flow(name="Flujo financiero", result_storage=local_file_system_block, log_prints=True) # type: ignore +@flow(name="Averiguar_prompt_enviado", result_storage=local_file_system_block, log_prints=True) # type: ignore def flujo_principal(): resultado_analisis = averiguar_el_prompt_que_seenvia_a_openai.submit(PROMPT).result() diff --git a/observability_stack/docker-compose.yml b/observability_stack/docker-compose.yml deleted file mode 100644 index c20b1bf..0000000 --- a/observability_stack/docker-compose.yml +++ /dev/null @@ -1,64 +0,0 @@ -version: '3.9' -services: - otel-collector: - image: otel/opentelemetry-collector-contrib:0.136.0 - container_name: otel-collector - command: - - --config=/etc/otel/config.yaml - volumes: - - ./otel-config.yaml:/etc/otel/config.yaml - ports: - - 4317:4317 - - 4318:4318 - depends_on: - - victoria - - tempo - victoria: - image: victoriametrics/victoria-metrics:latest - container_name: victoria - ports: - - 8428:8428 - volumes: - - ./victoria-data:/victoria-metrics-data - command: - - --storageDataPath=/victoria-metrics-data - - --retentionPeriod=3 - tempo: - image: grafana/tempo:latest - container_name: tempo - ports: - - 3200:3200 - volumes: - - ./tempo-data:/var/tempo - command: - - -config.file=/etc/tempo.yaml - configs: - - source: tempo_config - target: /etc/tempo.yaml - grafana-srv: - image: grafana/grafana-oss:latest - container_name: grafana-srv - ports: - - 33000:3000 - environment: - GF_SECURITY_ADMIN_USER: admin - GF_SECURITY_ADMIN_PASSWORD: admin123 - GF_USERS_ALLOW_SIGN_UP: 'false' - depends_on: - - victoria - - tempo - volumes: - - grafana_data:/var/lib/grafana - - ./provisioning/datasources:/etc/grafana/provisioning/datasources - - ./provisioning/dashboards:/etc/grafana/provisioning/dashboards - - ./grafana-dashboards:/var/lib/grafana/dashboards -configs: - tempo_config: - content: "\nserver:\n http_listen_port: 3200\n\ndistributor:\n receivers:\n\ - otlp:\n protocols:\n grpc:\n endpoint: \"0.0.0.0:4317\"\n http:\n\ - \ endpoint: \"0.0.0.0:4318\"\n\ningester:\n trace_idle_period: 10s\n \ - \ max_block_bytes: 1000000\n max_block_duration: 5m\n\ncompactor:\n compaction:\n\ - block_retention: 24h\n\nstorage:\n trace:\nwal:\n path: /var/tempo/wal\nlocal:\n\ - \ path: /var/tempo/blocks\n" -volumes: - grafana_data: {} diff --git a/observability_stack/grafana-dashboards/overview.json b/observability_stack/grafana-dashboards/overview.json deleted file mode 100644 index 1db2acf..0000000 --- a/observability_stack/grafana-dashboards/overview.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "id": null, - "uid": "overview", - "title": "\ud83d\udcca System Overview", - "timezone": "browser", - "panels": [ - { - "type": "graph", - "title": "CPU Usage", - "targets": [ - { - "expr": "process_cpu_seconds_total", - "legendFormat": "{{instance}}" - } - ] - }, - { - "type": "table", - "title": "Logs (simulados)", - "targets": [ - { - "expr": "up" - } - ] - } - ], - "schemaVersion": 36, - "version": 1 -} \ No newline at end of file diff --git a/observability_stack/otel-config.yaml b/observability_stack/otel-config.yaml deleted file mode 100644 index 8158e0f..0000000 --- a/observability_stack/otel-config.yaml +++ /dev/null @@ -1,42 +0,0 @@ -receivers: - otlp: - protocols: - grpc: {} - http: {} -processors: - batch: {} -exporters: - prometheusremotewrite: - endpoint: http://victoria:8428/api/v1/write - otlp/tempo: - endpoint: http://tempo:4317 - tls: - insecure: true - debug: - verbosity: normal -service: - pipelines: - metrics: - receivers: - - otlp - processors: - - batch - exporters: - - prometheusremotewrite - - debug - traces: - receivers: - - otlp - processors: - - batch - exporters: - - otlp/tempo - - debug - logs: - receivers: - - otlp - processors: - - batch - exporters: - - prometheusremotewrite - - debug diff --git a/observability_stack/provisioning/dashboards/dashboards.yaml b/observability_stack/provisioning/dashboards/dashboards.yaml deleted file mode 100644 index 93064c9..0000000 --- a/observability_stack/provisioning/dashboards/dashboards.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 -providers: -- name: default - orgId: 1 - folder: '' - type: file - disableDeletion: false - updateIntervalSeconds: 10 - options: - path: /var/lib/grafana/dashboards diff --git a/observability_stack/provisioning/datasources/datasources.yaml b/observability_stack/provisioning/datasources/datasources.yaml deleted file mode 100644 index e142544..0000000 --- a/observability_stack/provisioning/datasources/datasources.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: 1 -datasources: -- name: VictoriaMetrics - type: prometheus - access: proxy - url: http://victoria:8428 - isDefault: true -- name: Tempo - type: tempo - access: proxy - url: http://tempo:3200 diff --git a/observability_stack/victoria-data/cache/curr_hour_metric_ids b/observability_stack/victoria-data/cache/curr_hour_metric_ids deleted file mode 100644 index 452a8a5..0000000 Binary files a/observability_stack/victoria-data/cache/curr_hour_metric_ids and /dev/null differ diff --git a/observability_stack/victoria-data/cache/metric_usage_tracker b/observability_stack/victoria-data/cache/metric_usage_tracker deleted file mode 100644 index 0c359f3..0000000 Binary files a/observability_stack/victoria-data/cache/metric_usage_tracker and /dev/null differ diff --git a/observability_stack/victoria-data/cache/next_day_metric_ids_v2 b/observability_stack/victoria-data/cache/next_day_metric_ids_v2 deleted file mode 100644 index 1d7b5eb..0000000 Binary files a/observability_stack/victoria-data/cache/next_day_metric_ids_v2 and /dev/null differ diff --git a/observability_stack/victoria-data/cache/prev_hour_metric_ids b/observability_stack/victoria-data/cache/prev_hour_metric_ids deleted file mode 100644 index cb11c9a..0000000 Binary files a/observability_stack/victoria-data/cache/prev_hour_metric_ids and /dev/null differ diff --git a/observability_stack/victoria-data/cache/rollupResult.key.prefix b/observability_stack/victoria-data/cache/rollupResult.key.prefix deleted file mode 100644 index b3b767a..0000000 --- a/observability_stack/victoria-data/cache/rollupResult.key.prefix +++ /dev/null @@ -1 +0,0 @@ -Jpu6 \ No newline at end of file diff --git a/observability_stack/victoria-data/indexdb/186BA414465D796D/parts.json b/observability_stack/victoria-data/indexdb/186BA414465D796D/parts.json deleted file mode 100644 index 0637a08..0000000 --- a/observability_stack/victoria-data/indexdb/186BA414465D796D/parts.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/observability_stack/victoria-data/indexdb/186BA414465D796E/parts.json b/observability_stack/victoria-data/indexdb/186BA414465D796E/parts.json deleted file mode 100644 index 0637a08..0000000 --- a/observability_stack/victoria-data/indexdb/186BA414465D796E/parts.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/observability_stack/victoria-data/indexdb/186BA414465D796F/parts.json b/observability_stack/victoria-data/indexdb/186BA414465D796F/parts.json deleted file mode 100644 index 0637a08..0000000 --- a/observability_stack/victoria-data/indexdb/186BA414465D796F/parts.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/observability_stack/victoria-data/metadata/minTimestampForCompositeIndex b/observability_stack/victoria-data/metadata/minTimestampForCompositeIndex deleted file mode 100644 index 1b1cb4d..0000000 Binary files a/observability_stack/victoria-data/metadata/minTimestampForCompositeIndex and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 93a2c20..7de4d1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.13" dependencies = [ "agno>=2.1.1", + "google-cloud-bigquery>=3.38.0", "icecream>=2.1.8", "marimo>=0.16.5", "openai>=2.1.0", diff --git a/rag-datasets-reader-sa-key.json b/rag-datasets-reader-sa-key.json new file mode 100644 index 0000000..6479570 --- /dev/null +++ b/rag-datasets-reader-sa-key.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "autingo-159109", + "private_key_id": "c43c626d47606344cd3a255d62df1be77019cc54", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD0yeIqsGZ/RYHO\nqIZ67AN1Jd+c2rKzAOp1G/9XKuNfQV4XChkq2pVCMG78WbgUWSMlJY2OLsVdEvrS\n4W8MM9ZiXkJ0Ox3O5Y6/1w+omLBaqG/JuJU/euRvUZUIjLc36fpY0nzZu42CK7oK\nhUepE31WYf5i+MbhR5LpaOaJQQjfOhRZAd01iUCiKFGPVfI24dk0qYZnBQKVSSmH\nQD8zjNVHPo0LmXWOwOj2t4VrBq9WjrepJftzKumyieLJrMua76pMc+dmStyWb4yK\nTrk2yjXsN9QQRN3SLG+k0hedPdCa5ca+Wi0OCGBb1akaFjgAKw/ygiRtUDDUfmjv\nH/Q7wR/VAgMBAAECggEAOU8TfWGPmY/dEFQpqdkcBR7xD3CvIhPekDzWBqMSmOA9\npkC1vC/w/k5HCZr6qv7kaMO2NJm0GLKDGQBwxmdTc3O0dLBLbf8V8Mlpj9Pxg8QH\n6e7mODauCPbNYLNLCNLlSsq6sqIDgvx4QZLLAGVA9IqcKzEppJ/kX/Nwd6VLbbEe\nzm+P9RJRPGNBjpUKvkI1KLVRADLAuFQN44S9IzHItEYEi7C8Qe6EdPl5scXJn3hq\noPW7YoGgaaj7G81yXovQUK4tTSxduCsO4HhiYAF3CfYsoZ+sXQ/MpNQWZK5Yiic3\n+z6MsNSE0SYeOX9E008/PPzKwR2ehgmO2U6Lhl5AAQKBgQD8zSXbx7dEhRXI6+uV\nT+Fiuke3FGBUu6zGbMn75KGFnHaOMVfARaJnmla8EJ01ZkGMRis4f53elywUSQyJ\nBa7xCP7/FshuM9J+5vfvGM7NRc6kcUUaJSlg52/DNgeZy2cijBy0iaPy7+uyRRcO\nWhyaj3h+bv11US0dyt7TB3jf1QKBgQD34sgICtQcdkt51l+iHmAhiW2U6s8kuFDa\n9TX8bp4YaPoC65IkfutI3saLcJpqLgBb67j/HxCb6gkFr+OeQQIGN+X7J4rTUdhB\nLEWAnjMV3ow23q8tO5xN+jnLV0UCiF8EZLg7EIzLQ0MozjB9gndp8xxoEKDqWEG5\nagP9p4dAAQKBgELiF+EU7sTfHQtid5qyXqQbOrwSVQY1/RkmUS4mqCFMawVlwpyp\nD7WvXME2+BDXtAHj0q2I/gCVKGFZjkp2SXmV8rkUkwStC0Tt4KzOeHBQxsI1AZ5Q\nNKlhse0Iz2v+J5Q5U6LkQ48TsN0icF4osyalTLDOtpoiVvhp4xgcAvvdAoGAOBXL\nhGZOz5HESfDC+n886NmbPZJTA8/gG2pXqKGui39U8cwy6Kb+vSIKcgosJdH6qtGO\nrcpti5lMKUk+itPSjW2gT08HDgD6mORXZV5l2JDd0JxZrjZKiyoOYX+BUa1hMjFH\nrbV05Zh2XYkpV3xpYENtLe51OhB17mmaNY3uAAECgYBydII40sWjpvybaRc40UXH\nWLKCDPfeeSyJqrybU80nbOKI6WfmO9sBtVS/qf+4z8aDHenOhmcpXPIFhEe34ARw\nanWSjSBLI1sdn88KwtLSd2dCytN+0VnJ1RNpzHPwDK4J5wZaIJV0AYvryfWazqn4\nazDWb8s8eRHw+9nAS2TALg==\n-----END PRIVATE KEY-----\n", + "client_email": "rag-datasets-reader-sa@autingo-159109.iam.gserviceaccount.com", + "client_id": "111296269982496507213", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/rag-datasets-reader-sa%40autingo-159109.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +} diff --git a/schemas_bbdd/Objeto_ventas.json b/schemas_bbdd/Objeto_ventas.json new file mode 100644 index 0000000..d6d0d4c --- /dev/null +++ b/schemas_bbdd/Objeto_ventas.json @@ -0,0 +1,359 @@ +[ + { + "name": "Fecha", + "mode": "NULLABLE", + "type": "DATETIME", + "description": "", + "fields": [] + }, + { + "name": "numeroDocumento", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "NumeroLineas", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "idCentro", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "idProducto", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "cantidad", + "mode": "NULLABLE", + "type": "BIGNUMERIC", + "description": "", + "fields": [] + }, + { + "name": "precioUnitario", + "mode": "NULLABLE", + "type": "BIGNUMERIC", + "description": "", + "fields": [] + }, + { + "name": "Base_imponible_linea", + "mode": "NULLABLE", + "type": "BIGNUMERIC", + "description": "", + "fields": [] + }, + { + "name": "Tipo_de_documento", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "semana", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "ano", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "ano_mas_uno", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "Fecha_mas_un_ano", + "mode": "NULLABLE", + "type": "DATETIME", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__nav_id", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__id_centro", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__name", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Companies__name", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Zones__name", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Cluster_centros", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Jefes_de_centro_y_Rmos___co_51ce9994", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Jefes_de_centro_y_Rmos___co_c6afb8e8", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Jefes_de_centro_y_Rmos___co_a5423963", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Provincias_comunidades_y_su_b8d2c2a0", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Provincias_comunidades_y_su_cb74d1a1", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Centros___Centro_NavId__Provincias_comunidades_y_su_9fd8d3fc", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__id", + "mode": "NULLABLE", + "type": "INTEGER", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__nav_id", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__description", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__is_tecdoc", + "mode": "NULLABLE", + "type": "BOOLEAN", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Status_de_producto", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Is_Encargo", + "mode": "NULLABLE", + "type": "BOOLEAN", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__is_service", + "mode": "NULLABLE", + "type": "BOOLEAN", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__is_tpv", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Tipo___Navision", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Product_Groups__description", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Product_Categories__description", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo1", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo2", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo3", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo5", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo4", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo6", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo7", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo8", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo9", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idAtributo10", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idCategoria", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Producto___Nav__idGrupo", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Categoria_16_07_cgq___t_cb30d712", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Categoria_16_07_cgq___t_d83f396f", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + }, + { + "name": "Productos___Producto_NavId__Categoria_16_07_cgq___t_5e9550ff", + "mode": "NULLABLE", + "type": "STRING", + "description": "", + "fields": [] + } +] \ No newline at end of file diff --git a/utils/conseguir_datos_bq.py b/utils/conseguir_datos_bq.py new file mode 100644 index 0000000..3cda574 --- /dev/null +++ b/utils/conseguir_datos_bq.py @@ -0,0 +1,68 @@ +from google.cloud import bigquery +from typing import Generator, List, Dict +import os + +os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "rag-datasets-reader-sa-key.json" + + +def consultar_bigquery_paginado( + sql_query: str, + page_size: int = 1000, + project_id: str | None = None +) -> Generator[List[Dict], None, None]: + """ + Ejecuta una consulta SQL de BigQuery y devuelve los resultados paginados. + Limpia el texto SQL automáticamente antes de ejecutarlo. + + Args: + sql_query (str): Texto SQL (puede incluir espacios o punto y coma al final). + page_size (int): Número de filas por página. + project_id (str | None): ID del proyecto GCP (opcional si está configurado el entorno). + + Yields: + list[dict]: Página de resultados (cada fila convertida a dict). + """ + # 🧹 Limpieza del texto SQL + cleaned_query = ( + sql_query.strip() # elimina espacios y saltos al inicio y fin + .removeprefix("```sql").removeprefix("```").removesuffix("```") # limpia delimitadores markdown si los hay + .strip() # vuelve a limpiar por si quedaron espacios + ) + + # Quita el punto y coma final si lo tiene + if cleaned_query.endswith(";"): + cleaned_query = cleaned_query[:-1].strip() + + # Inicializa el cliente + client = bigquery.Client(project=project_id) + + # Ejecuta la consulta + query_job = client.query(cleaned_query) + + # Devuelve resultados paginados + iterator = query_job.result(page_size=page_size) + for page in iterator.pages: + yield [dict(row) for row in page] + + +if __name__ == "__main__": + resultado = consultar_bigquery_paginado("""```sql +SELECT + Productos___Producto_NavId__description AS producto, + Centros___Centro_NavId__name AS region, + SUM(cantidad) AS total_vendido, + SUM(cantidad * precioUnitario) AS ingreso_total +FROM + `autingo-159109.rag_datasets.Objeto_Ventas` +WHERE + Fecha >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH) +GROUP BY + producto, region +ORDER BY + total_vendido DESC; +```""") + + for pagina in resultado: + for fila in pagina: + print(fila) + print("----- Fin de página -----") \ No newline at end of file diff --git a/utils/transformar_datos.py b/utils/transformar_datos.py new file mode 100644 index 0000000..d45af6e --- /dev/null +++ b/utils/transformar_datos.py @@ -0,0 +1,32 @@ +from decimal import Decimal +from datetime import datetime, date, time + +def convertir_decimales(obj): + if isinstance(obj, list): + return [convertir_decimales(x) for x in obj] + elif isinstance(obj, dict): + return {k: convertir_decimales(v) for k, v in obj.items()} + elif isinstance(obj, Decimal): + return float(obj) + else: + return obj + + +def convertir_fechas(obj): + """ + Convierte objetos datetime, date y time a su representación ISO 8601. + """ + if isinstance(obj, list): + return [convertir_fechas(x) for x in obj] + elif isinstance(obj, dict): + return {k: convertir_fechas(v) for k, v in obj.items()} + elif isinstance(obj, (datetime, date, time)): + return obj.isoformat() + else: + return obj + + +def limpiar_datos_para_json(data): + data = convertir_decimales(data) + data = convertir_fechas(data) + return data \ No newline at end of file diff --git a/uv.lock b/uv.lock index ee0c75b..7fb7d1d 100644 --- a/uv.lock +++ b/uv.lock @@ -280,6 +280,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "agno" }, + { name = "google-cloud-bigquery" }, { name = "icecream" }, { name = "marimo" }, { name = "openai" }, @@ -292,6 +293,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "agno", specifier = ">=2.1.1" }, + { name = "google-cloud-bigquery", specifier = ">=3.38.0" }, { name = "icecream", specifier = ">=2.1.8" }, { name = "marimo", specifier = ">=0.16.5" }, { name = "openai", specifier = ">=2.1.0" }, @@ -519,6 +521,112 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, ] +[[package]] +name = "google-api-core" +version = "2.25.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/cd/63f1557235c2440fe0577acdbc32577c5c002684c58c7f4d770a92366a24/google_api_core-2.25.2.tar.gz", hash = "sha256:1c63aa6af0d0d5e37966f157a77f9396d820fba59f9e43e9415bc3dc5baff300", size = 166266, upload-time = "2025-10-03T00:07:34.778Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/d8/894716a5423933f5c8d2d5f04b16f052a515f78e815dab0c2c6f1fd105dc/google_api_core-2.25.2-py3-none-any.whl", hash = "sha256:e9a8f62d363dc8424a8497f4c2a47d6bcda6c16514c935629c257ab5d10210e7", size = 162489, upload-time = "2025-10-03T00:07:32.924Z" }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, + { name = "grpcio-status" }, +] + +[[package]] +name = "google-auth" +version = "2.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/af/5129ce5b2f9688d2fa49b463e544972a7c82b0fdb50980dafee92e121d9f/google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2", size = 292284, upload-time = "2025-09-30T22:51:26.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/a4/7319a2a8add4cc352be9e3efeff5e2aacee917c85ca2fa1647e29089983c/google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d", size = 221302, upload-time = "2025-09-30T22:51:24.212Z" }, +] + +[[package]] +name = "google-cloud-bigquery" +version = "3.38.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-resumable-media" }, + { name = "packaging" }, + { name = "python-dateutil" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/b2/a17e40afcf9487e3d17db5e36728ffe75c8d5671c46f419d7b6528a5728a/google_cloud_bigquery-3.38.0.tar.gz", hash = "sha256:8afcb7116f5eac849097a344eb8bfda78b7cfaae128e60e019193dd483873520", size = 503666, upload-time = "2025-09-17T20:33:33.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/3c/c8cada9ec282b29232ed9aed5a0b5cca6cf5367cb2ffa8ad0d2583d743f1/google_cloud_bigquery-3.38.0-py3-none-any.whl", hash = "sha256:e06e93ff7b245b239945ef59cb59616057598d369edac457ebf292bd61984da6", size = 259257, upload-time = "2025-09-17T20:33:31.404Z" }, +] + +[[package]] +name = "google-cloud-core" +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/b8/2b53838d2acd6ec6168fd284a990c76695e84c65deee79c9f3a4276f6b4f/google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53", size = 35861, upload-time = "2025-03-10T21:05:38.948Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/86/bda7241a8da2d28a754aad2ba0f6776e35b67e37c36ae0c45d49370f1014/google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e", size = 29348, upload-time = "2025-03-10T21:05:37.785Z" }, +] + +[[package]] +name = "google-crc32c" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/72/b8d785e9184ba6297a8620c8a37cf6e39b81a8ca01bb0796d7cbb28b3386/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35", size = 30467, upload-time = "2025-03-26T14:36:06.909Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/5f18076968212067c4e8ea95bf3b69669f9fc698476e5f5eb97d5b37999f/google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638", size = 30309, upload-time = "2025-03-26T15:06:15.318Z" }, + { url = "https://files.pythonhosted.org/packages/92/83/9228fe65bf70e93e419f38bdf6c5ca5083fc6d32886ee79b450ceefd1dbd/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb", size = 33133, upload-time = "2025-03-26T14:41:34.388Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ca/1ea2fd13ff9f8955b85e7956872fdb7050c4ace8a2306a6d177edb9cf7fe/google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6", size = 32773, upload-time = "2025-03-26T14:41:35.19Z" }, + { url = "https://files.pythonhosted.org/packages/89/32/a22a281806e3ef21b72db16f948cad22ec68e4bdd384139291e00ff82fe2/google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db", size = 33475, upload-time = "2025-03-26T14:29:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/002975aff514e57fc084ba155697a049b3f9b52225ec3bc0f542871dd524/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3", size = 33243, upload-time = "2025-03-26T14:41:35.975Z" }, + { url = "https://files.pythonhosted.org/packages/61/cb/c585282a03a0cea70fcaa1bf55d5d702d0f2351094d663ec3be1c6c67c52/google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9", size = 32870, upload-time = "2025-03-26T14:41:37.08Z" }, +] + +[[package]] +name = "google-resumable-media" +version = "2.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099, upload-time = "2024-08-07T22:20:38.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251, upload-time = "2024-08-07T22:20:36.409Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, +] + [[package]] name = "graphviz" version = "0.21" @@ -564,6 +672,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, ] +[[package]] +name = "grpcio" +version = "1.75.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/f7/8963848164c7604efb3a3e6ee457fdb3a469653e19002bd24742473254f8/grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2", size = 12731327, upload-time = "2025-09-26T09:03:36.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/74/bac4ab9f7722164afdf263ae31ba97b8174c667153510322a5eba4194c32/grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884", size = 5672779, upload-time = "2025-09-26T09:02:19.11Z" }, + { url = "https://files.pythonhosted.org/packages/a6/52/d0483cfa667cddaa294e3ab88fd2c2a6e9dc1a1928c0e5911e2e54bd5b50/grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac", size = 11470623, upload-time = "2025-09-26T09:02:22.117Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e4/d1954dce2972e32384db6a30273275e8c8ea5a44b80347f9055589333b3f/grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133", size = 6248838, upload-time = "2025-09-26T09:02:26.426Z" }, + { url = "https://files.pythonhosted.org/packages/06/43/073363bf63826ba8077c335d797a8d026f129dc0912b69c42feaf8f0cd26/grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d", size = 6922663, upload-time = "2025-09-26T09:02:28.724Z" }, + { url = "https://files.pythonhosted.org/packages/c2/6f/076ac0df6c359117676cacfa8a377e2abcecec6a6599a15a672d331f6680/grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d", size = 6436149, upload-time = "2025-09-26T09:02:30.971Z" }, + { url = "https://files.pythonhosted.org/packages/6b/27/1d08824f1d573fcb1fa35ede40d6020e68a04391709939e1c6f4193b445f/grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446", size = 7067989, upload-time = "2025-09-26T09:02:33.233Z" }, + { url = "https://files.pythonhosted.org/packages/c6/98/98594cf97b8713feb06a8cb04eeef60b4757e3e2fb91aa0d9161da769843/grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e", size = 8010717, upload-time = "2025-09-26T09:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7e/bb80b1bba03c12158f9254762cdf5cced4a9bc2e8ed51ed335915a5a06ef/grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc", size = 7463822, upload-time = "2025-09-26T09:02:38.26Z" }, + { url = "https://files.pythonhosted.org/packages/23/1c/1ea57fdc06927eb5640f6750c697f596f26183573069189eeaf6ef86ba2d/grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970", size = 3938490, upload-time = "2025-09-26T09:02:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/4b/24/fbb8ff1ccadfbf78ad2401c41aceaf02b0d782c084530d8871ddd69a2d49/grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66", size = 4642538, upload-time = "2025-09-26T09:02:42.519Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1b/9a0a5cecd24302b9fdbcd55d15ed6267e5f3d5b898ff9ac8cbe17ee76129/grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7", size = 5673319, upload-time = "2025-09-26T09:02:44.742Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ec/9d6959429a83fbf5df8549c591a8a52bb313976f6646b79852c4884e3225/grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66", size = 11480347, upload-time = "2025-09-26T09:02:47.539Z" }, + { url = "https://files.pythonhosted.org/packages/09/7a/26da709e42c4565c3d7bf999a9569da96243ce34a8271a968dee810a7cf1/grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421", size = 6254706, upload-time = "2025-09-26T09:02:50.4Z" }, + { url = "https://files.pythonhosted.org/packages/f1/08/dcb26a319d3725f199c97e671d904d84ee5680de57d74c566a991cfab632/grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8", size = 6922501, upload-time = "2025-09-26T09:02:52.711Z" }, + { url = "https://files.pythonhosted.org/packages/78/66/044d412c98408a5e23cb348845979a2d17a2e2b6c3c34c1ec91b920f49d0/grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c", size = 6437492, upload-time = "2025-09-26T09:02:55.542Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9d/5e3e362815152aa1afd8b26ea613effa005962f9da0eec6e0e4527e7a7d1/grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64", size = 7081061, upload-time = "2025-09-26T09:02:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1a/46615682a19e100f46e31ddba9ebc297c5a5ab9ddb47b35443ffadb8776c/grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e", size = 8010849, upload-time = "2025-09-26T09:03:00.548Z" }, + { url = "https://files.pythonhosted.org/packages/67/8e/3204b94ac30b0f675ab1c06540ab5578660dc8b690db71854d3116f20d00/grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0", size = 7464478, upload-time = "2025-09-26T09:03:03.096Z" }, + { url = "https://files.pythonhosted.org/packages/b7/97/2d90652b213863b2cf466d9c1260ca7e7b67a16780431b3eb1d0420e3d5b/grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c", size = 4012672, upload-time = "2025-09-26T09:03:05.477Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/e2e6e9fc1c985cd1a59e6996a05647c720fe8a03b92f5ec2d60d366c531e/grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464", size = 4772475, upload-time = "2025-09-26T09:03:07.661Z" }, +] + +[[package]] +name = "grpcio-status" +version = "1.75.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/1ce0e3eedcdc08b4739b3da5836f31142ec8bee1a9ae0ad8dc0dc39a14bf/grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8", size = 13671, upload-time = "2025-09-26T09:13:16.412Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/ad/6f414bb0b36eee20d93af6907256f208ffcda992ae6d3d7b6a778afe31e6/grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c", size = 14428, upload-time = "2025-09-26T09:12:55.516Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1282,6 +1435,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, ] +[[package]] +name = "proto-plus" +version = "1.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/ac/87285f15f7cce6d4a008f33f1757fb5a13611ea8914eb58c3d0d26243468/proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012", size = 56142, upload-time = "2025-03-10T15:54:38.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/6d/280c4c2ce28b1593a19ad5239c8b826871fc6ec275c21afc8e1820108039/proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66", size = 50163, upload-time = "2025-03-10T15:54:37.335Z" }, +] + [[package]] name = "protobuf" version = "6.32.1" @@ -1321,6 +1486,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/49/a6cfc94a9c483b1fa401fbcb23aca7892f60c7269c5ffa2ac408364f80dc/psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2", size = 2569060, upload-time = "2025-01-04T20:09:15.28Z" }, ] +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pycparser" version = "2.23" @@ -1736,6 +1922,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, ] +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + [[package]] name = "ruamel-yaml" version = "0.18.15"