From 824f7d8dafd5b0e8e588a4c2d90bb2e36b9387c9 Mon Sep 17 00:00:00 2001 From: Oscar <25309418+osw282@users.noreply.github.com> Date: Tue, 10 Feb 2026 16:43:41 +0000 Subject: [PATCH 1/8] Agent Implementation Using Pydantic AI (#149) * Move old stuff to a legacy folder and setup pre-commit for rewrite * Agent rewrite * Update cloud watch config * Update prompt and use remote github mcp server * Add cloud watch tool log * Use remote github mcp for now * Update agent * Update dev doc * Make service name a required parameter * Update cloud watch tool to search directly from log event * Cli * Readme --- .env.example | 17 + .gitignore | 184 +- .pre-commit-config.yaml | 33 +- .typos.toml | 16 - AGENTS.md | 12 + DEVELOPMENT.md | 89 +- Dockerfile | 44 + README.md | 250 +- bandit.yaml | 1 + docker-compose.yaml | 46 + .dockerignore => legacy/.dockerignore | 0 legacy/.gitignore | 255 ++ Makefile => legacy/Makefile | 0 legacy/README.md | 251 ++ .../compose.agent.yaml | 0 compose.dev.yaml => legacy/compose.dev.yaml | 0 {docs => legacy/docs}/agent-architecture.md | 0 {docs => legacy/docs}/creating-an-iam-role.md | 0 {docs => legacy/docs}/ecr-setup.md | 0 {docs => legacy/docs}/gar-setup.md | 0 .../imgs/architecture/agent-architecture.png | Bin .../github-mcp-server-client-architecture.png | Bin .../k8s-server-client-architecture.png | Bin .../slack-server-client-architecture.png | Bin .../docs}/imgs/iam/add-access-policy.png | Bin .../docs}/imgs/iam/create-user.png | Bin .../docs}/imgs/iam/iam-set-permissions.png | Bin .../docs}/imgs/iam/iam-user-details.png | Bin .../docs}/imgs/iam/iam-users-dashboard.png | Bin .../docs}/imgs/running_locally/access_key.png | Bin .../docs}/imgs/running_locally/api.png | Bin .../docs}/imgs/running_locally/config.png | Bin .../imgs/running_locally/currency_svc.png | Bin .../docs}/imgs/running_locally/first_step.png | Bin .../docs}/imgs/running_locally/github.png | Bin .../imgs/running_locally/github_setup.png | Bin .../docs}/imgs/running_locally/home.png | Bin .../docs}/imgs/running_locally/option_2.png | Bin .../running_locally/services_selection.png | Bin .../docs}/imgs/running_locally/sre-agent.png | Bin {docs => legacy/docs}/production-journey.md | 0 legacy/pyproject.toml | 128 + {sre_agent => legacy/sre_agent}/__init__.py | 0 .../sre_agent}/cli/__init__.py | 0 .../sre_agent}/cli/commands/__init__.py | 0 .../sre_agent}/cli/commands/config.py | 0 .../sre_agent}/cli/commands/diagnose.py | 0 .../sre_agent}/cli/commands/help.py | 0 .../sre_agent}/cli/interactive_shell.py | 0 {sre_agent => legacy/sre_agent}/cli/main.py | 0 .../sre_agent}/cli/utils/__init__.py | 0 .../sre_agent}/cli/utils/ascii_art.py | 0 .../sre_agent}/cli/utils/config.py | 0 .../sre_agent}/cli/utils/env_setup.py | 0 .../sre_agent}/cli/utils/paths.py | 0 .../sre_agent}/cli/utils/service_manager.py | 8 +- .../sre_agent}/client/.python-version | 0 .../sre_agent}/client/Dockerfile | 0 .../sre_agent}/client/__init__.py | 0 .../sre_agent}/client/client.py | 3 +- .../sre_agent}/client/pyproject.toml | 0 .../sre_agent}/client/utils/auth.py | 0 .../sre_agent}/client/utils/schemas.py | 1 + .../sre_agent}/compose.agent.yaml | 0 .../sre_agent}/compose.dev.yaml | 0 .../sre_agent}/firewall/.python-version | 0 .../sre_agent}/firewall/Dockerfile | 0 .../sre_agent}/firewall/__init__.py | 0 .../sre_agent}/firewall/firewall.py | 1 + .../sre_agent}/firewall/pyproject.toml | 0 .../sre_agent}/firewall/startup.sh | 0 .../sre_agent}/llm/.python-version | 0 .../sre_agent}/llm/Dockerfile | 0 {sre_agent => legacy/sre_agent}/llm/main.py | 2 +- .../sre_agent}/llm/pyproject.toml | 0 .../sre_agent}/llm/utils/adapters.py | 0 .../sre_agent}/llm/utils/clients.py | 0 .../sre_agent}/llm/utils/schemas.py | 0 .../sre_agent}/servers/.gitignore | 0 .../sre_agent}/servers/README.md | 0 .../sre_agent}/servers/github/Dockerfile | 0 .../sre_agent}/servers/github/README.md | 0 .../servers/github/common/errors.ts | 0 .../sre_agent}/servers/github/common/types.ts | 0 .../sre_agent}/servers/github/common/utils.ts | 0 .../servers/github/common/version.ts | 0 .../sre_agent}/servers/github/index.ts | 0 .../servers/github/operations/branches.ts | 0 .../servers/github/operations/commits.ts | 0 .../servers/github/operations/files.ts | 0 .../servers/github/operations/issues.ts | 0 .../servers/github/operations/pulls.ts | 0 .../servers/github/operations/repository.ts | 0 .../servers/github/operations/search.ts | 0 .../sre_agent}/servers/github/package.json | 0 .../sre_agent}/servers/github/tsconfig.json | 0 .../sre_agent}/servers/github/utils/logger.ts | 0 .../.github/workflows/cd.yml | 0 .../.github/workflows/ci.yml | 0 .../servers/mcp-server-kubernetes/.gitignore | 0 .../.vscode/extensions.json | 0 .../.vscode/settings.json | 0 .../mcp-server-kubernetes/ADVANCED_README.md | 0 .../servers/mcp-server-kubernetes/Dockerfile | 0 .../servers/mcp-server-kubernetes/LICENSE | 0 .../servers/mcp-server-kubernetes/README.md | 0 .../servers/mcp-server-kubernetes/bun.lockb | Bin .../mcp-server-kubernetes/package.json | 0 .../src/config/cleanup-config.ts | 0 .../src/config/container-templates.ts | 0 .../src/config/deployment-config.ts | 0 .../src/config/namespace-config.ts | 0 .../src/config/server-config.ts | 0 .../mcp-server-kubernetes/src/index.ts | 0 .../src/models/helm-models.ts | 0 .../src/models/kubectl-models.ts | 0 .../src/models/resource-models.ts | 0 .../src/models/response-schemas.ts | 0 .../src/models/tool-models.ts | 0 .../src/resources/handlers.ts | 0 .../src/tools/create_configmap.ts | 0 .../src/tools/create_cronjob.ts | 0 .../src/tools/create_deployment.ts | 0 .../src/tools/create_namespace.ts | 0 .../src/tools/create_pod.ts | 0 .../src/tools/create_service.ts | 0 .../src/tools/delete_configmap.ts | 0 .../src/tools/delete_cronjob.ts | 0 .../src/tools/delete_deployment.ts | 0 .../src/tools/delete_namespace.ts | 0 .../src/tools/delete_pod.ts | 0 .../src/tools/delete_service.ts | 0 .../src/tools/describe_cronjob.ts | 0 .../src/tools/describe_deployment.ts | 0 .../src/tools/describe_node.ts | 0 .../src/tools/describe_pod.ts | 0 .../src/tools/describe_service.ts | 0 .../src/tools/get_configmap.ts | 0 .../src/tools/get_current_context.ts | 0 .../src/tools/get_events.ts | 0 .../src/tools/get_job_logs.ts | 0 .../src/tools/get_logs.ts | 0 .../src/tools/helm-operations.ts | 0 .../src/tools/kubectl-operations.ts | 0 .../src/tools/list_contexts.ts | 0 .../src/tools/list_cronjobs.ts | 0 .../src/tools/list_deployments.ts | 0 .../src/tools/list_jobs.ts | 0 .../src/tools/list_nodes.ts | 0 .../src/tools/list_pods.ts | 0 .../src/tools/list_services.ts | 0 .../src/tools/port_forward.ts | 0 .../src/tools/scale_deployment.ts | 0 .../src/tools/set_current_context.ts | 0 .../src/tools/update_configmap.ts | 0 .../src/tools/update_deployment.ts | 0 .../src/tools/update_service.ts | 0 .../mcp-server-kubernetes/src/types.ts | 0 .../src/utils/kubernetes-manager.ts | 0 .../mcp-server-kubernetes/src/utils/logger.ts | 0 .../mcp-server-kubernetes/src/utils/sse.ts | 0 .../servers/mcp-server-kubernetes/startup.sh | 0 .../tests/configmap.test.ts | 0 .../tests/contexts.test.ts | 0 .../tests/cronjob.test.ts | 0 .../tests/current_context.test.ts | 0 .../mcp-server-kubernetes/tests/helm.test.ts | 0 .../tests/kubectl.test.ts | 0 .../tests/namespace.test.ts | 0 .../tests/non_destructive_tools.test.ts | 0 .../tests/port_forward.test.ts | 0 .../tests/service.test.ts | 0 .../tests/set_current_context.test.ts | 0 .../mcp-server-kubernetes/tests/sse.test.ts | 0 .../mcp-server-kubernetes/tests/unit.test.ts | 0 .../mcp-server-kubernetes/tsconfig.json | 0 .../mcp-server-kubernetes/vitest.config.ts | 0 .../servers/prompt_server/.python-version | 0 .../servers/prompt_server/Dockerfile | 0 .../servers/prompt_server/pyproject.toml | 0 .../servers/prompt_server/server.py | 0 .../servers/prompt_server/utils/schemas.py | 0 .../sre_agent}/servers/slack/Dockerfile | 0 .../sre_agent}/servers/slack/README.md | 0 .../sre_agent}/servers/slack/index.ts | 0 .../servers/slack/package-lock.json | 0 .../sre_agent}/servers/slack/package.json | 0 .../sre_agent}/servers/slack/tsconfig.json | 0 .../sre_agent}/servers/slack/utils/logger.ts | 0 .../sre_agent}/shared/__init__.py | 0 .../sre_agent}/shared/logger.py | 4 +- .../sre_agent}/shared/py.typed | 0 .../sre_agent}/shared/pyproject.toml | 0 .../sre_agent}/shared/schemas.py | 0 {sre_agent => legacy/sre_agent}/tsconfig.json | 0 {tests => legacy/tests}/__init__.py | 0 .../tests}/security_tests/test_guardrails.py | 1 - .../security_tests/test_input_validation.py | 1 - .../tests}/unit_tests/test_adapters.py | 0 legacy/uv.lock | 2325 ++++++++++++ mypy.ini | 4 + pyproject.toml | 163 +- ruff.toml | 71 + run.py | 60 + src/sre_agent/__init__.py | 15 + src/sre_agent/cli/__init__.py | 1 + src/sre_agent/cli/ascii_art.py | 17 + src/sre_agent/cli/banner.py | 48 + src/sre_agent/cli/config.py | 136 + src/sre_agent/cli/configuration.py | 262 ++ src/sre_agent/cli/interactive_shell.py | 36 + src/sre_agent/cli/main.py | 22 + src/sre_agent/cli/mode/__init__.py | 1 + src/sre_agent/cli/mode/local.py | 205 + src/sre_agent/cli/mode/paths.py | 12 + src/sre_agent/cli/mode/remote.py | 684 ++++ src/sre_agent/cli/ui.py | 5 + src/sre_agent/core/__init__.py | 15 + src/sre_agent/core/agent.py | 65 + src/sre_agent/core/api.py | 87 + src/sre_agent/core/config.py | 65 + src/sre_agent/core/deployments/__init__.py | 57 + .../core/deployments/aws_ecs/__init__.py | 59 + .../core/deployments/aws_ecs/cleanup.py | 326 ++ .../core/deployments/aws_ecs/deploy.py | 16 + src/sre_agent/core/deployments/aws_ecs/ecr.py | 20 + .../core/deployments/aws_ecs/ecs_tasks.py | 157 + src/sre_agent/core/deployments/aws_ecs/iam.py | 158 + .../core/deployments/aws_ecs/images.py | 115 + .../core/deployments/aws_ecs/models.py | 76 + .../core/deployments/aws_ecs/network.py | 142 + .../core/deployments/aws_ecs/secrets.py | 28 + .../deployments/aws_ecs/security_groups.py | 50 + .../core/deployments/aws_ecs/session.py | 32 + .../core/deployments/aws_ecs/status.py | 168 + src/sre_agent/core/interfaces.py | 54 + src/sre_agent/core/models.py | 52 + src/sre_agent/core/prompts.py | 35 + .../core/prompts/diagnosis_prompt.txt | 10 + src/sre_agent/core/prompts/system_prompt.txt | 28 + src/sre_agent/core/tools/__init__.py | 29 + src/sre_agent/core/tools/cloudwatch.py | 122 + src/sre_agent/core/tools/github.py | 29 + src/sre_agent/core/tools/slack.py | 30 + typos.toml | 22 + uv.lock | 3312 ++++++++++------- 246 files changed, 8833 insertions(+), 1940 deletions(-) create mode 100644 .env.example delete mode 100644 .typos.toml create mode 100644 AGENTS.md create mode 100644 Dockerfile create mode 100644 bandit.yaml create mode 100644 docker-compose.yaml rename .dockerignore => legacy/.dockerignore (100%) create mode 100644 legacy/.gitignore rename Makefile => legacy/Makefile (100%) create mode 100644 legacy/README.md rename compose.agent.yaml => legacy/compose.agent.yaml (100%) rename compose.dev.yaml => legacy/compose.dev.yaml (100%) rename {docs => legacy/docs}/agent-architecture.md (100%) rename {docs => legacy/docs}/creating-an-iam-role.md (100%) rename {docs => legacy/docs}/ecr-setup.md (100%) rename {docs => legacy/docs}/gar-setup.md (100%) rename {docs => legacy/docs}/imgs/architecture/agent-architecture.png (100%) rename {docs => legacy/docs}/imgs/architecture/github-mcp-server-client-architecture.png (100%) rename {docs => legacy/docs}/imgs/architecture/k8s-server-client-architecture.png (100%) rename {docs => legacy/docs}/imgs/architecture/slack-server-client-architecture.png (100%) rename {docs => legacy/docs}/imgs/iam/add-access-policy.png (100%) rename {docs => legacy/docs}/imgs/iam/create-user.png (100%) rename {docs => legacy/docs}/imgs/iam/iam-set-permissions.png (100%) rename {docs => legacy/docs}/imgs/iam/iam-user-details.png (100%) rename {docs => legacy/docs}/imgs/iam/iam-users-dashboard.png (100%) rename {docs => legacy/docs}/imgs/running_locally/access_key.png (100%) rename {docs => legacy/docs}/imgs/running_locally/api.png (100%) rename {docs => legacy/docs}/imgs/running_locally/config.png (100%) rename {docs => legacy/docs}/imgs/running_locally/currency_svc.png (100%) rename {docs => legacy/docs}/imgs/running_locally/first_step.png (100%) rename {docs => legacy/docs}/imgs/running_locally/github.png (100%) rename {docs => legacy/docs}/imgs/running_locally/github_setup.png (100%) rename {docs => legacy/docs}/imgs/running_locally/home.png (100%) rename {docs => legacy/docs}/imgs/running_locally/option_2.png (100%) rename {docs => legacy/docs}/imgs/running_locally/services_selection.png (100%) rename {docs => legacy/docs}/imgs/running_locally/sre-agent.png (100%) rename {docs => legacy/docs}/production-journey.md (100%) create mode 100644 legacy/pyproject.toml rename {sre_agent => legacy/sre_agent}/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/cli/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/cli/commands/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/cli/commands/config.py (100%) rename {sre_agent => legacy/sre_agent}/cli/commands/diagnose.py (100%) rename {sre_agent => legacy/sre_agent}/cli/commands/help.py (100%) rename {sre_agent => legacy/sre_agent}/cli/interactive_shell.py (100%) rename {sre_agent => legacy/sre_agent}/cli/main.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/ascii_art.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/config.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/env_setup.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/paths.py (100%) rename {sre_agent => legacy/sre_agent}/cli/utils/service_manager.py (97%) rename {sre_agent => legacy/sre_agent}/client/.python-version (100%) rename {sre_agent => legacy/sre_agent}/client/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/client/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/client/client.py (99%) rename {sre_agent => legacy/sre_agent}/client/pyproject.toml (100%) rename {sre_agent => legacy/sre_agent}/client/utils/auth.py (100%) rename {sre_agent => legacy/sre_agent}/client/utils/schemas.py (99%) rename {sre_agent => legacy/sre_agent}/compose.agent.yaml (100%) rename {sre_agent => legacy/sre_agent}/compose.dev.yaml (100%) rename {sre_agent => legacy/sre_agent}/firewall/.python-version (100%) rename {sre_agent => legacy/sre_agent}/firewall/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/firewall/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/firewall/firewall.py (99%) rename {sre_agent => legacy/sre_agent}/firewall/pyproject.toml (100%) rename {sre_agent => legacy/sre_agent}/firewall/startup.sh (100%) rename {sre_agent => legacy/sre_agent}/llm/.python-version (100%) rename {sre_agent => legacy/sre_agent}/llm/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/llm/main.py (98%) rename {sre_agent => legacy/sre_agent}/llm/pyproject.toml (100%) rename {sre_agent => legacy/sre_agent}/llm/utils/adapters.py (100%) rename {sre_agent => legacy/sre_agent}/llm/utils/clients.py (100%) rename {sre_agent => legacy/sre_agent}/llm/utils/schemas.py (100%) rename {sre_agent => legacy/sre_agent}/servers/.gitignore (100%) rename {sre_agent => legacy/sre_agent}/servers/README.md (100%) rename {sre_agent => legacy/sre_agent}/servers/github/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/servers/github/README.md (100%) rename {sre_agent => legacy/sre_agent}/servers/github/common/errors.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/common/types.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/common/utils.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/common/version.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/index.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/branches.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/commits.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/files.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/issues.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/pulls.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/repository.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/operations/search.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/github/package.json (100%) rename {sre_agent => legacy/sre_agent}/servers/github/tsconfig.json (100%) rename {sre_agent => legacy/sre_agent}/servers/github/utils/logger.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/.github/workflows/cd.yml (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/.github/workflows/ci.yml (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/.gitignore (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/.vscode/extensions.json (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/.vscode/settings.json (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/ADVANCED_README.md (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/LICENSE (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/README.md (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/bun.lockb (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/package.json (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/config/cleanup-config.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/config/container-templates.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/config/deployment-config.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/config/namespace-config.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/config/server-config.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/index.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/models/helm-models.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/models/kubectl-models.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/models/resource-models.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/models/response-schemas.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/models/tool-models.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/resources/handlers.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_configmap.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_cronjob.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_deployment.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_namespace.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_pod.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/create_service.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_configmap.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_cronjob.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_deployment.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_namespace.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_pod.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/delete_service.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/describe_cronjob.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/describe_deployment.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/describe_node.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/describe_pod.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/describe_service.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/get_configmap.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/get_current_context.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/get_events.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/get_job_logs.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/get_logs.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/helm-operations.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/kubectl-operations.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_contexts.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_cronjobs.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_deployments.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_jobs.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_nodes.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_pods.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/list_services.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/port_forward.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/scale_deployment.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/set_current_context.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/update_configmap.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/update_deployment.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/tools/update_service.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/types.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/utils/kubernetes-manager.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/utils/logger.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/src/utils/sse.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/startup.sh (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/configmap.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/contexts.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/cronjob.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/current_context.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/helm.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/kubectl.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/namespace.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/non_destructive_tools.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/port_forward.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/service.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/set_current_context.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/sse.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tests/unit.test.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/tsconfig.json (100%) rename {sre_agent => legacy/sre_agent}/servers/mcp-server-kubernetes/vitest.config.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/prompt_server/.python-version (100%) rename {sre_agent => legacy/sre_agent}/servers/prompt_server/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/servers/prompt_server/pyproject.toml (100%) rename {sre_agent => legacy/sre_agent}/servers/prompt_server/server.py (100%) rename {sre_agent => legacy/sre_agent}/servers/prompt_server/utils/schemas.py (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/Dockerfile (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/README.md (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/index.ts (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/package-lock.json (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/package.json (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/tsconfig.json (100%) rename {sre_agent => legacy/sre_agent}/servers/slack/utils/logger.ts (100%) rename {sre_agent => legacy/sre_agent}/shared/__init__.py (100%) rename {sre_agent => legacy/sre_agent}/shared/logger.py (93%) rename {sre_agent => legacy/sre_agent}/shared/py.typed (100%) rename {sre_agent => legacy/sre_agent}/shared/pyproject.toml (100%) rename {sre_agent => legacy/sre_agent}/shared/schemas.py (100%) rename {sre_agent => legacy/sre_agent}/tsconfig.json (100%) rename {tests => legacy/tests}/__init__.py (100%) rename {tests => legacy/tests}/security_tests/test_guardrails.py (99%) rename {tests => legacy/tests}/security_tests/test_input_validation.py (99%) rename {tests => legacy/tests}/unit_tests/test_adapters.py (100%) create mode 100644 legacy/uv.lock create mode 100644 mypy.ini create mode 100644 ruff.toml create mode 100644 run.py create mode 100644 src/sre_agent/__init__.py create mode 100644 src/sre_agent/cli/__init__.py create mode 100644 src/sre_agent/cli/ascii_art.py create mode 100644 src/sre_agent/cli/banner.py create mode 100644 src/sre_agent/cli/config.py create mode 100644 src/sre_agent/cli/configuration.py create mode 100644 src/sre_agent/cli/interactive_shell.py create mode 100644 src/sre_agent/cli/main.py create mode 100644 src/sre_agent/cli/mode/__init__.py create mode 100644 src/sre_agent/cli/mode/local.py create mode 100644 src/sre_agent/cli/mode/paths.py create mode 100644 src/sre_agent/cli/mode/remote.py create mode 100644 src/sre_agent/cli/ui.py create mode 100644 src/sre_agent/core/__init__.py create mode 100644 src/sre_agent/core/agent.py create mode 100644 src/sre_agent/core/api.py create mode 100644 src/sre_agent/core/config.py create mode 100644 src/sre_agent/core/deployments/__init__.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/__init__.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/cleanup.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/deploy.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/ecr.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/iam.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/images.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/models.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/network.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/secrets.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/security_groups.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/session.py create mode 100644 src/sre_agent/core/deployments/aws_ecs/status.py create mode 100644 src/sre_agent/core/interfaces.py create mode 100644 src/sre_agent/core/models.py create mode 100644 src/sre_agent/core/prompts.py create mode 100644 src/sre_agent/core/prompts/diagnosis_prompt.txt create mode 100644 src/sre_agent/core/prompts/system_prompt.txt create mode 100644 src/sre_agent/core/tools/__init__.py create mode 100644 src/sre_agent/core/tools/cloudwatch.py create mode 100644 src/sre_agent/core/tools/github.py create mode 100644 src/sre_agent/core/tools/slack.py create mode 100644 typos.toml diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..9f1cfed5 --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# LLM Provider +ANTHROPIC_API_KEY=sk-ant-xxxx + +# AWS (or use AWS CLI credentials) +AWS_REGION=eu-west-2 +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_SESSION_TOKEN= + +# GitHub MCP Server (Remote) +GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx +GITHUB_MCP_URL=https://api.githubcopilot.com/mcp/ + +# Slack MCP Server (runs as sidecar) +SLACK_BOT_TOKEN=xoxb-xxxx +SLACK_CHANNEL_ID=Cxxxxxxxxxx +SLACK_MCP_URL=http://localhost:13080/sse diff --git a/.gitignore b/.gitignore index 45a11fa2..15d6abce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Byte-compiled / optimized / DLL files __pycache__/ -*.py[cod] +*.py[codz] *$py.class # C extensions @@ -27,8 +27,8 @@ share/python-wheels/ MANIFEST # PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec @@ -46,7 +46,7 @@ htmlcov/ nosetests.xml coverage.xml *.cover -*.py,cover +*.py.cover .hypothesis/ .pytest_cache/ cover/ @@ -92,22 +92,37 @@ ipython_config.py # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. -#Pipfile.lock +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +# poetry.lock +# poetry.toml # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ @@ -116,11 +131,25 @@ __pypackages__/ celerybeat-schedule celerybeat.pid +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + # SageMath parsed files *.sage.py # Environments .env +.envrc .venv env/ venv/ @@ -153,103 +182,38 @@ dmypy.json cython_debug/ # PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ - -### macOS ### -# General +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml + +# .DS_Store .DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -*node_modules - -# Terraform -.terraform -*.tfstate -*.tfstate.* - -# Ignore Helm dependencies -charts/*/Chart.lock -charts/*/tmpcharts/ - -# Helm-generated files -*.tgz -Chart.lock -tmpcharts/ - -# Ignore Helm release artifacts -.release-name/ - -*values-secrets.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ebe4edb..f8442b80 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v6.0.0 hooks: - id: check-toml - id: check-yaml @@ -18,18 +18,16 @@ repos: - id: check-case-conflict - id: requirements-txt-fixer - - repo: https://github.com/psf/black - rev: 23.10.1 - hooks: - - id: black - args: [--config=pyproject.toml] - - - repo: https://github.com/charliermarsh/ruff-pre-commit + - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: "v0.1.2" + rev: v0.14.14 hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix, "--config=pyproject.toml"] # enable autofix + # Run the linter. + - id: ruff-check + args: [--fix, --config=ruff.toml] + # Run the formatter. + - id: ruff-format + args: [--config=ruff.toml] - repo: local hooks: @@ -38,22 +36,21 @@ repos: entry: mypy language: system types: [python] - args: ["--config-file=pyproject.toml", "--ignore-missing-imports"] - exclude: '.*__init__\.py|tests' + args: ["--config-file=mypy.ini"] + exclude: ^legacy/ - repo: https://github.com/crate-ci/typos - rev: v1.32.0 + rev: v1.42.1 hooks: - id: typos - args: [--config=pyproject.toml] + args: [--config=typos.toml] pass_filenames: false - repo: https://github.com/PyCQA/bandit - rev: 1.7.8 + rev: 1.9.3 hooks: - id: bandit - args: ["--config=pyproject.toml"] - additional_dependencies: ["bandit[toml]"] + args: ["-c", "bandit.yaml"] - repo: local hooks: diff --git a/.typos.toml b/.typos.toml deleted file mode 100644 index 00de7baf..00000000 --- a/.typos.toml +++ /dev/null @@ -1,16 +0,0 @@ -[default.extend-words] -"sanitized" = "sanitized" -"organization" = "organization" -"Math" = "Math" -"Initializes" = "Initializes" -"utilize" = "utilize" -"labeled" = "labeled" -"Initialized" = "Initialized" -"initialize" = "initialize" -"authorize" = "authorize" -"color" = "color" -"colors" = "colors" -"colorize" = "colorize" -"Colored" = "Colored" -"Authorization" = "Authorization" -"Artifact" = "Artifact" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..3a392b50 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,12 @@ +# AGENTS.md + +## Do +- Always use the context7 MCP server to look up up-to-date library syntax and usage. +- Keep code simple, with a strong focus on readability and maintainability. +- Use UK English. + +## Docstrings +- Keep module-level and script top-level docstrings to a single line. +- Use Google-style docstrings. +- Do not include types for arguments. +- Keep docstrings concise and only include what is necessary to help readers understand the function or class. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 387e158a..b3cfa9b2 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,53 +1,84 @@ -# Developer Readme +# DEVELOPER README -This document contains documentation intended for developers of sre-agent. +This document is for developers of sre-agent, specifically for v0.2.0. -Pre-requisites: +## To start the agent -- [Docker](https://docs.docker.com/engine/install/) - -> Note: In order for the pre-commit hooks to function properly, your Docker daemon should be running during setup. +Create a `.env` file: +```bash +cp .env.example .env +``` -## Developer environment setup +Add your AWS credentials and Anthropic API key. -To work on the sre-agent as a developer, you'll need to configure your local development environment. You can do this by simply running: +Start the agent server and the Slack MCP server: ```bash -make project-setup +docker compose up -d ``` -This will install Python `3.12` using PyEnv, create a virtual environment using uv, and install the pre-commit hooks. -> Note: The `project-setup` process will check whether `pre-commits`, and `uv` are installed. If not, it will ask to install them on your behalf as they're required to use this template. +Trigger an error on the [store](http://aea33d77009704f67b39fe82a5c41aab-398063840.eu-west-2.elb.amazonaws.com/) by adding loaf to the cart, or change the currency from EUR to GBP. (Note, there is an bug that errors might take some time to be indexed so if you trigger the agent immediately after you cause an error it might not be able to find the log.) +Trigger the locally running agent: +```bash +uv run python run.py /aws/containerinsights/no-loafers-for-you/application cartservice +``` -A Makefile is just a usual text file to define a set of rules or instructions to run which can be run using the `make` command. To see the available make commands: +Or: ```bash -make help +uv run python run.py /aws/containerinsights/no-loafers-for-you/application currencyservice ``` -## Changes to the cli +## Adding a New Tool -If you’ve made updates to the CLI code, you can install it locally by running: +When adding a new tool/integration, follow one of these patterns: -```bash -source .venv/bin/activate && pip install -e . -``` +### Option 1: MCP Server -Then test your changes by starting the CLI with: +If an MCP server exists for the service, you can use that. No interface implementation is needed. -```bash -sre-agent +```python +# tools/example.py +from pydantic_ai.mcp import MCPServerStdio +from sre_agent.config import AgentConfig + +def create_example_mcp_toolset(config: AgentConfig) -> MCPServerStdio: + return MCPServerStdio( + "docker", + args=["run", "-i", "--rm", "-e", f"TOKEN={config.example.token}", "mcp/example"], + timeout=30, + ) ``` -## Testing +**Examples:** `github.py`, `slack.py` -With the uv shell active (see above), you can run all the tests using: +### Option 2: Direct API -```bash -make tests -``` +Use this when no MCP server is available. You must implement the relevant interface. -Or specific tests: +```python +# tools/example.py +from sre_agent.interfaces import LoggingInterface +from sre_agent.models import LogQueryResult -```bash -python -m pytest tests/test_dummy.py +class ExampleLogging(LoggingInterface): + async def query_errors( + self, + source: str, + service_name: str, + time_range_minutes: int = 10, + ) -> LogQueryResult: + # Implementation using direct API calls + ... + +def create_example_toolset(config: AgentConfig) -> FunctionToolset: + toolset = FunctionToolset() + impl = ExampleLogging(config.example.api_key) + + @toolset.tool + async def search_logs(...) -> LogQueryResult: + return await impl.query_errors(...) + + return toolset ``` + +**Examples:** `cloudwatch.py` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..10a0da35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +# SRE Agent Container +# Multi-stage build for smaller image size + +FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS builder + +WORKDIR /app + +# Copy dependency files +COPY pyproject.toml uv.lock README.md ./ + +# Install dependencies (without the project itself) +RUN uv sync --frozen --no-install-project + +# Copy source code +COPY src/ src/ + +# Install the project +RUN uv sync --frozen + + +FROM python:3.13-slim-bookworm AS runtime + +WORKDIR /app + +# Copy the virtual environment from builder +COPY --from=builder /app/.venv /app/.venv + +# Copy source code (needed for prompt files) +COPY --from=builder /app/src /app/src + +# Set environment variables +ENV PATH="/app/.venv/bin:$PATH" +ENV PYTHONUNBUFFERED=1 +ENV PYTHONDONTWRITEBYTECODE=1 + +# Expose the API port +EXPOSE 8000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 + +# Run the FastAPI app +CMD ["uvicorn", "sre_agent.api:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index a726e458..9f3fa722 100644 --- a/README.md +++ b/README.md @@ -1,251 +1,9 @@ -

+

🚀 Site Reliability Engineer (SRE) Agent :detective:

-Welcome to the **SRE Agent** project! This open-source AI agent helps you debug, keep your systems on Kubernetes healthy, and make your DevOps life easier. +WIP. -Now powered by a **command-line interface (CLI)**, you can interact directly with the agent from your terminal. Plug in your Kubernetes cluster, GitHub repo, and let the agent handle the heavy lifting, diagnosing, reporting, and keeping your team in the loop. +To run the agent locally, use the CLI and follow the prompts: `uv run sre-agent`. -## 🌟 What is SRE Agent? - -SRE Agent is your AI-powered teammate for monitoring application and infrastructure logs, diagnosing issues, and reporting diagnostics after errors. With the new CLI, it’s easier than ever to connect the agent into your stack and start focusing on building instead of firefighting. - -![cli_home](docs/imgs/running_locally/home.png) - -## 🤔 Why Did We Build This? - -We wanted to learn best practices, costs, security, and performance tips for AI agents in production. Our journey is open-source, check out our [Production Journey Page](/docs/production-journey.md) and [Agent Architecture Page](/docs/agent-architecture.md) for the full story. - -We've been writing blogs and sharing our learnings along the way. Check out our [blog](https://www.fuzzylabs.ai/blog) for insights and updates. - -> **Contributions welcome!** [Join us](CONTRIBUTING.md) and help shape the future of AI-powered SRE. - -## ✨ Features - -- 🕵️‍♂️ **Root Cause Debugging** – Finds the real reason behind app and system errors -- 📜 **Kubernetes Logs** – Queries your cluster for logs and info -- 🔍 **GitHub Search** – Digs through your codebase for bugs -- 🚦 **CLI Powered** – Interact with the agent directly from your terminal, with guided setup and zero manual image building required. Run diagnostics, manage integrations, and get insights without leaving the CLI. - -> Powered by the [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol) for seamless LLM-to-tool connectivity. - -## 🤖 Supported LLM Providers - -The SRE Agent currently supports: - -### Anthropic -- **Models**: e.g. "claude-4-0-sonnet-latest" -- **Setup**: Requires `ANTHROPIC_API_KEY` - - -## 🛠️ Prerequisites - -- Python 3.12 or higher -- An app deployed on AWS EKS (Elastic Kubernetes Service) -- Anthropic API key - -## ⚡ Quick Start (5 minutes) - -### 1️⃣ Install the SRE Agent CLI -```bash -pip install sre-agent -``` - -### 2️⃣ Start the agent -```bash -sre-agent -``` - -This is what you’ll see when the agent starts up for the first time. - -![sre-agnet](docs/imgs/running_locally/sre-agent.png) - -### 3️⃣ Follow the guided setup to finish configuring the agent - -#### Step 1️⃣: AWS - -The first step is setting up your AWS credentials so the agent can access the cluster where your app is deployed. - -![first-step](docs/imgs/running_locally/first_step.png) - -From your AWS portal, click **Access keys**: - -![access-key](docs/imgs/running_locally/access_key.png) - -Copy the credentials shown under **Option 2** and paste them into the CLI. - -⚠️ Note: After pasting your credentials, you’ll need to press **Enter twice** to confirm. - -![option-2](docs/imgs/running_locally/option_2.png) - -Next, provide your **cluster name**. This should be the cluster where your app is deployed and where you want to monitor your deployments. - -Once entered, the agent will automatically test the connection to the cluster using the credentials you provided. - -Select the specific services you want to monitor by using a list such as [2,6,7] or all of them if you would like, - -![services_selection](docs/imgs/running_locally/services_selection.png) - -#### Step 2️⃣: GitHub Integration - -We will need to configure github access with a pat token so that the agent can read your repository and look at the code to find out what's causing the error. - -Follow the guided step, this should be straight forward: - -![github](docs/imgs/running_locally/github_setup.png) - -### 3️⃣ Follow the guided setup to finish configuring the agent - -#### Step 1️⃣: AWS Setup - -Start by configuring your AWS credentials so the agent can access the cluster where your app is deployed. - -![first-step](docs/imgs/running_locally/first_step.png) - -From your AWS portal, click **Access keys**: - -![access-key](docs/imgs/running_locally/access_key.png) - -Copy the credentials shown under **Option 2** and paste them into the CLI. - -⚠️ Note: After pasting your credentials, press **Enter twice** to confirm. - -![option-2](docs/imgs/running_locally/option_2.png) - -Next, enter your **cluster name**. This should be the cluster where your app is deployed and where you want to monitor your deployments. - -The agent will then test the connection to the cluster using the credentials you provided. - -After that, select the specific services you want to monitor. You can choose by index (for example, `[2,6,7]`) or select all of them. - -![services_selection](docs/imgs/running_locally/services_selection.png) - ---- - -#### Step 2️⃣: GitHub Integration - -Next, configure GitHub access using a Personal Access Token (PAT). This allows the agent to read your repository and inspect the code when diagnosing issues. - -Follow the guided step in the CLI—it’s straightforward: - -![github](docs/imgs/running_locally/github_setup.png) - ---- - -#### Step 3️⃣: Anthropic API Key - -Finally, provide your **Anthropic API key**, which will be used as the model provider powering the agent. - -![api](docs/imgs/running_locally/api.png) - -### 4️⃣ Start diagnosing issues - -You’re now inside the `sre-agent` CLI and ready to run diagnostics. - -For example, if your cluster has a service named `currencyservice`, you can run: - -```bash -diagnose currencyservice -``` - -![currency_svc](docs/imgs/running_locally/currency_svc.png) - -When the diagnosing is completed, you should see the result inside the cli. - -To exit the agent, just run the `exit` command. - -## ⚙️ Configuration & Add-Ons - -You can use the `config` command to set up options such as the cluster name, GitHub settings, and model providers. It also lets you enable additional add-ons, like sending diagnostic results to Slack or activating the Llama Firewall. - -![config](docs/imgs/running_locally/config.png) - -## 🔧 For Developers - -
-📦 Development Workflow - -### Project Structure -This is a uv workspace with multiple Python services and TypeScript MCP servers: -- `sre_agent/client/`: FastAPI orchestrator (Python) -- `sre_agent/llm/`: LLM service with multi-provider support (Python) -- `sre_agent/firewall/`: Llama Prompt Guard security layer (Python) -- `sre_agent/servers/mcp-server-kubernetes/`: Kubernetes operations (TypeScript) -- `sre_agent/servers/github/`: GitHub API integration (TypeScript) -- `sre_agent/servers/slack/`: Slack notifications (TypeScript) -- `sre_agent/servers/prompt_server/`: Structured prompts (Python) -- `sre_agent/cli/`: The Python CLI that powers the agent (Python) - -### Development Commands -```bash -make project-setup # Install uv, create venv, install pre-commit hooks -make check # Run linting, pre-commit hooks, and lock file check -make tests # Run pytest with coverage -make license-check # Verify dependency licenses -``` - -### TypeScript MCP Servers -```bash -# Kubernetes MCP server -cd sre_agent/servers/mcp-server-kubernetes -npm run build && npm run test - -# GitHub/Slack MCP servers -cd sre_agent/servers/github # or /slack -npm run build && npm run watch -``` - -### The CLI - -At a high level, there are two main parts you can work on: -- The **CLI**, which you can think of as the “front end.” -- The **agents/MCP servers**, which run in the background. - -If you want to work on the CLI, you can install and run it locally with: - -```bash -source .venv/bin/activate && pip install -e . -``` - -### Agents/MCP Servers - -If you’re working on the MCP servers, you’ll need to rebuild the Docker images for any server you modify. - -We provide two Compose files: - -- [compose.agent.yaml](compose.agent.yaml): uses images hosted on GHCR - -- [compose.dev.yaml](compose.dev.yaml): uses images built locally on your machine - -To test local changes, start the sre-agent with the --dev flag: - -```bash -sre-agent --dev -``` - -This will start the agent using the [compose.dev.yaml](compose.dev.yaml) file. - -
- -## 📚 Documentation - -Find all the docs you need in the [docs](docs) folder: - -- [Creating an IAM Role](docs/creating-an-iam-role.md) -- [ECR Setup Steps](docs/ecr-setup.md) -- [Agent Architecture](docs/agent-architecture.md) -- [Production Journey](docs/production-journey.md) - -## 🙏 Acknowledgements & Attribution - -Big thanks to: - -- [Suyog Sonwalkar](https://github.com/Flux159) for the [Kubernetes MCP server](/sre_agent/servers/mcp-server-kubernetes/) -- [Anthropic's Model Context Protocol team](https://github.com/modelcontextprotocol) for the [Slack](/sre_agent/servers/slack/) and [GitHub](/sre_agent/servers/github/) MCP servers - -## :book: Blogs - -Check out our blog posts for insights and updates: - -- [Bringing Agentic AI into the Real World](https://www.fuzzylabs.ai/blog-post/bringing-agentic-ai-into-the-real-world) -- [How We're Building an Autonomous SRE with FastMCP](https://www.fuzzylabs.ai/blog-post/how-were-building-an-autonomous-sre-with-fastmcp) +For direct local diagnostics without the CLI, start the Slack MCP container with `docker compose up -d slack`, then invoke `run.py` with a log group, service name, and time window in minutes, for example: `uv run python run.py /aws/containerinsights/no-loafers-for-you/application currencyservice 10`. diff --git a/bandit.yaml b/bandit.yaml new file mode 100644 index 00000000..b6180fd4 --- /dev/null +++ b/bandit.yaml @@ -0,0 +1 @@ +exclude_dirs: ['legacy'] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..9839e64f --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,46 @@ +# Docker Compose for SRE Agent and MCP server sidecars. +# +# Start with: docker compose up -d +# Logs: docker compose logs -f sre-agent + +name: sre-agent + +services: + # SRE Agent (FastAPI) + sre-agent: + build: + context: . + dockerfile: Dockerfile + ports: + - "8000:8000" + environment: + # LLM Provider + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - MODEL=${MODEL:-claude-sonnet-4-5-20250929} + # AWS + - AWS_REGION=${AWS_REGION} + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + - AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN} + # Slack MCP (SSE sidecar) + - SLACK_CHANNEL_ID=${SLACK_CHANNEL_ID} + - SLACK_MCP_URL=http://slack:13080/sse + # GitHub MCP (Remote) + - GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN} + - GITHUB_MCP_URL=https://api.githubcopilot.com/mcp/ + depends_on: + slack: + condition: service_started + restart: unless-stopped + + # Slack MCP Server (korotovsky/slack-mcp-server) + slack: + image: ghcr.io/korotovsky/slack-mcp-server:latest + environment: + - SLACK_MCP_XOXB_TOKEN=${SLACK_BOT_TOKEN} + - SLACK_MCP_ADD_MESSAGE_TOOL=${SLACK_CHANNEL_ID} + - SLACK_MCP_HOST=0.0.0.0 + - SLACK_MCP_PORT=13080 + restart: unless-stopped + ports: + - "13080:13080" diff --git a/.dockerignore b/legacy/.dockerignore similarity index 100% rename from .dockerignore rename to legacy/.dockerignore diff --git a/legacy/.gitignore b/legacy/.gitignore new file mode 100644 index 00000000..45a11fa2 --- /dev/null +++ b/legacy/.gitignore @@ -0,0 +1,255 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +*node_modules + +# Terraform +.terraform +*.tfstate +*.tfstate.* + +# Ignore Helm dependencies +charts/*/Chart.lock +charts/*/tmpcharts/ + +# Helm-generated files +*.tgz +Chart.lock +tmpcharts/ + +# Ignore Helm release artifacts +.release-name/ + +*values-secrets.yaml diff --git a/Makefile b/legacy/Makefile similarity index 100% rename from Makefile rename to legacy/Makefile diff --git a/legacy/README.md b/legacy/README.md new file mode 100644 index 00000000..1ecd466d --- /dev/null +++ b/legacy/README.md @@ -0,0 +1,251 @@ +

+ 🚀 Site Reliability Engineer (SRE) Agent :detective: +

+ +Welcome to the **SRE Agent** project! This open-source AI agent helps you debug, keep your systems on Kubernetes healthy, and make your DevOps life easier. + +Now powered by a **command-line interface (CLI)**, you can interact directly with the agent from your terminal. Plug in your Kubernetes cluster, GitHub repo, and let the agent handle the heavy lifting, diagnosing, reporting, and keeping your team in the loop. + +## 🌟 What is SRE Agent? + +SRE Agent is your AI-powered teammate for monitoring application and infrastructure logs, diagnosing issues, and reporting diagnostics after errors. With the new CLI, it’s easier than ever to connect the agent into your stack and start focusing on building instead of firefighting. + +![cli_home](docs/imgs/running_locally/home.png) + +## 🤔 Why Did We Build This? + +We wanted to learn best practices, costs, security, and performance tips for AI agents in production. Our journey is open-source, check out our [Production Journey Page](/docs/production-journey.md) and [Agent Architecture Page](/docs/agent-architecture.md) for the full story. + +We've been writing blogs and sharing our learnings along the way. Check out our [blog](https://www.fuzzylabs.ai/blog) for insights and updates. + +> **Contributions welcome!** [Join us](CONTRIBUTING.md) and help shape the future of AI-powered SRE. + +## ✨ Features + +- 🕵️‍♂️ **Root Cause Debugging** – Finds the real reason behind app and system errors +- 📜 **Kubernetes Logs** – Queries your cluster for logs and info +- 🔍 **GitHub Search** – Digs through your codebase for bugs +- 🚦 **CLI Powered** – Interact with the agent directly from your terminal, with guided setup and zero manual image building required. Run diagnostics, manage integrations, and get insights without leaving the CLI. + +> Powered by the [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol) for seamless LLM-to-tool connectivity. + +## 🤖 Supported LLM Providers + +The SRE Agent currently supports: + +### Anthropic +- **Models**: e.g. "claude-4-0-sonnet-latest" +- **Setup**: Requires `ANTHROPIC_API_KEY` + + +## 🛠️ Prerequisites + +- Python 3.12 or higher +- An app deployed on AWS EKS (Elastic Kubernetes Service) +- Anthropic API key + +## ⚡ Quick Start (5 minutes) + +### 1️⃣ Install the SRE Agent CLI +```bash +pip install sre-agent +``` + +### 2️⃣ Start the agent +```bash +sre-agent +``` + +This is what you’ll see when the agent starts up for the first time. + +![sre-agnet](docs/imgs/running_locally/sre-agent.png) + +### 3️⃣ Follow the guided setup to finish configuring the agent + +#### Step 1️⃣: AWS + +The first step is setting up your AWS credentials so the agent can access the cluster where your app is deployed. + +![first-step](docs/imgs/running_locally/first_step.png) + +From your AWS portal, click **Access keys**: + +![access-key](docs/imgs/running_locally/access_key.png) + +Copy the credentials shown under **Option 2** and paste them into the CLI. + +⚠️ Note: After pasting your credentials, you’ll need to press **Enter twice** to confirm. + +![option-2](docs/imgs/running_locally/option_2.png) + +Next, provide your **cluster name**. This should be the cluster where your app is deployed and where you want to monitor your deployments. + +Once entered, the agent will automatically test the connection to the cluster using the credentials you provided. + +Select the specific services you want to monitor by using a list such as [2,6,7] or all of them if you would like, + +![services_selection](docs/imgs/running_locally/services_selection.png) + +#### Step 2️⃣: GitHub Integration + +We will need to configure github access with a pat token so that the agent can read your repository and look at the code to find out what's causing the error. + +Follow the guided step, this should be straight forward: + +![github](docs/imgs/running_locally/github_setup.png) + +### 3️⃣ Follow the guided setup to finish configuring the agent + +#### Step 1️⃣: AWS Setup + +Start by configuring your AWS credentials so the agent can access the cluster where your app is deployed. + +![first-step](docs/imgs/running_locally/first_step.png) + +From your AWS portal, click **Access keys**: + +![access-key](docs/imgs/running_locally/access_key.png) + +Copy the credentials shown under **Option 2** and paste them into the CLI. + +⚠️ Note: After pasting your credentials, press **Enter twice** to confirm. + +![option-2](docs/imgs/running_locally/option_2.png) + +Next, enter your **cluster name**. This should be the cluster where your app is deployed and where you want to monitor your deployments. + +The agent will then test the connection to the cluster using the credentials you provided. + +After that, select the specific services you want to monitor. You can choose by index (for example, `[2,6,7]`) or select all of them. + +![services_selection](docs/imgs/running_locally/services_selection.png) + +--- + +#### Step 2️⃣: GitHub Integration + +Next, configure GitHub access using a Personal Access Token (PAT). This allows the agent to read your repository and inspect the code when diagnosing issues. + +Follow the guided step in the CLI—it’s straightforward: + +![github](docs/imgs/running_locally/github_setup.png) + +--- + +#### Step 3️⃣: Anthropic API Key + +Finally, provide your **Anthropic API key**, which will be used as the model provider powering the agent. + +![api](docs/imgs/running_locally/api.png) + +### 4️⃣ Start diagnosing issues + +You’re now inside the `sre-agent` CLI and ready to run diagnostics. + +For example, if your cluster has a service named `currencyservice`, you can run: + +```bash +diagnose currencyservice +``` + +![currency_svc](docs/imgs/running_locally/currency_svc.png) + +When the diagnosing is completed, you should see the result inside the cli. + +To exit the agent, just run the `exit` command. + +## ⚙️ Configuration & Add-Ons + +You can use the `config` command to set up options such as the cluster name, GitHub settings, and model providers. It also lets you enable additional add-ons, like sending diagnostic results to Slack or activating the Llama Firewall. + +![config](docs/imgs/running_locally/config.png) + +## 🔧 For Developers + +
+📦 Development Workflow + +### Project Structure +This is a uv workspace with multiple Python services and TypeScript MCP servers: +- `sre_agent/client/`: FastAPI orchestrator (Python) +- `sre_agent/llm/`: LLM service with multi-provider support (Python) +- `sre_agent/firewall/`: Llama Prompt Guard security layer (Python) +- `sre_agent/servers/mcp-server-kubernetes/`: Kubernetes operations (TypeScript) +- `sre_agent/servers/github/`: GitHub API integration (TypeScript) +- `sre_agent/servers/slack/`: Slack notifications (TypeScript) +- `sre_agent/servers/prompt_server/`: Structured prompts (Python) +- `sre_agent/cli/`: The Python CLI that powers the agent (Python) + +### Development Commands +```bash +make project-setup # Install uv, create venv, install pre-commit hooks +make check # Run linting, pre-commit hooks, and lock file check +make tests # Run pytest with coverage +make license-check # Verify dependency licenses +``` + +### TypeScript MCP Servers +```bash +# Kubernetes MCP server +cd sre_agent/servers/mcp-server-kubernetes +npm run build && npm run test + +# GitHub/Slack MCP servers +cd sre_agent/servers/github # or /slack +npm run build && npm run watch +``` + +### The CLI + +At a high level, there are two main parts you can work on: +- The **CLI**, which you can think of as the “front end.” +- The **agents/MCP servers**, which run in the background. + +If you want to work on the CLI, you can install and run it locally with: + +```bash +source .venv/bin/activate && pip install -e . +``` + +### Agents/MCP Servers + +If you’re working on the MCP servers, you’ll need to rebuild the Docker images for any server you modify. + +We provide two Compose files: + +- [compose.agent.yaml](compose.agent.yaml): uses images hosted on GHCR + +- [compose.dev.yaml](compose.dev.yaml): uses images built locally on your machine + +To test local changes, start the sre-agent with the --dev flag: + +```bash +sre-agent --dev +``` + +This will start the agent using the [compose.dev.yaml](compose.dev.yaml) file. + +
+ +## 📚 Documentation + +Find all the docs you need in the [docs](docs) folder: + +- [Creating an IAM Role](docs/creating-an-iam-role.md) +- [ECR Setup Steps](docs/ecr-setup.md) +- [Agent Architecture](docs/agent-architecture.md) +- [Production Journey](docs/production-journey.md) + +## 🙏 Acknowledgements & Attribution + +Big thanks to: + +- [Suyog Sonwalkar](https://github.com/Flux159) for the [Kubernetes MCP server](/sre_agent/servers/mcp-server-kubernetes/) +- [Anthropic's Model Context Protocol team](https://github.com/modelcontextprotocol) for the [Slack](/sre_agent/servers/slack/) and [GitHub](/sre_agent/servers/github/) MCP servers + +## :book: Blogs + +Check out our blog posts for insights and updates: + +- [Bringing Agentic AI into the Real World](https://www.fuzzylabs.ai/blog-post/bringing-agentic-ai-into-the-real-world) +- [How We're Building an Autonomous SRE with FastMCP](https://www.fuzzylabs.ai/blog-post/how-were-building-an-autonomous-sre-with-fastmcp) diff --git a/compose.agent.yaml b/legacy/compose.agent.yaml similarity index 100% rename from compose.agent.yaml rename to legacy/compose.agent.yaml diff --git a/compose.dev.yaml b/legacy/compose.dev.yaml similarity index 100% rename from compose.dev.yaml rename to legacy/compose.dev.yaml diff --git a/docs/agent-architecture.md b/legacy/docs/agent-architecture.md similarity index 100% rename from docs/agent-architecture.md rename to legacy/docs/agent-architecture.md diff --git a/docs/creating-an-iam-role.md b/legacy/docs/creating-an-iam-role.md similarity index 100% rename from docs/creating-an-iam-role.md rename to legacy/docs/creating-an-iam-role.md diff --git a/docs/ecr-setup.md b/legacy/docs/ecr-setup.md similarity index 100% rename from docs/ecr-setup.md rename to legacy/docs/ecr-setup.md diff --git a/docs/gar-setup.md b/legacy/docs/gar-setup.md similarity index 100% rename from docs/gar-setup.md rename to legacy/docs/gar-setup.md diff --git a/docs/imgs/architecture/agent-architecture.png b/legacy/docs/imgs/architecture/agent-architecture.png similarity index 100% rename from docs/imgs/architecture/agent-architecture.png rename to legacy/docs/imgs/architecture/agent-architecture.png diff --git a/docs/imgs/architecture/github-mcp-server-client-architecture.png b/legacy/docs/imgs/architecture/github-mcp-server-client-architecture.png similarity index 100% rename from docs/imgs/architecture/github-mcp-server-client-architecture.png rename to legacy/docs/imgs/architecture/github-mcp-server-client-architecture.png diff --git a/docs/imgs/architecture/k8s-server-client-architecture.png b/legacy/docs/imgs/architecture/k8s-server-client-architecture.png similarity index 100% rename from docs/imgs/architecture/k8s-server-client-architecture.png rename to legacy/docs/imgs/architecture/k8s-server-client-architecture.png diff --git a/docs/imgs/architecture/slack-server-client-architecture.png b/legacy/docs/imgs/architecture/slack-server-client-architecture.png similarity index 100% rename from docs/imgs/architecture/slack-server-client-architecture.png rename to legacy/docs/imgs/architecture/slack-server-client-architecture.png diff --git a/docs/imgs/iam/add-access-policy.png b/legacy/docs/imgs/iam/add-access-policy.png similarity index 100% rename from docs/imgs/iam/add-access-policy.png rename to legacy/docs/imgs/iam/add-access-policy.png diff --git a/docs/imgs/iam/create-user.png b/legacy/docs/imgs/iam/create-user.png similarity index 100% rename from docs/imgs/iam/create-user.png rename to legacy/docs/imgs/iam/create-user.png diff --git a/docs/imgs/iam/iam-set-permissions.png b/legacy/docs/imgs/iam/iam-set-permissions.png similarity index 100% rename from docs/imgs/iam/iam-set-permissions.png rename to legacy/docs/imgs/iam/iam-set-permissions.png diff --git a/docs/imgs/iam/iam-user-details.png b/legacy/docs/imgs/iam/iam-user-details.png similarity index 100% rename from docs/imgs/iam/iam-user-details.png rename to legacy/docs/imgs/iam/iam-user-details.png diff --git a/docs/imgs/iam/iam-users-dashboard.png b/legacy/docs/imgs/iam/iam-users-dashboard.png similarity index 100% rename from docs/imgs/iam/iam-users-dashboard.png rename to legacy/docs/imgs/iam/iam-users-dashboard.png diff --git a/docs/imgs/running_locally/access_key.png b/legacy/docs/imgs/running_locally/access_key.png similarity index 100% rename from docs/imgs/running_locally/access_key.png rename to legacy/docs/imgs/running_locally/access_key.png diff --git a/docs/imgs/running_locally/api.png b/legacy/docs/imgs/running_locally/api.png similarity index 100% rename from docs/imgs/running_locally/api.png rename to legacy/docs/imgs/running_locally/api.png diff --git a/docs/imgs/running_locally/config.png b/legacy/docs/imgs/running_locally/config.png similarity index 100% rename from docs/imgs/running_locally/config.png rename to legacy/docs/imgs/running_locally/config.png diff --git a/docs/imgs/running_locally/currency_svc.png b/legacy/docs/imgs/running_locally/currency_svc.png similarity index 100% rename from docs/imgs/running_locally/currency_svc.png rename to legacy/docs/imgs/running_locally/currency_svc.png diff --git a/docs/imgs/running_locally/first_step.png b/legacy/docs/imgs/running_locally/first_step.png similarity index 100% rename from docs/imgs/running_locally/first_step.png rename to legacy/docs/imgs/running_locally/first_step.png diff --git a/docs/imgs/running_locally/github.png b/legacy/docs/imgs/running_locally/github.png similarity index 100% rename from docs/imgs/running_locally/github.png rename to legacy/docs/imgs/running_locally/github.png diff --git a/docs/imgs/running_locally/github_setup.png b/legacy/docs/imgs/running_locally/github_setup.png similarity index 100% rename from docs/imgs/running_locally/github_setup.png rename to legacy/docs/imgs/running_locally/github_setup.png diff --git a/docs/imgs/running_locally/home.png b/legacy/docs/imgs/running_locally/home.png similarity index 100% rename from docs/imgs/running_locally/home.png rename to legacy/docs/imgs/running_locally/home.png diff --git a/docs/imgs/running_locally/option_2.png b/legacy/docs/imgs/running_locally/option_2.png similarity index 100% rename from docs/imgs/running_locally/option_2.png rename to legacy/docs/imgs/running_locally/option_2.png diff --git a/docs/imgs/running_locally/services_selection.png b/legacy/docs/imgs/running_locally/services_selection.png similarity index 100% rename from docs/imgs/running_locally/services_selection.png rename to legacy/docs/imgs/running_locally/services_selection.png diff --git a/docs/imgs/running_locally/sre-agent.png b/legacy/docs/imgs/running_locally/sre-agent.png similarity index 100% rename from docs/imgs/running_locally/sre-agent.png rename to legacy/docs/imgs/running_locally/sre-agent.png diff --git a/docs/production-journey.md b/legacy/docs/production-journey.md similarity index 100% rename from docs/production-journey.md rename to legacy/docs/production-journey.md diff --git a/legacy/pyproject.toml b/legacy/pyproject.toml new file mode 100644 index 00000000..ecb0295e --- /dev/null +++ b/legacy/pyproject.toml @@ -0,0 +1,128 @@ +[project] +name = "sre-agent" +version = "0.1.0" +description = "A Site Reliability Engineer AI agent that can monitor application and infrastructure logs, diagnose issues, and report on diagnostics." +authors = [{ name = "Fuzzy Labs", email = "info@fuzzylabs.ai" }] +readme = "README.md" +requires-python = ">=3.12,<4.0" +license = { text = "MIT" } +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", +] +dependencies = [ +] + +[project.scripts] +sre-agent = "sre_agent.cli.main:main" + +[tool.pytest.ini_options] +addopts = "--cov=sre_agent --cov-report term-missing" +testpaths = ["tests"] + +# mypy configuration +[tool.mypy] +show_error_codes = true +exclude = ["docs", "tests", "LICENSE"] +strict = true +namespace_packages = true + +# black configuration +[tool.black] +line-length = 100 +include = '\.pyi?$' +exclude = ''' +/( + \.git +| \.hg +| \.mypy_cache +| \.tox +| \.venv +| _build +| buck-out +| build +)/ +''' + +[tool.ruff] +target-version = "py312" + + +# Match black. Note that this also checks comment line length, but black does not format comments. +line-length = 100 + +show-fixes = true + +[tool.ruff.lint] +ignore-init-module-imports = true +select = [ + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "Q", # flake8-quotes + "ISC", # flake8-implicit-str-concat + "F", # pyflakes + "D", # pydocstyle + "E", # pycodestyle error + "W", # pycodestyle warning + "N", # pep8-naming + "I", # isort + "PL", # pylint rules from categories "Convention", "Error", and "Warning" + "PLE", # ruff currently implements only a subset of pylint's rules + "PLW", # pylint warning + "PLR", # pylint refactor + "UP", # pyupgrade + "C", # Complexity (mccabe+) & comprehensions +] +ignore = [ + "UP006", # See https://github.com/bokeh/bokeh/issues/13143 + "UP007", # See https://github.com/bokeh/bokeh/pull/13144 +] + +[tool.ruff.lint.pydocstyle] +# Use Google-style docstrings. +convention = "google" + +[tool.ruff.lint.mccabe] +# Flag errors (`C901`) whenever the complexity level exceeds 10. +max-complexity = 10 + + +# typos configuration +[tool.typos.files] +extend-exclude = [ + ".gitignore", + "LICENSE", + ".*", + "*servers*", + "*values-secrets.yaml", +] + +[tool.typos.default.extend-words] +center = "center" +Initialize = "Initialize" +initialize = "initialize" +Initialized = "Initialized" +Authorization = "Authorization" +EC = "EC" + +[tool.typos.default] +locale = "en-gb" + +# Bandit configuration +[tool.bandit] +exclude_dirs = [] +skips = ["B104"] + +[tool.bandit.assert_used] +skips = ['*test.py', '*/test_*.py'] + +[tool.uv.workspace] +members = [ + "sre_agent/llm", + "sre_agent/client", + "sre_agent/servers/prompt_server", + "sre_agent/firewall", +] diff --git a/sre_agent/__init__.py b/legacy/sre_agent/__init__.py similarity index 100% rename from sre_agent/__init__.py rename to legacy/sre_agent/__init__.py diff --git a/sre_agent/cli/__init__.py b/legacy/sre_agent/cli/__init__.py similarity index 100% rename from sre_agent/cli/__init__.py rename to legacy/sre_agent/cli/__init__.py diff --git a/sre_agent/cli/commands/__init__.py b/legacy/sre_agent/cli/commands/__init__.py similarity index 100% rename from sre_agent/cli/commands/__init__.py rename to legacy/sre_agent/cli/commands/__init__.py diff --git a/sre_agent/cli/commands/config.py b/legacy/sre_agent/cli/commands/config.py similarity index 100% rename from sre_agent/cli/commands/config.py rename to legacy/sre_agent/cli/commands/config.py diff --git a/sre_agent/cli/commands/diagnose.py b/legacy/sre_agent/cli/commands/diagnose.py similarity index 100% rename from sre_agent/cli/commands/diagnose.py rename to legacy/sre_agent/cli/commands/diagnose.py diff --git a/sre_agent/cli/commands/help.py b/legacy/sre_agent/cli/commands/help.py similarity index 100% rename from sre_agent/cli/commands/help.py rename to legacy/sre_agent/cli/commands/help.py diff --git a/sre_agent/cli/interactive_shell.py b/legacy/sre_agent/cli/interactive_shell.py similarity index 100% rename from sre_agent/cli/interactive_shell.py rename to legacy/sre_agent/cli/interactive_shell.py diff --git a/sre_agent/cli/main.py b/legacy/sre_agent/cli/main.py similarity index 100% rename from sre_agent/cli/main.py rename to legacy/sre_agent/cli/main.py diff --git a/sre_agent/cli/utils/__init__.py b/legacy/sre_agent/cli/utils/__init__.py similarity index 100% rename from sre_agent/cli/utils/__init__.py rename to legacy/sre_agent/cli/utils/__init__.py diff --git a/sre_agent/cli/utils/ascii_art.py b/legacy/sre_agent/cli/utils/ascii_art.py similarity index 100% rename from sre_agent/cli/utils/ascii_art.py rename to legacy/sre_agent/cli/utils/ascii_art.py diff --git a/sre_agent/cli/utils/config.py b/legacy/sre_agent/cli/utils/config.py similarity index 100% rename from sre_agent/cli/utils/config.py rename to legacy/sre_agent/cli/utils/config.py diff --git a/sre_agent/cli/utils/env_setup.py b/legacy/sre_agent/cli/utils/env_setup.py similarity index 100% rename from sre_agent/cli/utils/env_setup.py rename to legacy/sre_agent/cli/utils/env_setup.py diff --git a/sre_agent/cli/utils/paths.py b/legacy/sre_agent/cli/utils/paths.py similarity index 100% rename from sre_agent/cli/utils/paths.py rename to legacy/sre_agent/cli/utils/paths.py diff --git a/sre_agent/cli/utils/service_manager.py b/legacy/sre_agent/cli/utils/service_manager.py similarity index 97% rename from sre_agent/cli/utils/service_manager.py rename to legacy/sre_agent/cli/utils/service_manager.py index 9d8b8a4b..7612436d 100644 --- a/sre_agent/cli/utils/service_manager.py +++ b/legacy/sre_agent/cli/utils/service_manager.py @@ -111,9 +111,7 @@ def start_services( try: console.print(f"[cyan]Starting SRE Agent services with {self.compose_file}...[/cyan]") - result = subprocess.run( - cmd, capture_output=True, text=True, timeout=300, check=False - ) # nosec B603 B607 + result = subprocess.run(cmd, capture_output=True, text=True, timeout=300, check=False) # nosec B603 B607 if result.returncode == 0: console.print("[green]✅ Services started successfully![/green]") @@ -331,9 +329,7 @@ def get_service_logs(self, service: Optional[str] = None, lines: int = 50) -> st cmd.append(service) try: - result = subprocess.run( - cmd, capture_output=True, text=True, timeout=30, check=False - ) # nosec B603 B607 + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, check=False) # nosec B603 B607 return result.stdout if result.returncode == 0 else result.stderr except Exception as e: return f"Error getting logs: {e}" diff --git a/sre_agent/client/.python-version b/legacy/sre_agent/client/.python-version similarity index 100% rename from sre_agent/client/.python-version rename to legacy/sre_agent/client/.python-version diff --git a/sre_agent/client/Dockerfile b/legacy/sre_agent/client/Dockerfile similarity index 100% rename from sre_agent/client/Dockerfile rename to legacy/sre_agent/client/Dockerfile diff --git a/sre_agent/client/__init__.py b/legacy/sre_agent/client/__init__.py similarity index 100% rename from sre_agent/client/__init__.py rename to legacy/sre_agent/client/__init__.py diff --git a/sre_agent/client/client.py b/legacy/sre_agent/client/client.py similarity index 99% rename from sre_agent/client/client.py rename to legacy/sre_agent/client/client.py index 836b8306..694e4ba6 100644 --- a/sre_agent/client/client.py +++ b/legacy/sre_agent/client/client.py @@ -402,8 +402,7 @@ async def run_diagnosis_sync(service: str) -> dict[str, Any]: if not all(server in client.sessions for server in required_servers): missing = [s.name for s in required_servers if s not in client.sessions] logger.error( - "MCP Client failed to establish required server sessions: " - f"{', '.join(missing)}" + f"MCP Client failed to establish required server sessions: {', '.join(missing)}" ) raise RuntimeError("Required MCP sessions could not be established") diff --git a/sre_agent/client/pyproject.toml b/legacy/sre_agent/client/pyproject.toml similarity index 100% rename from sre_agent/client/pyproject.toml rename to legacy/sre_agent/client/pyproject.toml diff --git a/sre_agent/client/utils/auth.py b/legacy/sre_agent/client/utils/auth.py similarity index 100% rename from sre_agent/client/utils/auth.py rename to legacy/sre_agent/client/utils/auth.py diff --git a/sre_agent/client/utils/schemas.py b/legacy/sre_agent/client/utils/schemas.py similarity index 99% rename from sre_agent/client/utils/schemas.py rename to legacy/sre_agent/client/utils/schemas.py index 570c7383..171f7148 100644 --- a/sre_agent/client/utils/schemas.py +++ b/legacy/sre_agent/client/utils/schemas.py @@ -1,4 +1,5 @@ """Schemas for the client.""" + from __future__ import annotations import json diff --git a/sre_agent/compose.agent.yaml b/legacy/sre_agent/compose.agent.yaml similarity index 100% rename from sre_agent/compose.agent.yaml rename to legacy/sre_agent/compose.agent.yaml diff --git a/sre_agent/compose.dev.yaml b/legacy/sre_agent/compose.dev.yaml similarity index 100% rename from sre_agent/compose.dev.yaml rename to legacy/sre_agent/compose.dev.yaml diff --git a/sre_agent/firewall/.python-version b/legacy/sre_agent/firewall/.python-version similarity index 100% rename from sre_agent/firewall/.python-version rename to legacy/sre_agent/firewall/.python-version diff --git a/sre_agent/firewall/Dockerfile b/legacy/sre_agent/firewall/Dockerfile similarity index 100% rename from sre_agent/firewall/Dockerfile rename to legacy/sre_agent/firewall/Dockerfile diff --git a/sre_agent/firewall/__init__.py b/legacy/sre_agent/firewall/__init__.py similarity index 100% rename from sre_agent/firewall/__init__.py rename to legacy/sre_agent/firewall/__init__.py diff --git a/sre_agent/firewall/firewall.py b/legacy/sre_agent/firewall/firewall.py similarity index 99% rename from sre_agent/firewall/firewall.py rename to legacy/sre_agent/firewall/firewall.py index 74ebf442..6f9e1f57 100644 --- a/sre_agent/firewall/firewall.py +++ b/legacy/sre_agent/firewall/firewall.py @@ -1,4 +1,5 @@ """Encapsulation of LlamaFirewall functionality.""" + import os from collections.abc import AsyncGenerator from contextlib import asynccontextmanager diff --git a/sre_agent/firewall/pyproject.toml b/legacy/sre_agent/firewall/pyproject.toml similarity index 100% rename from sre_agent/firewall/pyproject.toml rename to legacy/sre_agent/firewall/pyproject.toml diff --git a/sre_agent/firewall/startup.sh b/legacy/sre_agent/firewall/startup.sh similarity index 100% rename from sre_agent/firewall/startup.sh rename to legacy/sre_agent/firewall/startup.sh diff --git a/sre_agent/llm/.python-version b/legacy/sre_agent/llm/.python-version similarity index 100% rename from sre_agent/llm/.python-version rename to legacy/sre_agent/llm/.python-version diff --git a/sre_agent/llm/Dockerfile b/legacy/sre_agent/llm/Dockerfile similarity index 100% rename from sre_agent/llm/Dockerfile rename to legacy/sre_agent/llm/Dockerfile diff --git a/sre_agent/llm/main.py b/legacy/sre_agent/llm/main.py similarity index 98% rename from sre_agent/llm/main.py rename to legacy/sre_agent/llm/main.py index 844e6a0f..01c7d756 100644 --- a/sre_agent/llm/main.py +++ b/legacy/sre_agent/llm/main.py @@ -64,7 +64,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[Any, Any]: STATE["client"] = get_client(settings.provider) if STATE["client"] is None: - raise ValueError(f"Unknown LLM provider. Supported providers are: {", ".join(Provider)}") + raise ValueError(f"Unknown LLM provider. Supported providers are: {', '.join(Provider)}") yield STATE.clear() diff --git a/sre_agent/llm/pyproject.toml b/legacy/sre_agent/llm/pyproject.toml similarity index 100% rename from sre_agent/llm/pyproject.toml rename to legacy/sre_agent/llm/pyproject.toml diff --git a/sre_agent/llm/utils/adapters.py b/legacy/sre_agent/llm/utils/adapters.py similarity index 100% rename from sre_agent/llm/utils/adapters.py rename to legacy/sre_agent/llm/utils/adapters.py diff --git a/sre_agent/llm/utils/clients.py b/legacy/sre_agent/llm/utils/clients.py similarity index 100% rename from sre_agent/llm/utils/clients.py rename to legacy/sre_agent/llm/utils/clients.py diff --git a/sre_agent/llm/utils/schemas.py b/legacy/sre_agent/llm/utils/schemas.py similarity index 100% rename from sre_agent/llm/utils/schemas.py rename to legacy/sre_agent/llm/utils/schemas.py diff --git a/sre_agent/servers/.gitignore b/legacy/sre_agent/servers/.gitignore similarity index 100% rename from sre_agent/servers/.gitignore rename to legacy/sre_agent/servers/.gitignore diff --git a/sre_agent/servers/README.md b/legacy/sre_agent/servers/README.md similarity index 100% rename from sre_agent/servers/README.md rename to legacy/sre_agent/servers/README.md diff --git a/sre_agent/servers/github/Dockerfile b/legacy/sre_agent/servers/github/Dockerfile similarity index 100% rename from sre_agent/servers/github/Dockerfile rename to legacy/sre_agent/servers/github/Dockerfile diff --git a/sre_agent/servers/github/README.md b/legacy/sre_agent/servers/github/README.md similarity index 100% rename from sre_agent/servers/github/README.md rename to legacy/sre_agent/servers/github/README.md diff --git a/sre_agent/servers/github/common/errors.ts b/legacy/sre_agent/servers/github/common/errors.ts similarity index 100% rename from sre_agent/servers/github/common/errors.ts rename to legacy/sre_agent/servers/github/common/errors.ts diff --git a/sre_agent/servers/github/common/types.ts b/legacy/sre_agent/servers/github/common/types.ts similarity index 100% rename from sre_agent/servers/github/common/types.ts rename to legacy/sre_agent/servers/github/common/types.ts diff --git a/sre_agent/servers/github/common/utils.ts b/legacy/sre_agent/servers/github/common/utils.ts similarity index 100% rename from sre_agent/servers/github/common/utils.ts rename to legacy/sre_agent/servers/github/common/utils.ts diff --git a/sre_agent/servers/github/common/version.ts b/legacy/sre_agent/servers/github/common/version.ts similarity index 100% rename from sre_agent/servers/github/common/version.ts rename to legacy/sre_agent/servers/github/common/version.ts diff --git a/sre_agent/servers/github/index.ts b/legacy/sre_agent/servers/github/index.ts similarity index 100% rename from sre_agent/servers/github/index.ts rename to legacy/sre_agent/servers/github/index.ts diff --git a/sre_agent/servers/github/operations/branches.ts b/legacy/sre_agent/servers/github/operations/branches.ts similarity index 100% rename from sre_agent/servers/github/operations/branches.ts rename to legacy/sre_agent/servers/github/operations/branches.ts diff --git a/sre_agent/servers/github/operations/commits.ts b/legacy/sre_agent/servers/github/operations/commits.ts similarity index 100% rename from sre_agent/servers/github/operations/commits.ts rename to legacy/sre_agent/servers/github/operations/commits.ts diff --git a/sre_agent/servers/github/operations/files.ts b/legacy/sre_agent/servers/github/operations/files.ts similarity index 100% rename from sre_agent/servers/github/operations/files.ts rename to legacy/sre_agent/servers/github/operations/files.ts diff --git a/sre_agent/servers/github/operations/issues.ts b/legacy/sre_agent/servers/github/operations/issues.ts similarity index 100% rename from sre_agent/servers/github/operations/issues.ts rename to legacy/sre_agent/servers/github/operations/issues.ts diff --git a/sre_agent/servers/github/operations/pulls.ts b/legacy/sre_agent/servers/github/operations/pulls.ts similarity index 100% rename from sre_agent/servers/github/operations/pulls.ts rename to legacy/sre_agent/servers/github/operations/pulls.ts diff --git a/sre_agent/servers/github/operations/repository.ts b/legacy/sre_agent/servers/github/operations/repository.ts similarity index 100% rename from sre_agent/servers/github/operations/repository.ts rename to legacy/sre_agent/servers/github/operations/repository.ts diff --git a/sre_agent/servers/github/operations/search.ts b/legacy/sre_agent/servers/github/operations/search.ts similarity index 100% rename from sre_agent/servers/github/operations/search.ts rename to legacy/sre_agent/servers/github/operations/search.ts diff --git a/sre_agent/servers/github/package.json b/legacy/sre_agent/servers/github/package.json similarity index 100% rename from sre_agent/servers/github/package.json rename to legacy/sre_agent/servers/github/package.json diff --git a/sre_agent/servers/github/tsconfig.json b/legacy/sre_agent/servers/github/tsconfig.json similarity index 100% rename from sre_agent/servers/github/tsconfig.json rename to legacy/sre_agent/servers/github/tsconfig.json diff --git a/sre_agent/servers/github/utils/logger.ts b/legacy/sre_agent/servers/github/utils/logger.ts similarity index 100% rename from sre_agent/servers/github/utils/logger.ts rename to legacy/sre_agent/servers/github/utils/logger.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/.github/workflows/cd.yml b/legacy/sre_agent/servers/mcp-server-kubernetes/.github/workflows/cd.yml similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/.github/workflows/cd.yml rename to legacy/sre_agent/servers/mcp-server-kubernetes/.github/workflows/cd.yml diff --git a/sre_agent/servers/mcp-server-kubernetes/.github/workflows/ci.yml b/legacy/sre_agent/servers/mcp-server-kubernetes/.github/workflows/ci.yml similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/.github/workflows/ci.yml rename to legacy/sre_agent/servers/mcp-server-kubernetes/.github/workflows/ci.yml diff --git a/sre_agent/servers/mcp-server-kubernetes/.gitignore b/legacy/sre_agent/servers/mcp-server-kubernetes/.gitignore similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/.gitignore rename to legacy/sre_agent/servers/mcp-server-kubernetes/.gitignore diff --git a/sre_agent/servers/mcp-server-kubernetes/.vscode/extensions.json b/legacy/sre_agent/servers/mcp-server-kubernetes/.vscode/extensions.json similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/.vscode/extensions.json rename to legacy/sre_agent/servers/mcp-server-kubernetes/.vscode/extensions.json diff --git a/sre_agent/servers/mcp-server-kubernetes/.vscode/settings.json b/legacy/sre_agent/servers/mcp-server-kubernetes/.vscode/settings.json similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/.vscode/settings.json rename to legacy/sre_agent/servers/mcp-server-kubernetes/.vscode/settings.json diff --git a/sre_agent/servers/mcp-server-kubernetes/ADVANCED_README.md b/legacy/sre_agent/servers/mcp-server-kubernetes/ADVANCED_README.md similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/ADVANCED_README.md rename to legacy/sre_agent/servers/mcp-server-kubernetes/ADVANCED_README.md diff --git a/sre_agent/servers/mcp-server-kubernetes/Dockerfile b/legacy/sre_agent/servers/mcp-server-kubernetes/Dockerfile similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/Dockerfile rename to legacy/sre_agent/servers/mcp-server-kubernetes/Dockerfile diff --git a/sre_agent/servers/mcp-server-kubernetes/LICENSE b/legacy/sre_agent/servers/mcp-server-kubernetes/LICENSE similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/LICENSE rename to legacy/sre_agent/servers/mcp-server-kubernetes/LICENSE diff --git a/sre_agent/servers/mcp-server-kubernetes/README.md b/legacy/sre_agent/servers/mcp-server-kubernetes/README.md similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/README.md rename to legacy/sre_agent/servers/mcp-server-kubernetes/README.md diff --git a/sre_agent/servers/mcp-server-kubernetes/bun.lockb b/legacy/sre_agent/servers/mcp-server-kubernetes/bun.lockb similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/bun.lockb rename to legacy/sre_agent/servers/mcp-server-kubernetes/bun.lockb diff --git a/sre_agent/servers/mcp-server-kubernetes/package.json b/legacy/sre_agent/servers/mcp-server-kubernetes/package.json similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/package.json rename to legacy/sre_agent/servers/mcp-server-kubernetes/package.json diff --git a/sre_agent/servers/mcp-server-kubernetes/src/config/cleanup-config.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/config/cleanup-config.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/config/cleanup-config.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/config/cleanup-config.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/config/container-templates.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/config/container-templates.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/config/container-templates.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/config/container-templates.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/config/deployment-config.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/config/deployment-config.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/config/deployment-config.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/config/deployment-config.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/config/namespace-config.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/config/namespace-config.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/config/namespace-config.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/config/namespace-config.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/config/server-config.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/config/server-config.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/config/server-config.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/config/server-config.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/index.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/index.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/index.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/index.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/models/helm-models.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/models/helm-models.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/models/helm-models.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/models/helm-models.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/models/kubectl-models.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/models/kubectl-models.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/models/kubectl-models.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/models/kubectl-models.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/models/resource-models.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/models/resource-models.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/models/resource-models.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/models/resource-models.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/models/response-schemas.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/models/response-schemas.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/models/response-schemas.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/models/response-schemas.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/models/tool-models.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/models/tool-models.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/models/tool-models.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/models/tool-models.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/resources/handlers.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/resources/handlers.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/resources/handlers.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/resources/handlers.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_configmap.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_configmap.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_configmap.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_configmap.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_cronjob.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_cronjob.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_cronjob.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_cronjob.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_deployment.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_deployment.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_deployment.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_deployment.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_namespace.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_namespace.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_namespace.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_namespace.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_pod.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_pod.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_pod.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_pod.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/create_service.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_service.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/create_service.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/create_service.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_configmap.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_configmap.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_configmap.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_configmap.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_cronjob.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_cronjob.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_cronjob.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_cronjob.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_deployment.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_deployment.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_deployment.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_deployment.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_namespace.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_namespace.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_namespace.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_namespace.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_pod.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_pod.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_pod.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_pod.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_service.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_service.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/delete_service.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/delete_service.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_cronjob.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_cronjob.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/describe_cronjob.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_cronjob.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_deployment.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_deployment.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/describe_deployment.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_deployment.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_node.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_node.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/describe_node.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_node.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_pod.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_pod.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/describe_pod.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_pod.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_service.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_service.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/describe_service.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/describe_service.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/get_configmap.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_configmap.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/get_configmap.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_configmap.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/get_current_context.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_current_context.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/get_current_context.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_current_context.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/get_events.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_events.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/get_events.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_events.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/get_job_logs.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_job_logs.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/get_job_logs.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_job_logs.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/get_logs.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_logs.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/get_logs.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/get_logs.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/helm-operations.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/helm-operations.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/helm-operations.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/helm-operations.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/kubectl-operations.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/kubectl-operations.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/kubectl-operations.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/kubectl-operations.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_contexts.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_contexts.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_contexts.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_contexts.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_cronjobs.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_cronjobs.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_cronjobs.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_cronjobs.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_deployments.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_deployments.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_deployments.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_deployments.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_jobs.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_jobs.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_jobs.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_jobs.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_nodes.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_nodes.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_nodes.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_nodes.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_pods.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_pods.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_pods.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_pods.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/list_services.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_services.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/list_services.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/list_services.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/port_forward.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/port_forward.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/port_forward.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/port_forward.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/scale_deployment.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/scale_deployment.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/scale_deployment.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/scale_deployment.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/set_current_context.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/set_current_context.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/set_current_context.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/set_current_context.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/update_configmap.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_configmap.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/update_configmap.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_configmap.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/update_deployment.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_deployment.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/update_deployment.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_deployment.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/tools/update_service.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_service.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/tools/update_service.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/tools/update_service.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/types.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/types.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/types.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/types.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/utils/kubernetes-manager.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/kubernetes-manager.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/utils/kubernetes-manager.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/kubernetes-manager.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/utils/logger.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/logger.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/utils/logger.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/logger.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/src/utils/sse.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/sse.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/src/utils/sse.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/src/utils/sse.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/startup.sh b/legacy/sre_agent/servers/mcp-server-kubernetes/startup.sh similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/startup.sh rename to legacy/sre_agent/servers/mcp-server-kubernetes/startup.sh diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/configmap.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/configmap.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/configmap.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/configmap.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/contexts.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/contexts.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/contexts.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/contexts.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/cronjob.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/cronjob.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/cronjob.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/cronjob.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/current_context.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/current_context.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/current_context.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/current_context.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/helm.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/helm.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/helm.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/helm.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/kubectl.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/kubectl.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/kubectl.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/kubectl.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/namespace.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/namespace.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/namespace.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/namespace.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/non_destructive_tools.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/non_destructive_tools.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/non_destructive_tools.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/non_destructive_tools.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/port_forward.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/port_forward.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/port_forward.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/port_forward.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/service.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/service.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/service.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/service.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/set_current_context.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/set_current_context.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/set_current_context.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/set_current_context.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/sse.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/sse.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/sse.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/sse.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tests/unit.test.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/tests/unit.test.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tests/unit.test.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/tests/unit.test.ts diff --git a/sre_agent/servers/mcp-server-kubernetes/tsconfig.json b/legacy/sre_agent/servers/mcp-server-kubernetes/tsconfig.json similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/tsconfig.json rename to legacy/sre_agent/servers/mcp-server-kubernetes/tsconfig.json diff --git a/sre_agent/servers/mcp-server-kubernetes/vitest.config.ts b/legacy/sre_agent/servers/mcp-server-kubernetes/vitest.config.ts similarity index 100% rename from sre_agent/servers/mcp-server-kubernetes/vitest.config.ts rename to legacy/sre_agent/servers/mcp-server-kubernetes/vitest.config.ts diff --git a/sre_agent/servers/prompt_server/.python-version b/legacy/sre_agent/servers/prompt_server/.python-version similarity index 100% rename from sre_agent/servers/prompt_server/.python-version rename to legacy/sre_agent/servers/prompt_server/.python-version diff --git a/sre_agent/servers/prompt_server/Dockerfile b/legacy/sre_agent/servers/prompt_server/Dockerfile similarity index 100% rename from sre_agent/servers/prompt_server/Dockerfile rename to legacy/sre_agent/servers/prompt_server/Dockerfile diff --git a/sre_agent/servers/prompt_server/pyproject.toml b/legacy/sre_agent/servers/prompt_server/pyproject.toml similarity index 100% rename from sre_agent/servers/prompt_server/pyproject.toml rename to legacy/sre_agent/servers/prompt_server/pyproject.toml diff --git a/sre_agent/servers/prompt_server/server.py b/legacy/sre_agent/servers/prompt_server/server.py similarity index 100% rename from sre_agent/servers/prompt_server/server.py rename to legacy/sre_agent/servers/prompt_server/server.py diff --git a/sre_agent/servers/prompt_server/utils/schemas.py b/legacy/sre_agent/servers/prompt_server/utils/schemas.py similarity index 100% rename from sre_agent/servers/prompt_server/utils/schemas.py rename to legacy/sre_agent/servers/prompt_server/utils/schemas.py diff --git a/sre_agent/servers/slack/Dockerfile b/legacy/sre_agent/servers/slack/Dockerfile similarity index 100% rename from sre_agent/servers/slack/Dockerfile rename to legacy/sre_agent/servers/slack/Dockerfile diff --git a/sre_agent/servers/slack/README.md b/legacy/sre_agent/servers/slack/README.md similarity index 100% rename from sre_agent/servers/slack/README.md rename to legacy/sre_agent/servers/slack/README.md diff --git a/sre_agent/servers/slack/index.ts b/legacy/sre_agent/servers/slack/index.ts similarity index 100% rename from sre_agent/servers/slack/index.ts rename to legacy/sre_agent/servers/slack/index.ts diff --git a/sre_agent/servers/slack/package-lock.json b/legacy/sre_agent/servers/slack/package-lock.json similarity index 100% rename from sre_agent/servers/slack/package-lock.json rename to legacy/sre_agent/servers/slack/package-lock.json diff --git a/sre_agent/servers/slack/package.json b/legacy/sre_agent/servers/slack/package.json similarity index 100% rename from sre_agent/servers/slack/package.json rename to legacy/sre_agent/servers/slack/package.json diff --git a/sre_agent/servers/slack/tsconfig.json b/legacy/sre_agent/servers/slack/tsconfig.json similarity index 100% rename from sre_agent/servers/slack/tsconfig.json rename to legacy/sre_agent/servers/slack/tsconfig.json diff --git a/sre_agent/servers/slack/utils/logger.ts b/legacy/sre_agent/servers/slack/utils/logger.ts similarity index 100% rename from sre_agent/servers/slack/utils/logger.ts rename to legacy/sre_agent/servers/slack/utils/logger.ts diff --git a/sre_agent/shared/__init__.py b/legacy/sre_agent/shared/__init__.py similarity index 100% rename from sre_agent/shared/__init__.py rename to legacy/sre_agent/shared/__init__.py diff --git a/sre_agent/shared/logger.py b/legacy/sre_agent/shared/logger.py similarity index 93% rename from sre_agent/shared/logger.py rename to legacy/sre_agent/shared/logger.py index da710496..5c3c3f02 100644 --- a/sre_agent/shared/logger.py +++ b/legacy/sre_agent/shared/logger.py @@ -57,7 +57,9 @@ class ColoredFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: # Add color to the levelname if record.levelname in self.COLORS: - record.levelname = f"{self.COLORS[record.levelname]}{record.levelname}{self.COLORS['RESET']}" # noqa: E501 + record.levelname = ( + f"{self.COLORS[record.levelname]}{record.levelname}{self.COLORS['RESET']}" # noqa: E501 + ) return super().format(record) diff --git a/sre_agent/shared/py.typed b/legacy/sre_agent/shared/py.typed similarity index 100% rename from sre_agent/shared/py.typed rename to legacy/sre_agent/shared/py.typed diff --git a/sre_agent/shared/pyproject.toml b/legacy/sre_agent/shared/pyproject.toml similarity index 100% rename from sre_agent/shared/pyproject.toml rename to legacy/sre_agent/shared/pyproject.toml diff --git a/sre_agent/shared/schemas.py b/legacy/sre_agent/shared/schemas.py similarity index 100% rename from sre_agent/shared/schemas.py rename to legacy/sre_agent/shared/schemas.py diff --git a/sre_agent/tsconfig.json b/legacy/sre_agent/tsconfig.json similarity index 100% rename from sre_agent/tsconfig.json rename to legacy/sre_agent/tsconfig.json diff --git a/tests/__init__.py b/legacy/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to legacy/tests/__init__.py diff --git a/tests/security_tests/test_guardrails.py b/legacy/tests/security_tests/test_guardrails.py similarity index 99% rename from tests/security_tests/test_guardrails.py rename to legacy/tests/security_tests/test_guardrails.py index c961cf37..dc7420e7 100644 --- a/tests/security_tests/test_guardrails.py +++ b/legacy/tests/security_tests/test_guardrails.py @@ -1,6 +1,5 @@ """Tests for the Guardrails library.""" - from unittest import IsolatedAsyncioTestCase import requests diff --git a/tests/security_tests/test_input_validation.py b/legacy/tests/security_tests/test_input_validation.py similarity index 99% rename from tests/security_tests/test_input_validation.py rename to legacy/tests/security_tests/test_input_validation.py index 04beaf4c..03f1122d 100644 --- a/tests/security_tests/test_input_validation.py +++ b/legacy/tests/security_tests/test_input_validation.py @@ -1,6 +1,5 @@ """A test to assert that an invalid input the API returns an error message.""" - import unittest from http import HTTPStatus diff --git a/tests/unit_tests/test_adapters.py b/legacy/tests/unit_tests/test_adapters.py similarity index 100% rename from tests/unit_tests/test_adapters.py rename to legacy/tests/unit_tests/test_adapters.py diff --git a/legacy/uv.lock b/legacy/uv.lock new file mode 100644 index 00000000..5120b808 --- /dev/null +++ b/legacy/uv.lock @@ -0,0 +1,2325 @@ +version = 1 +revision = 3 +requires-python = ">=3.12, <4.0" + +[manifest] +members = [ + "client", + "firewall", + "llm", + "prompt-server", + "sre-agent", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anthropic" +version = "0.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/f6/a78ff9e23981fde136c3ae5427a39b27df92ebe5e5997c6203796449f1e5/anthropic-0.53.0.tar.gz", hash = "sha256:f5d1499fc45b2e05801fcbbeae25679f72f7479763e3c706126a7a7c8de06eff", size = 307716, upload-time = "2025-06-09T16:20:31.689Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/3f/82c21f74afa3541d69d20b8265c7fdfd078a687e9eea48fda30f1838d0b7/anthropic-0.53.0-py3-none-any.whl", hash = "sha256:b3a84751885a81d96bbddef180c3ce559c9140f7f230cdd825385405bd6d312e", size = 287248, upload-time = "2025-06-09T16:20:29.98Z" }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, +] + +[[package]] +name = "attrs" +version = "23.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820, upload-time = "2023-12-31T06:30:32.926Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752, upload-time = "2023-12-31T06:30:30.772Z" }, +] + +[[package]] +name = "boltons" +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/1f/6c0608d86e0fc77c982a2923ece80eef85f091f2332fc13cbce41d70d502/boltons-21.0.0.tar.gz", hash = "sha256:65e70a79a731a7fe6e98592ecfb5ccf2115873d01dbc576079874629e5c90f13", size = 180201, upload-time = "2021-05-17T01:20:17.802Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/a7/1a31561d10a089fcb46fe286766dd4e053a12f6e23b4fd1c26478aff2475/boltons-21.0.0-py2.py3-none-any.whl", hash = "sha256:b9bb7b58b2b420bbe11a6025fdef6d3e5edc9f76a42fb467afe7ca212ef9948b", size = 193723, upload-time = "2021-05-17T01:20:20.023Z" }, +] + +[[package]] +name = "bracex" +version = "2.5.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/6c/57418c4404cd22fe6275b8301ca2b46a8cdaa8157938017a9ae0b3edf363/bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6", size = 26641, upload-time = "2024-09-28T21:41:22.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/02/8db98cdc1a58e0abd6716d5e63244658e6e63513c65f469f34b6f1053fd0/bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6", size = 11558, upload-time = "2024-09-28T21:41:21.016Z" }, +] + +[[package]] +name = "braq" +version = "0.0.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/3b/1b918c408e11ca33f9b9dcecc8e08eac7762887dd42b584f0efb6fe26c55/braq-0.0.12.tar.gz", hash = "sha256:51dae51b863cbba2cd37da163df06b7dc5124904d2c26b92bda54c1bde66d74b", size = 15272, upload-time = "2024-12-10T20:48:53.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/53/ed5082619966b1d15b5c039ac722ba99956d92d4b08a9bd5eb4c3535cc1f/braq-0.0.12-py3-none-any.whl", hash = "sha256:41b7bdd0d004faef693751615fbb11c53ac0b886c772b83aea61ea6dc2f6e518", size = 26392, upload-time = "2024-12-10T20:48:50.813Z" }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, +] + +[[package]] +name = "cattrs" +version = "24.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/7b/da4aa2f95afb2f28010453d03d6eedf018f9e085bd001f039e15731aba89/cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff", size = 426684, upload-time = "2025-03-25T15:01:00.325Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/ee/d68a3de23867a9156bab7e0a22fb9a0305067ee639032a22982cf7f725e7/cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5", size = 66462, upload-time = "2025-03-25T15:00:58.663Z" }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "click-option-group" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/9f/1f917934da4e07ae7715a982347e3c2179556d8a58d1108c5da3e8f09c76/click_option_group-0.5.7.tar.gz", hash = "sha256:8dc780be038712fc12c9fecb3db4fe49e0d0723f9c171d7cda85c20369be693c", size = 22110, upload-time = "2025-03-24T13:24:55.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/27/bf74dc1494625c3b14dbcdb93740defd7b8c58dae3736be8d264f2a643fb/click_option_group-0.5.7-py3-none-any.whl", hash = "sha256:96b9f52f397ef4d916f81929bd6c1f85e89046c7a401a64e72a61ae74ad35c24", size = 11483, upload-time = "2025-03-24T13:24:54.611Z" }, +] + +[[package]] +name = "client" +version = "0.1.0" +source = { virtual = "sre_agent/client" } +dependencies = [ + { name = "fastapi" }, + { name = "huggingface-hub" }, + { name = "llamafirewall" }, + { name = "mcp", extra = ["cli"] }, + { name = "python-dotenv" }, + { name = "python-multipart" }, + { name = "requests" }, + { name = "shared" }, + { name = "types-requests" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = ">=0.115.12" }, + { name = "huggingface-hub" }, + { name = "llamafirewall", specifier = ">=1.0.2" }, + { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, + { name = "python-dotenv", specifier = ">=1.1.0" }, + { name = "python-multipart", specifier = ">=0.0.20" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "shared" }, + { name = "types-requests", specifier = ">=2.32.0.20250328" }, + { name = "uvicorn", specifier = ">=0.34.2" }, +] + +[[package]] +name = "codeshield" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "semgrep" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/0e/cb79d48ba05eda459a5a2e90b6056019cf7f41441cdee2a17e8dd63e5502/codeshield-1.0.1.tar.gz", hash = "sha256:61866b9281c506f9e176995408daab931d52832e625f6056bba273e80a81139f", size = 274198, upload-time = "2024-04-19T15:10:35.326Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/a8/1ce1dcbdc8593e04048b1ea469db8eb92783e82e351405a375e929979f0b/codeshield-1.0.1-py3-none-any.whl", hash = "sha256:cd3516a5006002e0e7400a98e5a4592256a37ce6caf5e162d45ed093eb548377", size = 173368, upload-time = "2024-04-19T15:10:33.413Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, + { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, + { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, + { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, + { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, + { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, + { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, + { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, + { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, + { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, + { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, + { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, + { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, + { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, + { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, + { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, + { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, + { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, + { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, + { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, + { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, + { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +] + +[[package]] +name = "deprecated" +version = "1.2.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, +] + +[[package]] +name = "distlib" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, +] + +[[package]] +name = "face" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boltons" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/79/2484075a8549cd64beae697a8f664dee69a5ccf3a7439ee40c8f93c1978a/face-24.0.0.tar.gz", hash = "sha256:611e29a01ac5970f0077f9c577e746d48c082588b411b33a0dd55c4d872949f6", size = 62732, upload-time = "2024-11-02T05:24:26.095Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/47/21867c2e5fd006c8d36a560df9e32cb4f1f566b20c5dd41f5f8a2124f7de/face-24.0.0-py3-none-any.whl", hash = "sha256:0e2c17b426fa4639a4e77d1de9580f74a98f4869ba4c7c8c175b810611622cd3", size = 54742, upload-time = "2024-11-02T05:24:24.939Z" }, +] + +[[package]] +name = "fastapi" +version = "0.115.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, +] + +[[package]] +name = "fhconfparser" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/b3/ca177719df2db0050599c576023858b86cabe4f54d3beda0e7e673a6892f/fhconfparser-2024.1.tar.gz", hash = "sha256:de8af019f0071e614d523985e1d93e0fce20a409d1c64dead03b1b665d4b2e4d", size = 8357, upload-time = "2024-01-24T21:48:56.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/2b/fd360e1b65ba44179424aa0a8c227c17d7df384f20bb8d38a5cbe23e3ba2/fhconfparser-2024.1-py3-none-any.whl", hash = "sha256:f6048cb646e69a3422a581bc0102150c2b79fe7ff26b82233e5ef52f72820e3e", size = 9221, upload-time = "2024-01-24T21:48:54.81Z" }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, +] + +[[package]] +name = "firewall" +version = "0.1.0" +source = { virtual = "sre_agent/firewall" } +dependencies = [ + { name = "fastapi" }, + { name = "huggingface-hub", extra = ["hf-xet"] }, + { name = "llamafirewall" }, + { name = "pydantic" }, + { name = "transformers" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = ">=0.115.12" }, + { name = "huggingface-hub", extras = ["hf-xet"], specifier = ">=0.31.1" }, + { name = "llamafirewall", specifier = ">=1.0.2" }, + { name = "pydantic", specifier = ">=2.11.3" }, + { name = "transformers", specifier = ">=4.51.3" }, + { name = "uvicorn", specifier = ">=0.34.2" }, +] + +[[package]] +name = "fsspec" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033, upload-time = "2025-05-24T12:03:23.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052, upload-time = "2025-05-24T12:03:21.66Z" }, +] + +[[package]] +name = "glom" +version = "22.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "boltons" }, + { name = "face" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/d1/69432deefa6f5283ec75b246d0540097ae26f618b915519ee3824c4c5dd6/glom-22.1.0.tar.gz", hash = "sha256:1510c6587a8f9c64a246641b70033cbc5ebde99f02ad245693678038e821aeb5", size = 189738, upload-time = "2022-01-24T09:34:04.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/e8/68e274b2a30e1fdfd25bdc27194382be3f233929c8f727c0440d58ac074f/glom-22.1.0-py2.py3-none-any.whl", hash = "sha256:5339da206bf3532e01a83a35aca202960ea885156986d190574b779598e9e772", size = 100687, upload-time = "2022-01-24T09:34:02.391Z" }, +] + +[[package]] +name = "google-auth" +version = "2.40.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, +] + +[[package]] +name = "google-genai" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "google-auth" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/37/98742eeae25556d7558f336f9cdbb8e7276d32a5699b03cabc3ffa9f12ea/google_genai-1.22.0.tar.gz", hash = "sha256:1ece195e7be97cb94dbecce43dd88e3f4e376afd31045e54d1dd0ef272a6ee6b", size = 221720, upload-time = "2025-06-26T00:09:27.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/fa/ad39a0457a9c3e21438062076cc216d41c4f8b414aa3d2ec481c721ca5f7/google_genai-1.22.0-py3-none-any.whl", hash = "sha256:6627bea9451775a2af78c6cb1992f5a31b90c50d64fb1f1435a385737a69fce4", size = 222848, upload-time = "2025-06-26T00:09:25.955Z" }, +] + +[[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 = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/dc/dc091aeeb671e71cbec30e84963f9c0202c17337b24b0a800e7d205543e8/hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3", size = 488127, upload-time = "2025-06-04T00:47:27.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/1f/bc01a4c0894973adebbcd4aa338a06815c76333ebb3921d94dcbd40dae6a/hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b", size = 2256929, upload-time = "2025-06-04T00:47:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/78/07/6ef50851b5c6b45b77a6e018fa299c69a2db3b8bbd0d5af594c0238b1ceb/hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1", size = 2153719, upload-time = "2025-06-04T00:47:19.302Z" }, + { url = "https://files.pythonhosted.org/packages/52/48/e929e6e3db6e4758c2adf0f2ca2c59287f1b76229d8bdc1a4c9cfc05212e/hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534", size = 4820519, upload-time = "2025-06-04T00:47:17.244Z" }, + { url = "https://files.pythonhosted.org/packages/28/2e/03f89c5014a5aafaa9b150655f811798a317036646623bdaace25f485ae8/hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841", size = 4964121, upload-time = "2025-06-04T00:47:15.17Z" }, + { url = "https://files.pythonhosted.org/packages/47/8b/5cd399a92b47d98086f55fc72d69bc9ea5e5c6f27a9ed3e0cdd6be4e58a3/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b", size = 5283017, upload-time = "2025-06-04T00:47:23.239Z" }, + { url = "https://files.pythonhosted.org/packages/53/e3/2fcec58d2fcfd25ff07feb876f466cfa11f8dcf9d3b742c07fe9dd51ee0a/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1", size = 4970349, upload-time = "2025-06-04T00:47:25.383Z" }, + { url = "https://files.pythonhosted.org/packages/53/bf/10ca917e335861101017ff46044c90e517b574fbb37219347b83be1952f6/hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3", size = 2310934, upload-time = "2025-06-04T00:47:29.632Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" }, +] + +[package.optional-dependencies] +hf-xet = [ + { name = "hf-xet" }, +] + +[[package]] +name = "identify" +version = "2.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120, upload-time = "2024-03-20T19:51:32.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409, upload-time = "2024-03-20T19:51:30.241Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" }, + { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" }, + { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" }, + { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" }, + { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" }, + { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" }, + { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" }, + { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" }, + { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" }, + { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, + { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, + { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, + { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, + { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, + { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, + { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, + { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, + { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, + { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, + { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, + { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, + { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, + { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, + { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, +] + +[[package]] +name = "kvf" +version = "0.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "braq" }, + { name = "paradict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/f8/e1826c156d4f97cf4662a6110cbbcfd91b5e5570c8a88bf0a8270718621e/kvf-0.0.3.tar.gz", hash = "sha256:f4885b1bbe66c8c20fdabe5cedeb3c0e5d12a54ac495f9e5fcf6fed0e0c51b73", size = 4938, upload-time = "2024-12-10T20:49:13.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/db/4a8d3b1fef45cabcadf36f9a2231b2cde3dddd3a58ab1723119c7fbce34f/kvf-0.0.3-py3-none-any.whl", hash = "sha256:9d666e51cae512e3f95c55b77524e34d0095b278c81f96f7bbc7d37b5bd545c6", size = 4716, upload-time = "2024-12-10T20:49:11.815Z" }, +] + +[[package]] +name = "licensecheck" +version = "2024.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appdirs" }, + { name = "fhconfparser" }, + { name = "loguru" }, + { name = "markdown" }, + { name = "packaging" }, + { name = "requests" }, + { name = "requests-cache" }, + { name = "requirements-parser" }, + { name = "rich" }, + { name = "tomli" }, + { name = "uv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/77/a73c2b8285525b80920c25e78aa93685a16738c79c9021868e478eeae90c/licensecheck-2024.3.tar.gz", hash = "sha256:e838e1c87a7ede553df376ad35a69d7c4b02676df0fba9dd1c6a6866eb0e0ee5", size = 21052, upload-time = "2024-08-26T20:53:02.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/aa/88b9cb56e47db1327b65773d72d78c850ce26e7adcf5e7963220c48f5d1b/licensecheck-2024.3-py3-none-any.whl", hash = "sha256:0baef4c1865e0325a35ff25ed12a0c7094035b7dcfbab9a1abfe43d7735adebe", size = 24984, upload-time = "2024-08-26T20:53:00.819Z" }, +] + +[[package]] +name = "llamafirewall" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "codeshield" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "torch" }, + { name = "transformers" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/f5/9dbd3b0a74c11323d967b0e1210a9fac0de068abc0c2e5dc08a6ee2094a5/llamafirewall-1.0.3.tar.gz", hash = "sha256:54fe55c8636fb0b7e78734fdbb96f0036de7ad6613e3dc23ab8531fcf73e6ec1", size = 20805, upload-time = "2025-05-29T16:49:14.281Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/d2/3c4fe84430f2bd77b5c40e77f197ed3b0bb7f0979abe06d66b831727026a/llamafirewall-1.0.3-py2.py3-none-any.whl", hash = "sha256:7bc110f4322c5eefb112fcde83b21da67176de4fa9036c184b14bf04f1ea4b30", size = 34421, upload-time = "2025-05-29T16:49:12.692Z" }, +] + +[[package]] +name = "llm" +version = "0.1.0" +source = { virtual = "sre_agent/llm" } +dependencies = [ + { name = "anthropic" }, + { name = "fastapi" }, + { name = "google-genai" }, + { name = "mcp", extra = ["cli"] }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "shared" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=0.49.0" }, + { name = "fastapi", specifier = ">=0.115.12" }, + { name = "google-genai", specifier = ">=1.19.0" }, + { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, + { name = "pydantic", specifier = ">=2.11.3" }, + { name = "pydantic-settings", specifier = ">=2.9.1" }, + { name = "python-dotenv", specifier = ">=1.1.0" }, + { name = "shared" }, + { name = "uvicorn", specifier = ">=0.34.2" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "markdown" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload-time = "2025-04-11T14:42:50.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload-time = "2025-04-11T14:42:49.178Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +] + +[[package]] +name = "mcp" +version = "1.9.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045, upload-time = "2025-06-05T15:48:25.681Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063, upload-time = "2025-06-05T15:48:24.171Z" }, +] + +[package.optional-dependencies] +cli = [ + { name = "python-dotenv" }, + { name = "typer" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "mypy" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" }, + { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" }, + { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" }, + { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" }, + { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" }, + { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, + { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, + { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813, upload-time = "2025-06-07T14:54:32.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/59/9df493df81ac6f76e9f05cdbe013cdb0c9a37b434f6e594f5bd25e278908/numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba", size = 20897025, upload-time = "2025-06-07T14:40:33.558Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/4ff04335901d6cf3a6bb9c748b0097546ae5af35e455ae9b962ebff4ecd7/numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e", size = 14129882, upload-time = "2025-06-07T14:40:55.034Z" }, + { url = "https://files.pythonhosted.org/packages/71/8d/a942cd4f959de7f08a79ab0c7e6cecb7431d5403dce78959a726f0f57aa1/numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2", size = 5110181, upload-time = "2025-06-07T14:41:04.4Z" }, + { url = "https://files.pythonhosted.org/packages/86/5d/45850982efc7b2c839c5626fb67fbbc520d5b0d7c1ba1ae3651f2f74c296/numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459", size = 6647581, upload-time = "2025-06-07T14:41:14.695Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c0/c871d4a83f93b00373d3eebe4b01525eee8ef10b623a335ec262b58f4dc1/numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a", size = 14262317, upload-time = "2025-06-07T14:41:35.862Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f6/bc47f5fa666d5ff4145254f9e618d56e6a4ef9b874654ca74c19113bb538/numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a", size = 16633919, upload-time = "2025-06-07T14:42:00.622Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b4/65f48009ca0c9b76df5f404fccdea5a985a1bb2e34e97f21a17d9ad1a4ba/numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67", size = 15567651, upload-time = "2025-06-07T14:42:24.429Z" }, + { url = "https://files.pythonhosted.org/packages/f1/62/5367855a2018578e9334ed08252ef67cc302e53edc869666f71641cad40b/numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc", size = 18361723, upload-time = "2025-06-07T14:42:51.167Z" }, + { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285, upload-time = "2025-06-07T14:43:02.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594, upload-time = "2025-06-07T14:43:21.071Z" }, + { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498, upload-time = "2025-06-07T14:43:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633, upload-time = "2025-06-07T14:44:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683, upload-time = "2025-06-07T14:44:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683, upload-time = "2025-06-07T14:44:38.417Z" }, + { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253, upload-time = "2025-06-07T14:44:49.359Z" }, + { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658, upload-time = "2025-06-07T14:45:10.156Z" }, + { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765, upload-time = "2025-06-07T14:45:35.076Z" }, + { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335, upload-time = "2025-06-07T14:45:58.797Z" }, + { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608, upload-time = "2025-06-07T14:46:25.687Z" }, + { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005, upload-time = "2025-06-07T14:50:13.138Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093, upload-time = "2025-06-07T14:50:31.82Z" }, + { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689, upload-time = "2025-06-07T14:50:47.888Z" }, + { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612, upload-time = "2025-06-07T14:46:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953, upload-time = "2025-06-07T14:47:18.053Z" }, + { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806, upload-time = "2025-06-07T14:47:27.524Z" }, + { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169, upload-time = "2025-06-07T14:47:38.057Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701, upload-time = "2025-06-07T14:47:59.113Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983, upload-time = "2025-06-07T14:48:24.196Z" }, + { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435, upload-time = "2025-06-07T14:48:47.712Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798, upload-time = "2025-06-07T14:49:14.866Z" }, + { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632, upload-time = "2025-06-07T14:49:25.67Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491, upload-time = "2025-06-07T14:49:44.898Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345, upload-time = "2025-06-07T14:50:02.311Z" }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.6.4.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322, upload-time = "2024-11-20T17:40:25.65Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.6.80" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980, upload-time = "2024-11-20T17:36:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972, upload-time = "2024-10-01T16:58:06.036Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380, upload-time = "2024-10-01T17:00:14.643Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690, upload-time = "2024-11-20T17:35:30.697Z" }, + { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678, upload-time = "2024-10-01T16:57:33.821Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.5.1.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386, upload-time = "2024-10-25T19:54:26.39Z" }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632, upload-time = "2024-11-20T17:41:32.357Z" }, + { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622, upload-time = "2024-10-01T17:03:58.79Z" }, +] + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.11.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103, upload-time = "2024-11-20T17:42:11.83Z" }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.7.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010, upload-time = "2024-11-20T17:42:50.958Z" }, + { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000, upload-time = "2024-10-01T17:04:45.274Z" }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790, upload-time = "2024-11-20T17:43:43.211Z" }, + { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780, upload-time = "2024-10-01T17:05:39.875Z" }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367, upload-time = "2024-11-20T17:44:54.824Z" }, + { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357, upload-time = "2024-10-01T17:06:29.861Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.26.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.6.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971, upload-time = "2024-11-20T17:46:53.366Z" }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.6.77" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276, upload-time = "2024-11-20T17:38:27.621Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265, upload-time = "2024-10-01T17:00:38.172Z" }, +] + +[[package]] +name = "openai" +version = "1.85.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/3c/1143dc0a865d06482454fddb35d739c9260b18d721f01287f79cc53a315f/openai-1.85.0.tar.gz", hash = "sha256:6ba76e4ebc5725f71f2f6126c7cb5169ca8de60dd5aa61f350f9448ad162c913", size = 468207, upload-time = "2025-06-09T16:51:17.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/73/b4427c7873f4f778ec7a6d2b1724fd3aadc85719a12e324615b9c2bc614f/openai-1.85.0-py3-none-any.whl", hash = "sha256:7dc3e839cb8bb8747979a90c63ad4cb25a8e0cbec17b53eec009532c9965cecf", size = 730229, upload-time = "2025-06-09T16:51:15.678Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "importlib-metadata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/0d/10357006dc10fc65f7c7b46c18232e466e355f9e606ac461cfc7193b4cbe/opentelemetry_api-1.25.0.tar.gz", hash = "sha256:77c4985f62f2614e42ce77ee4c9da5fa5f0bc1e1821085e9a47533a9323ae869", size = 60383, upload-time = "2024-05-31T01:40:38.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b2/4bc5e52c9a23df0ac17dbb23923e609a8269cd67000a712b4f5bcfae1490/opentelemetry_api-1.25.0-py3-none-any.whl", hash = "sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737", size = 59910, upload-time = "2024-05-31T01:40:00.911Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/a7/85ffaaacd712e4634fa1c56cbf79a02cf90b8a178fe1eee2cabfb0b7f44d/opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3", size = 17152, upload-time = "2024-05-31T01:40:42.259Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/02/74ac6619eec78c82a923324f916d3eccd2f2254cf4270b669e96b76bf717/opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693", size = 17762, upload-time = "2024-05-31T01:40:13.172Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/d9/1c3c518853c27d323a46813d3e99d601959ca2c6963d5217fe2110f0d579/opentelemetry_exporter_otlp_proto_http-1.25.0.tar.gz", hash = "sha256:9f8723859e37c75183ea7afa73a3542f01d0fd274a5b97487ea24cb683d7d684", size = 14048, upload-time = "2024-05-31T01:40:43.749Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/b9/a47734f7c5a45619d8c64c227f119092b4679b2c49d37116fda7c0fc4573/opentelemetry_exporter_otlp_proto_http-1.25.0-py3-none-any.whl", hash = "sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6", size = 16790, upload-time = "2024-05-31T01:40:16.649Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.46b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "setuptools" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/20/0a5d980843e048e9516443a91c63a559b40e5d50a730e73e72a5bde727fd/opentelemetry_instrumentation-0.46b0.tar.gz", hash = "sha256:974e0888fb2a1e01c38fbacc9483d024bb1132aad92d6d24e2e5543887a7adda", size = 24048, upload-time = "2024-05-31T16:17:29.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/e5/d6fff0a6f6fbddf03c7fb48ab47925581c4f1a8268f9ad98e5ea4a8b90a5/opentelemetry_instrumentation-0.46b0-py3-none-any.whl", hash = "sha256:89cd721b9c18c014ca848ccd11181e6b3fd3f6c7669e35d59c48dc527408c18b", size = 29108, upload-time = "2024-05-31T16:16:14.042Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-requests" +version = "0.46b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/28/5b5e9fb74639e47f026a3fd6550bba965ca18b316a8178907540e711855c/opentelemetry_instrumentation_requests-0.46b0.tar.gz", hash = "sha256:ef0ad63bfd0d52631daaf7d687e763dbd89b465f5cb052f12a4e67e5e3d181e4", size = 13709, upload-time = "2024-05-31T16:18:08.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/42/5eee8720eccd4b94dd3d4908364785db8b22c9ae512ee47caff29064ca4c/opentelemetry_instrumentation_requests-0.46b0-py3-none-any.whl", hash = "sha256:a8c2472800d8686f3f286cd524b8746b386154092e85a791ba14110d1acc9b81", size = 12170, upload-time = "2024-05-31T16:17:07.341Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/3c/28c9ce40eb8ab287471af81659089ca98ef4f7ce289669e23b19c29f24a8/opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3", size = 35062, upload-time = "2024-05-31T01:40:52.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/ae/d6b5f11ecbffafe8b6d54130fed0cc78aad3711e00074d63a7359d6dcf3b/opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f", size = 52450, upload-time = "2024-05-31T01:40:30.987Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/3c/77076b77f1d73141adc119f62370ec9456ef314ba0b4e7072e3775c36ef7/opentelemetry_sdk-1.25.0.tar.gz", hash = "sha256:ce7fc319c57707ef5bf8b74fb9f8ebdb8bfafbe11898410e0d2a761d08a98ec7", size = 141042, upload-time = "2024-05-31T01:40:53.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/b2/729a959a8aa032bce246c791f977161099ab60fb0188408ccec1bf283b00/opentelemetry_sdk-1.25.0-py3-none-any.whl", hash = "sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9", size = 107028, upload-time = "2024-05-31T01:40:33.281Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.46b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/ea/a4a5277247b3d2ed2e23a58b0d509c2eafa4ebb56038ba5b23c0f9ea6242/opentelemetry_semantic_conventions-0.46b0.tar.gz", hash = "sha256:fbc982ecbb6a6e90869b15c1673be90bd18c8a56ff1cffc0864e38e2edffaefa", size = 80198, upload-time = "2024-05-31T01:40:54.722Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/41/28dae1ec1fe0151373f06bd06d9170ca14b52d5b3a6c2dc55f85bc219619/opentelemetry_semantic_conventions-0.46b0-py3-none-any.whl", hash = "sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07", size = 130549, upload-time = "2024-05-31T01:40:35.348Z" }, +] + +[[package]] +name = "opentelemetry-util-http" +version = "0.46b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/91/45bf243850463b2c83000ca129442255eaef7c446bd0f59a2ab54b15abff/opentelemetry_util_http-0.46b0.tar.gz", hash = "sha256:03b6e222642f9c7eae58d9132343e045b50aca9761fcb53709bd2b663571fdf6", size = 7387, upload-time = "2024-05-31T16:18:21.321Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/7f/26d3d8880ea79adde8bb7bc306b25ca5134d6f6c3006ba464716405b4729/opentelemetry_util_http-0.46b0-py3-none-any.whl", hash = "sha256:8dc1949ce63caef08db84ae977fdc1848fe6dc38e6bbaad0ae3e6ecd0d451629", size = 6920, upload-time = "2024-05-31T16:17:25.344Z" }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, +] + +[[package]] +name = "paradict" +version = "0.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/83/8cf8d94be55ab9ea783e1f8ece06059cd986bb482ad69f7be549839b9e07/paradict-0.0.16.tar.gz", hash = "sha256:d909d122bf47028a45334eb2280d1e1bcb401fda89986af42c39fd2fadf9de4d", size = 61471, upload-time = "2024-12-10T21:23:49.007Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/f9/a9807d307ba1837bb8799e1337f41edcdbb92ef6090668dc50f483a168bf/paradict-0.0.16-py3-none-any.whl", hash = "sha256:28df79f0dc0e68c8f8a3e9b7c75e67a85305ef7298653fc7a369a1bf4f58cb20", size = 61735, upload-time = "2024-12-10T21:23:45.408Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "peewee" +version = "3.18.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/ce/c2bb58d00cb12d19dea28d5a98d05a14350197a3d03eba60be9bae708bac/peewee-3.18.1.tar.gz", hash = "sha256:a76a694b3b3012ce22f00d51fd83e55bf80b595275a90ed62cd36eb45496cf1d", size = 3026130, upload-time = "2025-04-30T15:40:35.06Z" } + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, +] + +[[package]] +name = "prompt-server" +version = "0.1.0" +source = { virtual = "sre_agent/servers/prompt_server" } +dependencies = [ + { name = "fastapi" }, + { name = "mcp", extra = ["cli"] }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = ">=0.115.12" }, + { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "protobuf" +version = "4.25.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/01/34c8d2b6354906d728703cb9d546a0e534de479e25f1b581e4094c4a85cc/protobuf-4.25.8.tar.gz", hash = "sha256:6135cf8affe1fc6f76cced2641e4ea8d3e59518d1f24ae41ba97bcad82d397cd", size = 380920, upload-time = "2025-05-28T14:22:25.153Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/ff/05f34305fe6b85bbfbecbc559d423a5985605cad5eda4f47eae9e9c9c5c5/protobuf-4.25.8-cp310-abi3-win32.whl", hash = "sha256:504435d831565f7cfac9f0714440028907f1975e4bed228e58e72ecfff58a1e0", size = 392745, upload-time = "2025-05-28T14:22:10.524Z" }, + { url = "https://files.pythonhosted.org/packages/08/35/8b8a8405c564caf4ba835b1fdf554da869954712b26d8f2a98c0e434469b/protobuf-4.25.8-cp310-abi3-win_amd64.whl", hash = "sha256:bd551eb1fe1d7e92c1af1d75bdfa572eff1ab0e5bf1736716814cdccdb2360f9", size = 413736, upload-time = "2025-05-28T14:22:13.156Z" }, + { url = "https://files.pythonhosted.org/packages/28/d7/ab27049a035b258dab43445eb6ec84a26277b16105b277cbe0a7698bdc6c/protobuf-4.25.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ca809b42f4444f144f2115c4c1a747b9a404d590f18f37e9402422033e464e0f", size = 394537, upload-time = "2025-05-28T14:22:14.768Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6d/a4a198b61808dd3d1ee187082ccc21499bc949d639feb948961b48be9a7e/protobuf-4.25.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:9ad7ef62d92baf5a8654fbb88dac7fa5594cfa70fd3440488a5ca3bfc6d795a7", size = 294005, upload-time = "2025-05-28T14:22:16.052Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c6/c9deaa6e789b6fc41b88ccbdfe7a42d2b82663248b715f55aa77fbc00724/protobuf-4.25.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:83e6e54e93d2b696a92cad6e6efc924f3850f82b52e1563778dfab8b355101b0", size = 294924, upload-time = "2025-05-28T14:22:17.105Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c1/6aece0ab5209981a70cd186f164c133fdba2f51e124ff92b73de7fd24d78/protobuf-4.25.8-py3-none-any.whl", hash = "sha256:15a0af558aa3b13efef102ae6e4f3efac06f1eea11afb3a57db2901447d9fb59", size = 156757, upload-time = "2025-05-28T14:22:24.135Z" }, +] + +[[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 = "pydantic" +version = "2.11.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" }, +] + +[[package]] +name = "pytest-cov" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "questionary" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "prompt-toolkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/45/eafb0bba0f9988f6a2520f9ca2df2c82ddfa8d67c95d6625452e97b204a5/questionary-2.1.1.tar.gz", hash = "sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d", size = 25845, upload-time = "2025-08-28T19:00:20.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781, upload-time = "2024-11-06T20:10:07.07Z" }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455, upload-time = "2024-11-06T20:10:09.117Z" }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759, upload-time = "2024-11-06T20:10:11.155Z" }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976, upload-time = "2024-11-06T20:10:13.24Z" }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077, upload-time = "2024-11-06T20:10:15.37Z" }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160, upload-time = "2024-11-06T20:10:19.027Z" }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896, upload-time = "2024-11-06T20:10:21.85Z" }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997, upload-time = "2024-11-06T20:10:24.329Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725, upload-time = "2024-11-06T20:10:28.067Z" }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481, upload-time = "2024-11-06T20:10:31.612Z" }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896, upload-time = "2024-11-06T20:10:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138, upload-time = "2024-11-06T20:10:36.142Z" }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692, upload-time = "2024-11-06T20:10:38.394Z" }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135, upload-time = "2024-11-06T20:10:40.367Z" }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567, upload-time = "2024-11-06T20:10:43.467Z" }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "requests-cache" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "cattrs" }, + { name = "platformdirs" }, + { name = "requests" }, + { name = "url-normalize" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/be/7b2a95a9e7a7c3e774e43d067c51244e61dea8b120ae2deff7089a93fb2b/requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1", size = 3018209, upload-time = "2024-06-18T17:18:03.774Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/2e/8f4051119f460cfc786aa91f212165bb6e643283b533db572d7b33952bd2/requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603", size = 61425, upload-time = "2024-06-18T17:17:45Z" }, +] + +[[package]] +name = "requirements-parser" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/96/fb6dbfebb524d5601d359a47c78fe7ba1eef90fc4096404aa60c9a906fbb/requirements_parser-0.13.0.tar.gz", hash = "sha256:0843119ca2cb2331de4eb31b10d70462e39ace698fd660a915c247d2301a4418", size = 22630, upload-time = "2025-05-21T13:42:05.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/60/50fbb6ffb35f733654466f1a90d162bcbea358adc3b0871339254fbc37b2/requirements_parser-0.13.0-py3-none-any.whl", hash = "sha256:2b3173faecf19ec5501971b7222d38f04cb45bb9d87d0ad629ca71e2e62ded14", size = 14782, upload-time = "2025-05-21T13:42:04.007Z" }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647, upload-time = "2025-05-21T12:43:28.559Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454, upload-time = "2025-05-21T12:43:30.615Z" }, + { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665, upload-time = "2025-05-21T12:43:32.629Z" }, + { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873, upload-time = "2025-05-21T12:43:34.576Z" }, + { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866, upload-time = "2025-05-21T12:43:36.123Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886, upload-time = "2025-05-21T12:43:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666, upload-time = "2025-05-21T12:43:40.065Z" }, + { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109, upload-time = "2025-05-21T12:43:42.263Z" }, + { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244, upload-time = "2025-05-21T12:43:43.846Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023, upload-time = "2025-05-21T12:43:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634, upload-time = "2025-05-21T12:43:48.263Z" }, + { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713, upload-time = "2025-05-21T12:43:49.897Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280, upload-time = "2025-05-21T12:43:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399, upload-time = "2025-05-21T12:43:53.351Z" }, + { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, + { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, + { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, + { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, + { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, + { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, + { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, +] + +[[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.17.40" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/d6/eb2833ccba5ea36f8f4de4bcfa0d1a91eb618f832d430b70e3086821f251/ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d", size = 137672, upload-time = "2023-10-20T12:53:56.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/79/5e2cffa1c77432f11cd93a5351f30732c997a239d3a3090856a72d6d8ba7/ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c", size = 113666, upload-time = "2023-10-20T12:53:52.628Z" }, +] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, + { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, + { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, + { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210, upload-time = "2025-02-26T09:15:13.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917, upload-time = "2025-02-26T09:15:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419, upload-time = "2025-02-26T09:15:01.765Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493, upload-time = "2025-02-26T09:14:51.812Z" }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400, upload-time = "2025-02-26T09:14:53.549Z" }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891, upload-time = "2025-02-26T09:14:55.717Z" }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694, upload-time = "2025-02-26T09:14:57.036Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642, upload-time = "2025-02-26T09:15:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241, upload-time = "2025-02-26T09:14:58.303Z" }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001, upload-time = "2025-02-26T09:15:05.79Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013, upload-time = "2025-02-26T09:15:07.892Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687, upload-time = "2025-02-26T09:15:09.979Z" }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147, upload-time = "2025-02-26T09:15:11.185Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677, upload-time = "2025-02-26T09:15:16.554Z" }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878, upload-time = "2025-02-26T09:15:14.99Z" }, +] + +[[package]] +name = "semgrep" +version = "1.85.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "boltons" }, + { name = "click" }, + { name = "click-option-group" }, + { name = "colorama" }, + { name = "defusedxml" }, + { name = "exceptiongroup" }, + { name = "glom" }, + { name = "jsonschema" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-instrumentation-requests" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "peewee" }, + { name = "requests" }, + { name = "rich" }, + { name = "ruamel-yaml" }, + { name = "tomli" }, + { name = "typing-extensions" }, + { name = "urllib3" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/41/3e42c952458400baf0cf3f89e79dfc1d93ea636eedeb6ebaae33280e4383/semgrep-1.85.0.tar.gz", hash = "sha256:1a321cca4c5da84eb466ca1a4ceda10223e806225e371c4fef710cfe4b4b1df7", size = 27204522, upload-time = "2024-08-15T17:56:40.809Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/04/53e7df61e90f66f45e0f7d60b52d3787bdaae550c5c5a0940c40dce28036/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-any.whl", hash = "sha256:91fab3a0aa7f987a6605e01617179a363338350cca51174905d6ad0080a8d08e", size = 27619862, upload-time = "2024-08-15T17:56:24.981Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6a/0762eded629759b3b876dcd81a33a736a3ecae08a4896f21abcc4c800b3d/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_10_14_x86_64.whl", hash = "sha256:a63055b392da70c46947780f43fecf54064fb60d8de11a16902e0cc149350a3e", size = 27833827, upload-time = "2024-08-15T17:56:29.344Z" }, + { url = "https://files.pythonhosted.org/packages/97/fc/d34edce42fc2785645f64345d91b3ebd7d705c129306e0b420bd4ae0d31a/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_11_0_arm64.whl", hash = "sha256:157b7724c35a8bda972921abfaaf252bdb754bbda004b2a82aaa38ac8099e176", size = 33557689, upload-time = "2024-08-15T17:56:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/23/0e/679e6fe0b6f3e1496f01ad28a79829b832df69f19815a3891f0e9a144b35/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-musllinux_1_0_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6b5d509576bbe0d68245d9ee2973ccecb485ca5907e85a7a8793552bf622cb", size = 32246576, upload-time = "2024-08-15T17:56:36.849Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "shared" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "kvf" }, + { name = "paradict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/39/f39c2560ac971efbf437f7ffa1d82a12fa77f50b0127e6e5ec5cc8d377df/shared-0.0.32.tar.gz", hash = "sha256:7308adc95c0dab14d0c99635cd8049d1f004cc7fef7396d3fe47323c34ec58c6", size = 7793, upload-time = "2024-12-10T20:49:22.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/03/da58e40386d8ebcdfa3617070a95ca1deb5a5e6aa3d4e15ea2045173d5ac/shared-0.0.32-py3-none-any.whl", hash = "sha256:f17962c0f0fe6a23015accc7cac029e1c24c4b14578094e1f7033a7a7ef16140", size = 29304, upload-time = "2024-12-10T20:49:19.763Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sre-agent" +version = "0.0.1" +source = { editable = "." } +dependencies = [ + { name = "click" }, + { name = "google-genai" }, + { name = "httpx" }, + { name = "prompt-toolkit" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "questionary" }, + { name = "rich" }, + { name = "types-requests" }, +] + +[package.dev-dependencies] +ci = [ + { name = "anthropic" }, + { name = "fastapi" }, + { name = "llamafirewall" }, + { name = "mcp" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "shared" }, + { name = "transformers" }, + { name = "types-requests" }, +] +dev = [ + { name = "licensecheck" }, + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "click", specifier = ">=8.0.0" }, + { name = "google-genai", specifier = ">=1.19.0" }, + { name = "httpx", specifier = ">=0.25.0" }, + { name = "prompt-toolkit", specifier = ">=3.0.52" }, + { name = "pydantic-settings", specifier = ">=2.9.1" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "questionary", specifier = ">=2.0.0" }, + { name = "rich", specifier = ">=13.0.0" }, + { name = "types-requests", specifier = ">=2.32.0.20250602" }, +] + +[package.metadata.requires-dev] +ci = [ + { name = "anthropic", specifier = ">=0.49.0" }, + { name = "fastapi", specifier = ">=0.115.12" }, + { name = "llamafirewall", specifier = ">=1.0.2" }, + { name = "mcp", specifier = ">=1.6.0" }, + { name = "pydantic", specifier = ">=2.11.3" }, + { name = "pydantic-settings", specifier = ">=2.9.1" }, + { name = "python-dotenv", specifier = ">=1.1.0" }, + { name = "shared" }, + { name = "transformers", specifier = ">=4.51.3" }, + { name = "types-requests", specifier = ">=2.32.0.20250328" }, +] +dev = [ + { name = "licensecheck", specifier = ">=2024.1.2" }, + { name = "mypy", specifier = ">=1.15.0" }, + { name = "pre-commit", specifier = ">=4.2.0" }, + { name = "pytest", specifier = ">=7.2.0" }, + { name = "pytest-cov", specifier = ">=4.0.0" }, +] + +[[package]] +name = "sse-starlette" +version = "2.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" }, +] + +[[package]] +name = "starlette" +version = "0.46.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload-time = "2025-04-13T13:56:17.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tenacity" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78", size = 47309, upload-time = "2024-07-05T07:25:31.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256, upload-time = "2025-03-13T10:51:18.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767, upload-time = "2025-03-13T10:51:09.459Z" }, + { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555, upload-time = "2025-03-13T10:51:07.692Z" }, + { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541, upload-time = "2025-03-13T10:50:56.679Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058, upload-time = "2025-03-13T10:50:59.525Z" }, + { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278, upload-time = "2025-03-13T10:51:04.678Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253, upload-time = "2025-03-13T10:51:01.261Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225, upload-time = "2025-03-13T10:51:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874, upload-time = "2025-03-13T10:51:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448, upload-time = "2025-03-13T10:51:10.927Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877, upload-time = "2025-03-13T10:51:12.688Z" }, + { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645, upload-time = "2025-03-13T10:51:14.723Z" }, + { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380, upload-time = "2025-03-13T10:51:16.526Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506, upload-time = "2025-03-13T10:51:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481, upload-time = "2025-03-13T10:51:19.243Z" }, +] + +[[package]] +name = "tomli" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, +] + +[[package]] +name = "torch" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/93/fb505a5022a2e908d81fe9a5e0aa84c86c0d5f408173be71c6018836f34e/torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa", size = 98948276, upload-time = "2025-06-04T17:39:12.852Z" }, + { url = "https://files.pythonhosted.org/packages/56/7e/67c3fe2b8c33f40af06326a3d6ae7776b3e3a01daa8f71d125d78594d874/torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc", size = 821025792, upload-time = "2025-06-04T17:34:58.747Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/a37495502bc7a23bf34f89584fa5a78e25bae7b8da513bc1b8f97afb7009/torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b", size = 216050349, upload-time = "2025-06-04T17:38:59.709Z" }, + { url = "https://files.pythonhosted.org/packages/3a/60/04b77281c730bb13460628e518c52721257814ac6c298acd25757f6a175c/torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb", size = 68645146, upload-time = "2025-06-04T17:38:52.97Z" }, + { url = "https://files.pythonhosted.org/packages/66/81/e48c9edb655ee8eb8c2a6026abdb6f8d2146abd1f150979ede807bb75dcb/torch-2.7.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:03563603d931e70722dce0e11999d53aa80a375a3d78e6b39b9f6805ea0a8d28", size = 98946649, upload-time = "2025-06-04T17:38:43.031Z" }, + { url = "https://files.pythonhosted.org/packages/3a/24/efe2f520d75274fc06b695c616415a1e8a1021d87a13c68ff9dce733d088/torch-2.7.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d632f5417b6980f61404a125b999ca6ebd0b8b4bbdbb5fbbba44374ab619a412", size = 821033192, upload-time = "2025-06-04T17:38:09.146Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d9/9c24d230333ff4e9b6807274f6f8d52a864210b52ec794c5def7925f4495/torch-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:23660443e13995ee93e3d844786701ea4ca69f337027b05182f5ba053ce43b38", size = 216055668, upload-time = "2025-06-04T17:38:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/95/bf/e086ee36ddcef9299f6e708d3b6c8487c1651787bb9ee2939eb2a7f74911/torch-2.7.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0da4f4dba9f65d0d203794e619fe7ca3247a55ffdcbd17ae8fb83c8b2dc9b585", size = 68925988, upload-time = "2025-06-04T17:38:29.273Z" }, + { url = "https://files.pythonhosted.org/packages/69/6a/67090dcfe1cf9048448b31555af6efb149f7afa0a310a366adbdada32105/torch-2.7.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e08d7e6f21a617fe38eeb46dd2213ded43f27c072e9165dc27300c9ef9570934", size = 99028857, upload-time = "2025-06-04T17:37:50.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/1c/48b988870823d1cc381f15ec4e70ed3d65e043f43f919329b0045ae83529/torch-2.7.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:30207f672328a42df4f2174b8f426f354b2baa0b7cca3a0adb3d6ab5daf00dc8", size = 821098066, upload-time = "2025-06-04T17:37:33.939Z" }, + { url = "https://files.pythonhosted.org/packages/7b/eb/10050d61c9d5140c5dc04a89ed3257ef1a6b93e49dd91b95363d757071e0/torch-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:79042feca1c634aaf6603fe6feea8c6b30dfa140a6bbc0b973e2260c7e79a22e", size = 216336310, upload-time = "2025-06-04T17:36:09.862Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/beb45cdf5c4fc3ebe282bf5eafc8dfd925ead7299b3c97491900fe5ed844/torch-2.7.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:988b0cbc4333618a1056d2ebad9eb10089637b659eb645434d0809d8d937b946", size = 68645708, upload-time = "2025-06-04T17:34:39.852Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "transformers" +version = "4.52.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/a9/275037087f9d846580b02f2d7cae0e0a6955d46f84583d0151d6227bd416/transformers-4.52.4.tar.gz", hash = "sha256:aff3764441c1adc192a08dba49740d3cbbcb72d850586075aed6bd89b98203e6", size = 8945376, upload-time = "2025-05-30T09:17:17.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/f2/25b27b396af03d5b64e61976b14f7209e2939e9e806c10749b6d277c273e/transformers-4.52.4-py3-none-any.whl", hash = "sha256:203f5c19416d5877e36e88633943761719538a25d9775977a24fe77a1e5adfc7", size = 10460375, upload-time = "2025-05-30T09:17:14.477Z" }, +] + +[[package]] +name = "triton" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43", size = 155669138, upload-time = "2025-05-29T23:39:51.771Z" }, + { url = "https://files.pythonhosted.org/packages/74/1f/dfb531f90a2d367d914adfee771babbd3f1a5b26c3f5fbc458dee21daa78/triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240", size = 155673035, upload-time = "2025-05-29T23:40:02.468Z" }, + { url = "https://files.pythonhosted.org/packages/28/71/bd20ffcb7a64c753dc2463489a61bf69d531f308e390ad06390268c4ea04/triton-3.3.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3198adb9d78b77818a5388bff89fa72ff36f9da0bc689db2f0a651a67ce6a42", size = 155735832, upload-time = "2025-05-29T23:40:10.522Z" }, +] + +[[package]] +name = "typer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, +] + +[[package]] +name = "types-requests" +version = "2.32.0.20250602" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "url-normalize" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/31/febb777441e5fcdaacb4522316bf2a527c44551430a4873b052d545e3279/url_normalize-2.2.1.tar.gz", hash = "sha256:74a540a3b6eba1d95bdc610c24f2c0141639f3ba903501e61a52a8730247ff37", size = 18846, upload-time = "2025-04-26T20:37:58.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/d9/5ec15501b675f7bc07c5d16aa70d8d778b12375686b6efd47656efdc67cd/url_normalize-2.2.1-py3-none-any.whl", hash = "sha256:3deb687587dc91f7b25c9ae5162ffc0f057ae85d22b1e15cf5698311247f567b", size = 14728, upload-time = "2025-04-26T20:37:57.217Z" }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, +] + +[[package]] +name = "uv" +version = "0.7.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/35/360a4aa325254b7f11d0898d30588861428659011b34f1e19c40fdd15db6/uv-0.7.12.tar.gz", hash = "sha256:4aa152e6a70d5662ca66a918f697bf8fb710f391068aa7d04e032af2edebb095", size = 3298683, upload-time = "2025-06-06T20:39:04.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/64/ee9f1b27f006c49a6765e9655ab93e7c8cbd6f0bf8b731f30f608b0be9fd/uv-0.7.12-py3-none-linux_armv6l.whl", hash = "sha256:81824caf5756ffee54b4c937d92d7c8c224c416270c90a83b9b4a973f6e4e559", size = 17024991, upload-time = "2025-06-06T20:38:17.053Z" }, + { url = "https://files.pythonhosted.org/packages/43/aa/f42707faa13a9c1b4f662456b2dca4bde169eb921f135319d8856c6e5e8e/uv-0.7.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:02e67c5f9d141fb25976cddb28abceaf715412ed83070cb9b87c5c488c8451af", size = 17097383, upload-time = "2025-06-06T20:38:21.174Z" }, + { url = "https://files.pythonhosted.org/packages/b9/a9/0f27e16e161f98240a328b5201b8abf178b751fde4fc56c54c1321812cd5/uv-0.7.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e70a4393fd6a09b056e1ac500fe2b796d26c30783194868c6801ea08c3bbf863", size = 15812649, upload-time = "2025-06-06T20:38:23.51Z" }, + { url = "https://files.pythonhosted.org/packages/0b/eb/605d8f1d08606024209d0e31c3799c696199a887260ee1db52663e4da2e8/uv-0.7.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bb47326b9c4802db28e11f1aab174d5c9c0a8b26ed0a83094d3882dd8f5049ad", size = 16344497, upload-time = "2025-06-06T20:38:25.899Z" }, + { url = "https://files.pythonhosted.org/packages/b7/86/3503eb869fa17d607cc296a6514db52ec73c2ec85ad608952a207fd2e8ff/uv-0.7.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14214a51e0ae0f0e8dbcac35a29722c45dbf40d0fd37309897642f7989af6caf", size = 16773525, upload-time = "2025-06-06T20:38:28.619Z" }, + { url = "https://files.pythonhosted.org/packages/9b/d6/868fb3f0b9f2a0d2f14cb8079171b862adbd782e47e0469dad3d3d71c938/uv-0.7.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fa630d865111c26f26c5e6f4547a73b13284f098471a4ca982d7b0caf0e658b", size = 17551173, upload-time = "2025-06-06T20:38:31.166Z" }, + { url = "https://files.pythonhosted.org/packages/d4/a8/b5be1c67c7894caf178e850903ac25f465e3508a6eada2ae735b187dc39d/uv-0.7.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1557a154d2c36030ff0b707f3c2bfafd977e54fcd4d628dd0fa8a265449e9f13", size = 18359491, upload-time = "2025-06-06T20:38:33.569Z" }, + { url = "https://files.pythonhosted.org/packages/95/23/f62bab13f67ed785f7ad01546c499809d1db71b03f94950380f0bc407625/uv-0.7.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e0ba7767b21d58d65703c3cd43814ccfe06d7664ac42b3589d5f2b72486b903", size = 18098855, upload-time = "2025-06-06T20:38:36.029Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4a/db21a5d3839771799af2df366cc5ed0933ebe9fc9e920f212e33dc00136e/uv-0.7.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0672dc5dc1b0ae7191d11ecae8bb794c7e860936b66c2bc3855bd0dee17fca1", size = 18206282, upload-time = "2025-06-06T20:38:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ae/fcfd916cbc109c5626dc25b208395b47ba12b27af82f3bb8e247b4e95692/uv-0.7.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34b4ad4288828210c2e075934009903514ca97bd603aced7d0755040b4d0489", size = 17777690, upload-time = "2025-06-06T20:38:41.021Z" }, + { url = "https://files.pythonhosted.org/packages/92/78/608163b35ffaf1054cd10197646b6336e7be7b6a51dfef6d98a91600c6be/uv-0.7.12-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:8a7ed9e94ec409bfc7181ee274d1b0ed6292698a20df0ae035ce422224863af5", size = 16599406, upload-time = "2025-06-06T20:38:43.72Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d6/6fe3b16390472a9d31dd1e0e7e3759b884d71e8a0dff1baf4a753b4adaaa/uv-0.7.12-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:85e8d3dea95016a45ed8c48343f98734d1b5c4be7bba26257d4c8873059646fa", size = 16714823, upload-time = "2025-06-06T20:38:45.949Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a5/b0432a25eaa23e9f909649321784b8e4be4579e9957eb5d369aa30c79164/uv-0.7.12-py3-none-musllinux_1_1_i686.whl", hash = "sha256:01310c45d55f6e7580124c9b1f7e3586b9609c4f8e5a78558a75951b03541bb2", size = 17086446, upload-time = "2025-06-06T20:38:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/da/d8/673591f34f897aa4216144a513e60c2004399155c47e7b550612960359c6/uv-0.7.12-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:4c697ef9d9f6b6f42df5a661efa8a745c0e4c330039d45b549b2ca7e7b66f8a5", size = 17903789, upload-time = "2025-06-06T20:38:51.864Z" }, + { url = "https://files.pythonhosted.org/packages/15/09/e476187c0a1da78b9c2021f3c3ab31ed2469a70d222bde5dc892236b3c4f/uv-0.7.12-py3-none-win32.whl", hash = "sha256:6008abf92c8d37060944377d89bf9f514aa18370391d9d63dc7d449dac94aca1", size = 17344011, upload-time = "2025-06-06T20:38:54.276Z" }, + { url = "https://files.pythonhosted.org/packages/08/9e/c52c7f50280e57110ca79b6805877f50514d9a777d31a683a4eb1de52312/uv-0.7.12-py3-none-win_amd64.whl", hash = "sha256:bb57bd26becd86194788f832af373b6ba431314fa0f6f7e904c90cac1818a7dc", size = 18803328, upload-time = "2025-06-06T20:38:59.368Z" }, + { url = "https://files.pythonhosted.org/packages/8e/35/4800ff7bc1663d9f967eabc8440074f906c8a98ea28d1aae66d2d19b7ae9/uv-0.7.12-py3-none-win_arm64.whl", hash = "sha256:8aba24e12ded2f2974a2f213e55daabf78002613d3772c1396fc924c6682cd27", size = 17450522, upload-time = "2025-06-06T20:39:01.963Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.34.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.31.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, +] + +[[package]] +name = "wcmatch" +version = "8.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/c4/55e0d36da61d7b8b2a49fd273e6b296fd5e8471c72ebbe438635d1af3968/wcmatch-8.5.2.tar.gz", hash = "sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2", size = 114983, upload-time = "2024-05-15T12:51:08.054Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/78/533ef890536e5ba0fd4f7df37482b5800ecaaceae9afc30978a1a7f88ff1/wcmatch-8.5.2-py3-none-any.whl", hash = "sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478", size = 39397, upload-time = "2024-05-15T12:51:06.2Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..9b38021b --- /dev/null +++ b/mypy.ini @@ -0,0 +1,4 @@ +[mypy] +exclude = ["legacy/", "docs/", "tests/", "LICENSE"] +strict = true +ignore_missing_imports = true diff --git a/pyproject.toml b/pyproject.toml index de525334..9ff27c18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,168 +1,39 @@ [project] name = "sre-agent" -version = "0.1.0" +version = "0.2.0" description = "A Site Reliability Engineer AI agent that can monitor application and infrastructure logs, diagnose issues, and report on diagnostics." authors = [{ name = "Fuzzy Labs", email = "info@fuzzylabs.ai" }] readme = "README.md" -requires-python = ">=3.12,<4.0" +requires-python = ">=3.13,<4.0" license = { text = "MIT" } -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.12", -] dependencies = [ - "google-genai>=1.19.0", - "click>=8.0.0", - "rich>=13.0.0", - "httpx>=0.25.0", - "pydantic-settings>=2.9.1", - "types-requests>=2.32.0.20250602", - "prompt-toolkit>=3.0.52", - "python-dotenv>=1.0.0", - "questionary>=2.0.0", + "boto3>=1.42.39", + "click>=8.3.1", + "fastapi>=0.128.0", + "platformdirs>=4.5.1", + "pydantic-ai>=1.51.0", + "pydantic-settings>=2.12.0", + "python-dotenv>=1.2.1", + "questionary>=2.1.1", + "rich>=14.3.2", + "uvicorn>=0.40.0", ] [project.scripts] sre-agent = "sre_agent.cli.main:main" [dependency-groups] -ci = [ - "anthropic>=0.49.0", - "fastapi>=0.115.12", - "mcp>=1.6.0", - "pydantic>=2.11.3", - "pydantic-settings>=2.9.1", - "python-dotenv>=1.1.0", - "types-requests>=2.32.0.20250328", - "llamafirewall>=1.0.2", - "shared", - "transformers>=4.51.3", -] dev = [ - "pytest>=7.2.0", - "pytest-cov>=4.0.0", - "licensecheck>=2024.1.2", - "mypy>=1.15.0", - "pre-commit>=4.2.0", + "mypy>=1.19.1", ] [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = ["uv_build>=0.9.28,<0.10.0"] +build-backend = "uv_build" -[tool.hatchling.build.targets.wheel] -packages = ["sre_agent"] -include = [ - "sre_agent/compose.*.yaml", -] +[tool.hatch.build.targets.wheel] +packages = ["src/sre_agent"] [tool.pytest.ini_options] addopts = "--cov=sre_agent --cov-report term-missing" testpaths = ["tests"] - -# mypy configuration -[tool.mypy] -show_error_codes = true -exclude = ["docs", "tests", "LICENSE"] -strict = true -namespace_packages = true - -# black configuration -[tool.black] -line-length = 100 -include = '\.pyi?$' -exclude = ''' -/( - \.git -| \.hg -| \.mypy_cache -| \.tox -| \.venv -| _build -| buck-out -| build -)/ -''' - -[tool.ruff] -target-version = "py312" - - -# Match black. Note that this also checks comment line length, but black does not format comments. -line-length = 100 - -show-fixes = true - -[tool.ruff.lint] -ignore-init-module-imports = true -select = [ - "C4", # flake8-comprehensions - "SIM", # flake8-simplify - "Q", # flake8-quotes - "ISC", # flake8-implicit-str-concat - "F", # pyflakes - "D", # pydocstyle - "E", # pycodestyle error - "W", # pycodestyle warning - "N", # pep8-naming - "I", # isort - "PL", # pylint rules from categories "Convention", "Error", and "Warning" - "PLE", # ruff currently implements only a subset of pylint's rules - "PLW", # pylint warning - "PLR", # pylint refactor - "UP", # pyupgrade - "C", # Complexity (mccabe+) & comprehensions -] -ignore = [ - "UP006", # See https://github.com/bokeh/bokeh/issues/13143 - "UP007", # See https://github.com/bokeh/bokeh/pull/13144 -] - -[tool.ruff.lint.pydocstyle] -# Use Google-style docstrings. -convention = "google" - -[tool.ruff.lint.mccabe] -# Flag errors (`C901`) whenever the complexity level exceeds 10. -max-complexity = 10 - - -# typos configuration -[tool.typos.files] -extend-exclude = [ - ".gitignore", - "LICENSE", - ".*", - "*servers*", - "*values-secrets.yaml", -] - -[tool.typos.default.extend-words] -center = "center" -Initialize = "Initialize" -initialize = "initialize" -Initialized = "Initialized" -Authorization = "Authorization" -EC = "EC" - -[tool.typos.default] -locale = "en-gb" - -# Bandit configuration -[tool.bandit] -exclude_dirs = [] -skips = ["B104"] - -[tool.bandit.assert_used] -skips = ['*test.py', '*/test_*.py'] - -[tool.uv.workspace] -members = [ - "sre_agent/llm", - "sre_agent/client", - "sre_agent/servers/prompt_server", - "sre_agent/firewall", -] diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..d9e7842f --- /dev/null +++ b/ruff.toml @@ -0,0 +1,71 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + "legacy", +] + +# Same as Black. +line-length = 100 +indent-width = 4 + +target-version = "py312" +force-exclude = true + +[lint] +select = [ + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "Q", # flake8-quotes + "ISC", # flake8-implicit-str-concat + "F", # pyflakes + "D", # pydocstyle + "E", # pycodestyle error + "W", # pycodestyle warning + "N", # pep8-naming + "I", # isort + "PL", # pylint rules from categories "Convention", "Error", and "Warning" + "PLE", # ruff currently implements only a subset of pylint's rules + "PLW", # pylint warning + "PLR", # pylint refactor + "UP", # pyupgrade + "C", # Complexity (mccabe+) & comprehensions +] +ignore = [ + "UP006", # See https://github.com/bokeh/bokeh/issues/13143 + "UP007", # See https://github.com/bokeh/bokeh/pull/13144 + "PLC0415", # Allow imports inside functions (useful for optional deps) + "PLR2004", # Allow magic values in comparisons (array indices etc.) +] + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +[lint.pydocstyle] +# Use Google-style docstrings. +convention = "google" diff --git a/run.py b/run.py new file mode 100644 index 00000000..bc0b15fd --- /dev/null +++ b/run.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Run the SRE Agent to diagnose errors.""" + +import asyncio +import logging +import sys +from pathlib import Path + +from dotenv import load_dotenv + +load_dotenv(Path(__file__).parent / ".env") + +# Configure logging to see tool calls and agent thoughts +logging.basicConfig(level=logging.INFO) +# Set pydantic_ai to INFO to see agent activity +logging.getLogger("pydantic_ai").setLevel(logging.INFO) + +from sre_agent import diagnose_error + + +async def main() -> None: + """Run the SRE Agent.""" + if len(sys.argv) < 3: + print("Usage: python run.py [time_range_minutes]") + print("Example: python run.py /aws/fluentbit/logs cartservice 10") + sys.exit(1) + + log_group = sys.argv[1] + service_name = sys.argv[2] + time_range_minutes = int(sys.argv[3]) if len(sys.argv) > 3 else 10 + + print(f"🔍 Diagnosing errors in {log_group}") + print(f" Service: {service_name}") + print(f" Time range: last {time_range_minutes} minutes") + print("-" * 60) + + try: + result = await diagnose_error( + log_group=log_group, + service_name=service_name, + time_range_minutes=time_range_minutes, + ) + + print("-" * 60) + print("📋 DIAGNOSIS RESULT") + print("-" * 60) + print(f"\nSummary: {result.summary}") + print(f"\nRoot Cause: {result.root_cause}") + + if result.suggested_fixes: + print("\nSuggested Fixes:") + for fix in result.suggested_fixes: + print(f" • {fix.description}") + except Exception as e: + print(f"\n❌ FATAL ERROR: {e}") + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/src/sre_agent/__init__.py b/src/sre_agent/__init__.py new file mode 100644 index 00000000..cce880c4 --- /dev/null +++ b/src/sre_agent/__init__.py @@ -0,0 +1,15 @@ +"""Public API for the SRE Agent.""" + +from sre_agent.core.agent import create_sre_agent, diagnose_error +from sre_agent.core.config import AgentConfig, get_config +from sre_agent.core.models import ErrorDiagnosis, LogEntry, LogQueryResult + +__all__ = [ + "create_sre_agent", + "diagnose_error", + "AgentConfig", + "get_config", + "ErrorDiagnosis", + "LogEntry", + "LogQueryResult", +] diff --git a/src/sre_agent/cli/__init__.py b/src/sre_agent/cli/__init__.py new file mode 100644 index 00000000..ec1ebe60 --- /dev/null +++ b/src/sre_agent/cli/__init__.py @@ -0,0 +1 @@ +"""CLI package for the SRE Agent.""" diff --git a/src/sre_agent/cli/ascii_art.py b/src/sre_agent/cli/ascii_art.py new file mode 100644 index 00000000..f9a179d4 --- /dev/null +++ b/src/sre_agent/cli/ascii_art.py @@ -0,0 +1,17 @@ +"""ASCII art for the CLI.""" + + +def get_ascii_art() -> str: + """Return the SRE Agent ASCII art. + + Returns: + The ASCII art string. + """ + return """ +███████╗██████╗ ███████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗ +██╔════╝██╔══██╗██╔════╝ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝ +███████╗██████╔╝█████╗ ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ +╚════██║██╔══██╗██╔══╝ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ +███████║██║ ██║███████╗ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ +╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ +""" diff --git a/src/sre_agent/cli/banner.py b/src/sre_agent/cli/banner.py new file mode 100644 index 00000000..24457374 --- /dev/null +++ b/src/sre_agent/cli/banner.py @@ -0,0 +1,48 @@ +"""CLI banner rendering.""" + +from importlib.metadata import PackageNotFoundError, version + +from rich.panel import Panel +from rich.text import Text + +from sre_agent.cli.ascii_art import get_ascii_art +from sre_agent.cli.ui import console + + +def print_global_banner() -> None: + """Print the main CLI banner.""" + ascii_art = get_ascii_art().strip("\n") + banner_text = Text() + colours = ["orange1", "dark_orange", "orange3", "orange1"] + banner_text.append("\n") + for index, line in enumerate(ascii_art.splitlines()): + if not line.strip(): + banner_text.append("\n") + continue + banner_text.append(f"{line}\n", style=colours[index % len(colours)]) + + banner_text.append( + "\nYour AI-powered Site Reliability Engineering assistant\n", + style="bright_white", + ) + banner_text.append("Diagnose • Monitor • Debug • Scale\n", style="dim white") + banner_text.append(f"v{_get_version()}\n", style="orange1") + console.print( + Panel( + banner_text, + title="Welcome to SRE Agent", + border_style="cyan", + ) + ) + + +def _get_version() -> str: + """Return the CLI version. + + Returns: + The CLI version string. + """ + try: + return version("sre-agent") + except PackageNotFoundError: + return "0.2.0" diff --git a/src/sre_agent/cli/config.py b/src/sre_agent/cli/config.py new file mode 100644 index 00000000..73fcb9af --- /dev/null +++ b/src/sre_agent/cli/config.py @@ -0,0 +1,136 @@ +"""CLI configuration helpers.""" + +import json +from dataclasses import asdict, dataclass, field, fields +from pathlib import Path +from typing import Any + +from platformdirs import user_config_dir + +APP_NAME = "sre-agent" +CONFIG_FILENAME = "config.json" + + +class ConfigError(RuntimeError): + """Configuration related errors.""" + + +@dataclass +class CliConfig: + """CLI configuration for ECS deployment.""" + + aws_region: str = "eu-west-2" + aws_profile: str | None = None + + project_name: str = "sre-agent" + cluster_name: str = "sre-agent" + task_family: str = "sre-agent" + task_cpu: int = 512 + task_memory: int = 1024 + image_tag: str = "latest" + + vpc_id: str | None = None + private_subnet_ids: list[str] = field(default_factory=list) + security_group_id: str | None = None + + ecr_repo_sre_agent: str = "sre-agent" + ecr_repo_slack_mcp: str = "sre-agent-slack-mcp" + + secret_anthropic_name: str = "sre-agent/anthropic_api_key" + secret_slack_bot_name: str = "sre-agent/slack_bot_token" + secret_github_token_name: str = "sre-agent/github_token" + secret_anthropic_arn: str | None = None + secret_slack_bot_arn: str | None = None + secret_github_token_arn: str | None = None + + exec_role_arn: str | None = None + task_role_arn: str | None = None + + ecr_sre_agent_uri: str | None = None + ecr_slack_mcp_uri: str | None = None + + task_definition_arn: str | None = None + cluster_arn: str | None = None + + model: str = "claude-sonnet-4-5-20250929" + slack_channel_id: str | None = None + github_mcp_url: str = "https://api.githubcopilot.com/mcp/" + log_group_name: str = "/ecs/sre-agent" + + slack_mcp_host: str = "127.0.0.1" + slack_mcp_port: int = 13080 + + +def config_dir() -> Path: + """Return the user configuration directory. + + Returns: + The user configuration directory path. + """ + return Path(user_config_dir(APP_NAME)) + + +def config_path() -> Path: + """Return the configuration file path. + + Returns: + The configuration file path. + """ + return config_dir() / CONFIG_FILENAME + + +def load_config() -> CliConfig: + """Load CLI configuration from disk. + + Returns: + The loaded configuration object. + """ + path = config_path() + if not path.exists(): + return CliConfig() + + try: + data = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + raise ConfigError(f"Invalid configuration file: {exc}") from exc + + if not isinstance(data, dict): + raise ConfigError("Configuration file must contain a JSON object.") + + return _config_from_dict(data) + + +def save_config(config: CliConfig) -> Path: + """Save CLI configuration to disk. + + Args: + config: Configuration object to save. + + Returns: + The saved configuration file path. + """ + path = config_path() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(asdict(config), indent=2), encoding="utf-8") + return path + + +def _config_from_dict(data: dict[str, Any]) -> CliConfig: + """Build a CLI configuration from a dictionary. + + Args: + data: Dictionary containing configuration data. + + Returns: + The configuration object. + """ + allowed_fields = {field.name for field in fields(CliConfig)} + filtered: dict[str, Any] = {key: value for key, value in data.items() if key in allowed_fields} + + subnet_ids = filtered.get("private_subnet_ids") + if isinstance(subnet_ids, str): + filtered["private_subnet_ids"] = [ + item.strip() for item in subnet_ids.split(",") if item.strip() + ] + + return CliConfig(**filtered) diff --git a/src/sre_agent/cli/configuration.py b/src/sre_agent/cli/configuration.py new file mode 100644 index 00000000..634749cf --- /dev/null +++ b/src/sre_agent/cli/configuration.py @@ -0,0 +1,262 @@ +"""Configuration setup for CLI runs.""" + +import os +import re +from pathlib import Path + +import questionary + +from sre_agent.cli.config import CliConfig, load_config, save_config +from sre_agent.cli.mode.paths import project_root +from sre_agent.cli.ui import console + + +def ensure_required_config() -> CliConfig: + """Ensure the required configuration is present. + + Returns: + The configuration object. + """ + config = load_config() + env_values = _load_env_values() + missing = _find_missing_config(env_values, config) + + if not missing: + console.print("[green]Configuration detected.[/green]") + reuse = questionary.confirm("Reuse existing configuration?", default=True).ask() + if reuse: + return config + console.print("[dim]Reconfiguring all settings.[/dim]") + return _run_config_wizard(config, env_values, force_reconfigure=True) + + console.print("[yellow]Configuration missing for:[/yellow]") + for item in missing: + console.print(f"- {item}") + + configure = questionary.confirm("Configure now?", default=True).ask() + if not configure: + console.print("[dim]Skipping configuration. Some features may fail.[/dim]") + return config + + return _run_config_wizard(config, env_values, force_reconfigure=False) + + +def _run_config_wizard( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, +) -> CliConfig: + """Prompt for required configuration and save it to .env. + + Args: + config: Existing configuration values. + env_values: Current environment values. + force_reconfigure: Whether to ignore existing values. + + Returns: + The updated configuration object. + """ + env_path = project_root() / ".env" + updates: dict[str, str] = {} + + updates["ANTHROPIC_API_KEY"] = _prompt_secret( + "Anthropic API key:", + env_values.get("ANTHROPIC_API_KEY"), + force_reconfigure, + ) + updates["SLACK_BOT_TOKEN"] = _prompt_secret( + "Slack bot token:", + env_values.get("SLACK_BOT_TOKEN"), + force_reconfigure, + ) + slack_channel_id = _prompt_text( + "Slack channel ID:", + env_values.get("SLACK_CHANNEL_ID"), + force_reconfigure, + ) + updates["SLACK_CHANNEL_ID"] = slack_channel_id + updates["GITHUB_PERSONAL_ACCESS_TOKEN"] = _prompt_secret( + "GitHub token:", + env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"), + force_reconfigure, + ) + + use_profile = questionary.confirm( + "Use AWS_PROFILE instead of access keys?", + default=bool(env_values.get("AWS_PROFILE")), + ).ask() + if use_profile: + updates["AWS_PROFILE"] = _prompt_text( + "AWS_PROFILE:", + env_values.get("AWS_PROFILE"), + force_reconfigure, + ) + else: + updates["AWS_ACCESS_KEY_ID"] = _prompt_text( + "AWS access key ID:", + env_values.get("AWS_ACCESS_KEY_ID"), + force_reconfigure, + ) + updates["AWS_SECRET_ACCESS_KEY"] = _prompt_secret( + "AWS secret access key:", + env_values.get("AWS_SECRET_ACCESS_KEY"), + force_reconfigure, + ) + session_token = questionary.password("AWS session token (optional):").ask() + if session_token: + updates["AWS_SESSION_TOKEN"] = session_token + + updates["AWS_REGION"] = _prompt_text( + "AWS region:", + env_values.get("AWS_REGION", config.aws_region), + force_reconfigure, + ) + + _write_env_file(env_path, updates) + + config.slack_channel_id = slack_channel_id + config.aws_region = updates["AWS_REGION"] + save_config(config) + + console.print(f"[green]Saved configuration to {env_path}[/green]") + return config + + +def _find_missing_config(env_values: dict[str, str], config: CliConfig) -> list[str]: + """Return a list of missing configuration items. + + Args: + env_values: Current environment values. + config: Existing configuration values. + + Returns: + A list of missing configuration labels. + """ + missing = [] + if not env_values.get("ANTHROPIC_API_KEY"): + missing.append("Anthropic API key") + if not env_values.get("SLACK_BOT_TOKEN"): + missing.append("Slack bot token") + if not env_values.get("SLACK_CHANNEL_ID") and not config.slack_channel_id: + missing.append("Slack channel ID") + if not env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"): + missing.append("GitHub token") + + has_profile = bool(env_values.get("AWS_PROFILE") or config.aws_profile) + has_keys = bool(env_values.get("AWS_ACCESS_KEY_ID") and env_values.get("AWS_SECRET_ACCESS_KEY")) + if not (has_profile or has_keys): + missing.append("AWS credentials (AWS_PROFILE or access keys)") + + if not env_values.get("AWS_REGION") and not config.aws_region: + missing.append("AWS region") + + return missing + + +def _prompt_secret(label: str, current: str | None, force_reconfigure: bool) -> str: + """Prompt for a secret value. + + Args: + label: Prompt label for the value. + current: Current value if already set. + force_reconfigure: Whether to ignore existing values. + + Returns: + The selected secret value. + """ + if current and not force_reconfigure: + use_existing = questionary.confirm(f"{label} already set. Keep it?", default=True).ask() + if use_existing: + return current + value: str | None = questionary.password(label).ask() + if not value: + console.print("[yellow]Value required.[/yellow]") + return _prompt_secret(label, current, force_reconfigure) + return value + + +def _prompt_text(label: str, current: str | None, force_reconfigure: bool) -> str: + """Prompt for a text value. + + Args: + label: Prompt label for the value. + current: Current value if already set. + force_reconfigure: Whether to ignore existing values. + + Returns: + The selected text value. + """ + default = "" if force_reconfigure else (current or "") + value: str | None = questionary.text(label, default=default).ask() + if not value: + console.print("[yellow]Value required.[/yellow]") + return _prompt_text(label, current, force_reconfigure) + return value + + +def _load_env_values() -> dict[str, str]: + """Load .env values and overlay environment variables. + + Returns: + Combined .env and environment variable values. + """ + env_path = project_root() / ".env" + values = _read_env_file(env_path) + for key, value in os.environ.items(): + if value: + values[key] = value + return values + + +def _read_env_file(path: Path) -> dict[str, str]: + """Read simple key/value pairs from a .env file. + + Args: + path: Path to the .env file. + + Returns: + Parsed key/value pairs. + """ + if not path.exists(): + return {} + + values: dict[str, str] = {} + for raw_line in path.read_text(encoding="utf-8").splitlines(): + line = raw_line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + values[key.strip()] = value.strip().strip("\"'") + return values + + +def _write_env_file(path: Path, updates: dict[str, str]) -> None: + """Write updates to the .env file. + + Args: + path: Path to the .env file. + updates: Values to write into the file. + """ + current = _read_env_file(path) + current.update({key: value for key, value in updates.items() if value}) + + lines = [] + for key, value in current.items(): + safe_value = _escape_env_value(value) + lines.append(f"{key}={safe_value}") + + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def _escape_env_value(value: str) -> str: + """Escape a value for .env output. + + Args: + value: Value to escape. + + Returns: + The escaped value. + """ + if re.search(r"\s", value): + return f'"{value}"' + return value diff --git a/src/sre_agent/cli/interactive_shell.py b/src/sre_agent/cli/interactive_shell.py new file mode 100644 index 00000000..05669641 --- /dev/null +++ b/src/sre_agent/cli/interactive_shell.py @@ -0,0 +1,36 @@ +"""Interactive shell for guided deployment.""" + +import questionary + +from sre_agent.cli.banner import print_global_banner +from sre_agent.cli.configuration import ensure_required_config +from sre_agent.cli.mode.local import run_local_mode +from sre_agent.cli.mode.remote import run_remote_mode +from sre_agent.cli.ui import console + +REMOTE_DEPLOYMENT_LABEL = "Remote Deployment (WIP)" + + +def start_interactive_shell() -> None: + """Start the interactive deployment shell.""" + print_global_banner() + ensure_required_config() + + while True: + choice = questionary.select( + "Running Mode:", + choices=[ + "Local", + REMOTE_DEPLOYMENT_LABEL, + "Exit", + ], + ).ask() + + if choice in (None, "Exit"): + console.print("Goodbye.") + return + + if choice == "Local": + run_local_mode() + elif choice == REMOTE_DEPLOYMENT_LABEL: + run_remote_mode() diff --git a/src/sre_agent/cli/main.py b/src/sre_agent/cli/main.py new file mode 100644 index 00000000..aa5ba976 --- /dev/null +++ b/src/sre_agent/cli/main.py @@ -0,0 +1,22 @@ +"""CLI entrypoint for the SRE Agent.""" + +import click + +from sre_agent.cli.interactive_shell import start_interactive_shell + + +@click.group(invoke_without_command=True) +@click.pass_context +def cli(ctx: click.Context) -> None: + """Run the SRE Agent CLI entrypoint. + + Args: + ctx: Click context for the command invocation. + """ + if ctx.invoked_subcommand is None: + start_interactive_shell() + + +def main() -> None: + """Run the CLI.""" + cli() diff --git a/src/sre_agent/cli/mode/__init__.py b/src/sre_agent/cli/mode/__init__.py new file mode 100644 index 00000000..a80447c3 --- /dev/null +++ b/src/sre_agent/cli/mode/__init__.py @@ -0,0 +1 @@ +"""Running mode workflows for the CLI.""" diff --git a/src/sre_agent/cli/mode/local.py b/src/sre_agent/cli/mode/local.py new file mode 100644 index 00000000..fc6a275c --- /dev/null +++ b/src/sre_agent/cli/mode/local.py @@ -0,0 +1,205 @@ +"""Local running mode for the CLI.""" + +import math +import shutil +import subprocess # nosec B404 +import sys +from pathlib import Path + +import questionary +from rich.panel import Panel + +from sre_agent.cli.mode.paths import project_root +from sre_agent.cli.ui import console + + +def run_local_mode() -> None: + """Run the agent locally.""" + console.print("[cyan]Local run[/cyan]") + console.print("[dim]This runs the agent using your local environment.[/dim]") + + log_group = questionary.text( + "CloudWatch log group:", + validate=lambda value: True if value.strip() else "Log group is required.", + ).ask() + if not log_group: + return + _ensure_slack_mcp_running() + _start_local_shell(log_group) + + +def _ensure_slack_mcp_running() -> None: + """Start the Slack MCP server container if needed.""" + console.print("[cyan]Ensuring Slack MCP server is running...[/cyan]") + compose_cmd = _docker_compose_cmd() + if compose_cmd is None: + console.print("[yellow]Docker Compose not found. Start Slack MCP manually.[/yellow]") + console.print("Run: `docker compose up -d slack`") + return + + result = subprocess.run( + [*compose_cmd, "up", "-d", "slack"], + cwd=project_root(), + check=False, + capture_output=True, + text=True, + ) # nosec B603 + if result.returncode != 0: + console.print("[yellow]Could not start Slack MCP server automatically.[/yellow]") + console.print("Run: `docker compose up -d slack`") + + +def _docker_compose_cmd() -> list[str] | None: + """Return the docker compose command. + + Returns: + The docker compose command parts, or None when unavailable. + """ + docker_path = shutil.which("docker") + if docker_path: + result = subprocess.run( + [docker_path, "compose", "version"], + check=False, + capture_output=True, + text=True, + ) # nosec B603 + if result.returncode == 0: + return [docker_path, "compose"] + docker_compose_path = shutil.which("docker-compose") + if docker_compose_path: + return [docker_compose_path] + return None + + +def _start_local_shell(log_group: str) -> None: + """Start a local interactive shell for diagnoses. + + Args: + log_group: CloudWatch log group name. + """ + _print_local_banner(log_group) + run_path = project_root() / "run.py" + if not run_path.exists(): + console.print("[red]run.py not found in project root.[/red]") + return + + while True: + try: + command = input("sre-agent (local)> ") + except EOFError: + console.print() + return + + command = command.strip() + if not command: + continue + + if command in {"exit", "quit"}: + console.print("[dim]Exiting local shell.[/dim]") + return + + if command == "help": + _print_local_help() + continue + + if command.startswith("diagnose "): + _handle_diagnose_command(run_path, log_group, command) + continue + + console.print("[yellow]Unknown command. Type 'help' for commands.[/yellow]") + + +def _print_local_banner(log_group: str) -> None: + """Print the local shell banner. + + Args: + log_group: CloudWatch log group name. + """ + console.print( + Panel( + "Starting interactive shell...\nType 'help' for available commands or 'exit' to quit.", + title="Local Mode", + border_style="cyan", + ) + ) + console.print(f"[green]Connected to: {log_group}[/green]") + console.print("[dim]Slack MCP is required for local diagnostics.[/dim]") + console.print("\n[bold]Example command:[/bold]") + console.print("diagnose currencyservice 10m") + + +def _print_local_help() -> None: + """Print local shell help.""" + console.print("[bold]Commands:[/bold]") + console.print("- diagnose [duration]") + console.print(" Examples: diagnose currencyservice 10m, diagnose cartservice 5") + console.print("- help") + console.print("- exit") + + +def _handle_diagnose_command(run_path: Path, log_group: str, command: str) -> None: + """Parse and run a diagnose command. + + Args: + run_path: Path to the local run script. + log_group: CloudWatch log group name. + command: Raw command string. + """ + parts = command.split() + if len(parts) < 2: + console.print("[yellow]Usage: diagnose [duration][/yellow]") + return + + service_name = parts[1].strip() + if not service_name: + console.print("[yellow]Service name is required.[/yellow]") + return + + duration = parts[2] if len(parts) > 2 else "10m" + minutes = _parse_duration_minutes(duration) + if minutes is None: + console.print("[yellow]Invalid duration. Use 10m, 1h, or minutes like 5.[/yellow]") + return + + console.print(f"[cyan]Running diagnosis for {service_name} (last {minutes} minutes)...[/cyan]") + subprocess.run( + [ + sys.executable, + str(run_path), + log_group, + service_name, + str(minutes), + ], + check=False, + ) # nosec B603 + + +def _parse_duration_minutes(value: str) -> int | None: + """Parse a duration string into minutes. + + Args: + value: Duration input from the user. + + Returns: + Duration in minutes, or None when invalid. + """ + raw = value.strip().lower() + minutes: int | None = None + if raw.isdigit(): + minutes = int(raw) + else: + unit = raw[-1] + number = raw[:-1] + if number.isdigit(): + amount = int(number) + if amount > 0: + if unit == "m": + minutes = amount + elif unit == "h": + minutes = amount * 60 + elif unit == "s": + minutes = max(1, math.ceil(amount / 60)) + + if minutes is None or minutes <= 0: + return None + return minutes diff --git a/src/sre_agent/cli/mode/paths.py b/src/sre_agent/cli/mode/paths.py new file mode 100644 index 00000000..94571240 --- /dev/null +++ b/src/sre_agent/cli/mode/paths.py @@ -0,0 +1,12 @@ +"""Path helpers for the CLI.""" + +from pathlib import Path + + +def project_root() -> Path: + """Return the repository root directory. + + Returns: + The repository root directory path. + """ + return Path(__file__).resolve().parents[4] diff --git a/src/sre_agent/cli/mode/remote.py b/src/sre_agent/cli/mode/remote.py new file mode 100644 index 00000000..1fd5a1ec --- /dev/null +++ b/src/sre_agent/cli/mode/remote.py @@ -0,0 +1,684 @@ +"""Remote deployment mode for the CLI.""" + +from datetime import UTC, datetime +from typing import Any + +import questionary + +from sre_agent.cli.config import CliConfig, load_config, save_config +from sre_agent.cli.mode.paths import project_root +from sre_agent.cli.ui import console +from sre_agent.core.deployments.aws_ecs import ( + EcsDeploymentConfig, + ImageBuildConfig, + NetworkSelection, + build_and_push_images, + check_deployment, + cleanup_resources, + create_basic_vpc, + create_secret, + create_security_group, + create_session, + ensure_cluster, + ensure_repository, + ensure_roles, + ensure_service_linked_role, + get_secret_arn, + register_task_definition, + run_task, +) + + +def run_remote_mode() -> None: + """Run the remote deployment actions.""" + target = questionary.select( + "Remote Deployment:", + choices=[ + "Deploy to AWS ECS", + "Check deployment", + "Clean up deployment", + "Back", + ], + ).ask() + + if target in (None, "Back"): + return + + if target == "Deploy to AWS ECS": + _deploy_to_ecs() + elif target == "Check deployment": + _check_deployment() + elif target == "Clean up deployment": + _cleanup_menu() + + +def _deploy_to_ecs() -> None: + """Run the full ECS deployment flow.""" + config = load_config() + _print_deployment_summary(config) + confirm = questionary.confirm( + "Proceed with ECS deployment?", + default=True, + ).ask() + if not confirm: + console.print("[dim]Deployment cancelled.[/dim]") + return + + steps = [ + _run_network_step, + _run_security_group_step, + _run_secrets_step, + _run_iam_step, + _run_ecr_step, + _run_build_push_step, + _run_task_definition_step, + _run_cluster_step, + ] + updated = config + for step in steps: + ecs_config = _ecs_config_from_cli(updated) + next_config = step(updated, ecs_config) + if next_config is None: + return + updated = next_config + + ecs_config = _ecs_config_from_cli(updated) + _run_task_step(updated, ecs_config) + + +def _check_deployment() -> None: + """Check current deployment resources.""" + config = load_config() + ecs_config = _ecs_config_from_cli(config) + session = create_session(ecs_config) + console.print("[cyan]Checking current deployment...[/cyan]") + results = check_deployment(session, ecs_config) + for name, status in results.items(): + console.print(f"- {name}: {status}") + + +def _cleanup_menu() -> None: + """Clean up deployment resources.""" + console.print("[cyan]Clean up deployment resources[/cyan]") + console.print("[dim]This removes ECS resources created by the deployment flow.[/dim]") + + config = load_config() + ecs_config = _ecs_config_from_cli(config) + _print_cleanup_summary(config) + + confirm = questionary.confirm( + "This will delete the resources listed above. Continue?", + default=False, + ).ask() + if not confirm: + console.print("[dim]Clean up cancelled.[/dim]") + return + + force_delete = questionary.confirm( + "Delete secrets immediately (no recovery window)?", + default=False, + ).ask() + + cleanup_resources(ecs_config, _report_step, force_delete) + _reset_cleanup_state(config) + + +def _ecs_config_from_cli(config: CliConfig) -> EcsDeploymentConfig: + """Build an ECS deployment config from CLI config. + + Args: + config: CLI configuration values. + + Returns: + The ECS deployment configuration. + """ + return EcsDeploymentConfig( + aws_region=config.aws_region, + aws_profile=config.aws_profile, + project_name=config.project_name, + cluster_name=config.cluster_name, + task_family=config.task_family, + task_cpu=config.task_cpu, + task_memory=config.task_memory, + image_tag=config.image_tag, + vpc_id=config.vpc_id, + private_subnet_ids=config.private_subnet_ids, + security_group_id=config.security_group_id, + ecr_repo_sre_agent=config.ecr_repo_sre_agent, + ecr_repo_slack_mcp=config.ecr_repo_slack_mcp, + secret_anthropic_name=config.secret_anthropic_name, + secret_slack_bot_name=config.secret_slack_bot_name, + secret_github_token_name=config.secret_github_token_name, + secret_anthropic_arn=config.secret_anthropic_arn, + secret_slack_bot_arn=config.secret_slack_bot_arn, + secret_github_token_arn=config.secret_github_token_arn, + exec_role_arn=config.exec_role_arn, + task_role_arn=config.task_role_arn, + ecr_sre_agent_uri=config.ecr_sre_agent_uri, + ecr_slack_mcp_uri=config.ecr_slack_mcp_uri, + task_definition_arn=config.task_definition_arn, + cluster_arn=config.cluster_arn, + model=config.model, + slack_channel_id=config.slack_channel_id, + github_mcp_url=config.github_mcp_url, + log_group_name=config.log_group_name, + slack_mcp_host=config.slack_mcp_host, + slack_mcp_port=config.slack_mcp_port, + ) + + +def _run_network_step( + config: CliConfig, + ecs_config: EcsDeploymentConfig, +) -> CliConfig | None: + """Run the VPC selection step. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Starting ECS network setup...[/cyan]") + console.print("[dim]This will create a new VPC, private subnet, and NAT gateway.[/dim]") + session = create_session(ecs_config) + _report_step("Creating a new VPC with a private subnet and NAT gateway") + network = create_basic_vpc(session, ecs_config.project_name, _report_step) + return _update_config_with_network(config, network) + + +def _run_security_group_step( + config: CliConfig, + ecs_config: EcsDeploymentConfig, +) -> CliConfig | None: + """Create a security group. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + if not config.vpc_id: + console.print("[yellow]No VPC selected yet. Run network setup first.[/yellow]") + return None + + console.print("[cyan]Setting up security group...[/cyan]") + console.print("[dim]This will create a dedicated security group for ECS tasks.[/dim]") + session = create_session(ecs_config) + _report_step("Creating a new security group for ECS tasks") + suffix = datetime.now(UTC).strftime("%Y%m%d-%H%M%S") + name = f"{ecs_config.project_name}-tasks-{suffix}" + description = "Security group for SRE Agent ECS tasks" + group = create_security_group(session, config.vpc_id, name, description) + return _update_config_with_security_group(config, group) + + +def _run_secrets_step( + config: CliConfig, + ecs_config: EcsDeploymentConfig, +) -> CliConfig | None: + """Create Secrets Manager entries for API keys. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up Secrets Manager...[/cyan]") + console.print("[dim]This stores API keys securely for ECS tasks.[/dim]") + session = create_session(ecs_config) + + anthropic_arn = _ensure_secret( + session, + config.secret_anthropic_name, + "Anthropic API key", + config.secret_anthropic_arn, + ) + if anthropic_arn is None: + return None + + slack_arn = _ensure_secret( + session, + config.secret_slack_bot_name, + "Slack bot token", + config.secret_slack_bot_arn, + ) + if slack_arn is None: + return None + + github_arn = _ensure_secret( + session, + config.secret_github_token_name, + "GitHub token", + config.secret_github_token_arn, + ) + if github_arn is None: + return None + + return _update_config_with_secrets(config, anthropic_arn, slack_arn, github_arn) + + +def _run_iam_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create IAM roles for ECS tasks. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up IAM roles...[/cyan]") + console.print("[dim]This grants ECS tasks access to logs and secrets.[/dim]") + + secret_arns = [ + config.secret_anthropic_arn, + config.secret_slack_bot_arn, + config.secret_github_token_arn, + ] + if any(secret is None for secret in secret_arns): + console.print("[yellow]Secrets are missing. Run the secrets step first.[/yellow]") + return None + + session = create_session(ecs_config) + exec_role_arn, task_role_arn = ensure_roles( + session, + config.project_name, + config.aws_region, + [secret for secret in secret_arns if secret], + _report_step, + ) + return _update_config_with_roles(config, exec_role_arn, task_role_arn) + + +def _run_ecr_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create ECR repositories for images. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up ECR repositories...[/cyan]") + console.print("[dim]This stores the container images for ECS.[/dim]") + session = create_session(ecs_config) + + _report_step("Ensuring sre-agent repository") + sre_agent_uri = ensure_repository(session, config.ecr_repo_sre_agent) + + _report_step("Ensuring Slack MCP repository") + slack_mcp_uri = ensure_repository(session, config.ecr_repo_slack_mcp) + + return _update_config_with_ecr(config, sre_agent_uri, slack_mcp_uri) + + +def _run_build_push_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Build and push container images. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Building and pushing images...[/cyan]") + console.print("[dim]This builds the agent image and mirrors the Slack MCP image.[/dim]") + if not config.ecr_sre_agent_uri or not config.ecr_slack_mcp_uri: + console.print("[yellow]ECR repositories are missing. Run the ECR step first.[/yellow]") + return None + + session = create_session(ecs_config) + root_dir = project_root() + image_config = ImageBuildConfig( + sre_agent_uri=config.ecr_sre_agent_uri, + slack_mcp_uri=config.ecr_slack_mcp_uri, + image_tag=config.image_tag, + ) + build_and_push_images( + session, + root_dir, + image_config, + _report_step, + ) + return config + + +def _run_task_definition_step( + config: CliConfig, + ecs_config: EcsDeploymentConfig, +) -> CliConfig | None: + """Register the ECS task definition. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Registering ECS task definition...[/cyan]") + console.print("[dim]This defines how the ECS task runs the agent and Slack MCP.[/dim]") + if not config.slack_channel_id: + slack_channel_id = questionary.text("Slack channel ID:").ask() + if not slack_channel_id: + console.print("[yellow]Slack channel ID is required.[/yellow]") + return None + config.slack_channel_id = slack_channel_id + save_config(config) + + ecs_config = _ecs_config_from_cli(config) + session = create_session(ecs_config) + task_definition_arn = register_task_definition(session, ecs_config, _report_step) + return _update_config_with_task_definition(config, task_definition_arn) + + +def _run_cluster_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Ensure the ECS cluster exists. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Ensuring ECS cluster...[/cyan]") + console.print("[dim]This creates the ECS cluster if it does not exist.[/dim]") + session = create_session(ecs_config) + cluster_arn = ensure_cluster(session, config.cluster_name) + return _update_config_with_cluster(config, cluster_arn) + + +def _run_task_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> None: + """Run a one-off ECS task. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + """ + if not config.task_definition_arn: + console.print("[yellow]Task definition is missing. Register it first.[/yellow]") + return + if not config.private_subnet_ids or not config.security_group_id: + console.print("[yellow]Network configuration is missing.[/yellow]") + return + + confirm = questionary.confirm("Run a one-off ECS task now?", default=False).ask() + if not confirm: + console.print("[dim]Skipping task run.[/dim]") + return + + console.print("[cyan]Running ECS task...[/cyan]") + session = create_session(ecs_config) + ensure_service_linked_role(session, _report_step) + task_arn = run_task( + session, + config.cluster_name, + config.task_definition_arn, + config.private_subnet_ids, + config.security_group_id, + ) + console.print(f"[green]Task started: {task_arn}[/green]") + + +def _ensure_secret( + session: object, + name: str, + label: str, + existing_arn: str | None, +) -> str | None: + """Ensure a secret exists and return its ARN. + + Args: + session: Boto3 session wrapper for AWS calls. + name: Secret name to use. + label: Human-readable label for prompts. + existing_arn: Existing ARN if already stored. + + Returns: + The secret ARN, or None if creation failed. + """ + if existing_arn: + _report_step(f"Using saved secret ARN for {label}") + return existing_arn + + arn = get_secret_arn(session, name) + if arn: + _report_step(f"Found existing secret for {label}") + return arn + + value = questionary.password(f"Enter {label}:").ask() + if not value: + console.print("[yellow]Secret value is required.[/yellow]") + return None + + _report_step(f"Creating secret {name}") + return create_secret(session, name, value) + + +def _print_cleanup_summary(config: CliConfig) -> None: + """Print a summary of resources to be cleaned up. + + Args: + config: CLI configuration values. + """ + console.print("[bold]Resources to clean up:[/bold]") + console.print(f"- VPC: {config.vpc_id or 'not set'}") + console.print(f"- Private subnets: {', '.join(config.private_subnet_ids) or 'not set'}") + console.print(f"- Security group: {config.security_group_id or 'not set'}") + console.print(f"- ECS cluster: {config.cluster_name}") + console.print(f"- Task definition: {config.task_definition_arn or 'not set'}") + console.print(f"- ECR repos: {config.ecr_repo_sre_agent}, {config.ecr_repo_slack_mcp}") + console.print(f"- Log group: {config.log_group_name}") + secret_names = ", ".join( + [ + config.secret_anthropic_name, + config.secret_slack_bot_name, + config.secret_github_token_name, + ] + ) + console.print(f"- Secrets: {secret_names}") + iam_roles = f"{config.project_name}-task-execution, {config.project_name}-task" + console.print(f"- IAM roles: {iam_roles}") + + +def _print_deployment_summary(config: CliConfig) -> None: + """Print a summary of resources that will be created. + + Args: + config: CLI configuration values. + """ + console.print("[bold]Deployment plan:[/bold]") + console.print("- Create a new VPC with one public and one private subnet") + console.print("- Create an internet gateway, NAT gateway, and route tables") + console.print("- Create a dedicated security group for ECS tasks") + secret_names = ", ".join( + [ + config.secret_anthropic_name, + config.secret_slack_bot_name, + config.secret_github_token_name, + ] + ) + console.print(f"- Store secrets in Secrets Manager ({secret_names})") + iam_roles = f"{config.project_name}-task-execution and {config.project_name}-task" + console.print(f"- Create IAM roles: {iam_roles}") + ecr_repos = f"{config.ecr_repo_sre_agent}, {config.ecr_repo_slack_mcp}" + console.print(f"- Create ECR repositories: {ecr_repos}") + console.print("- Build and push container images") + console.print(f"- Register ECS task definition: {config.task_family}") + console.print(f"- Ensure ECS cluster: {config.cluster_name}") + console.print("- Optionally run a one-off ECS task") + + +def _reset_cleanup_state(config: CliConfig) -> None: + """Clear deployment state after clean up. + + Args: + config: CLI configuration values. + """ + config.vpc_id = None + config.private_subnet_ids = [] + config.security_group_id = None + config.secret_anthropic_arn = None + config.secret_slack_bot_arn = None + config.secret_github_token_arn = None + config.exec_role_arn = None + config.task_role_arn = None + config.ecr_sre_agent_uri = None + config.ecr_slack_mcp_uri = None + config.task_definition_arn = None + config.cluster_arn = None + + path = save_config(config) + console.print(f"[green]Cleared deployment state in {path}[/green]") + + +def _update_config_with_secrets( + config: CliConfig, + anthropic_arn: str, + slack_arn: str, + github_arn: str, +) -> CliConfig: + """Persist secret ARNs to config. + + Args: + config: CLI configuration values. + anthropic_arn: Anthropic secret ARN. + slack_arn: Slack bot token secret ARN. + github_arn: GitHub token secret ARN. + + Returns: + The updated configuration. + """ + config.secret_anthropic_arn = anthropic_arn + config.secret_slack_bot_arn = slack_arn + config.secret_github_token_arn = github_arn + path = save_config(config) + console.print(f"[green]Saved secrets configuration to {path}[/green]") + return config + + +def _update_config_with_roles( + config: CliConfig, + exec_role_arn: str, + task_role_arn: str, +) -> CliConfig: + """Persist role ARNs to config. + + Args: + config: CLI configuration values. + exec_role_arn: Execution role ARN. + task_role_arn: Task role ARN. + + Returns: + The updated configuration. + """ + config.exec_role_arn = exec_role_arn + config.task_role_arn = task_role_arn + path = save_config(config) + console.print(f"[green]Saved IAM role configuration to {path}[/green]") + return config + + +def _update_config_with_ecr( + config: CliConfig, + sre_agent_uri: str, + slack_mcp_uri: str, +) -> CliConfig: + """Persist ECR repository URIs to config. + + Args: + config: CLI configuration values. + sre_agent_uri: SRE agent repository URI. + slack_mcp_uri: Slack MCP repository URI. + + Returns: + The updated configuration. + """ + config.ecr_sre_agent_uri = sre_agent_uri + config.ecr_slack_mcp_uri = slack_mcp_uri + path = save_config(config) + console.print(f"[green]Saved ECR repository configuration to {path}[/green]") + return config + + +def _update_config_with_task_definition(config: CliConfig, task_definition_arn: str) -> CliConfig: + """Persist task definition ARN to config. + + Args: + config: CLI configuration values. + task_definition_arn: Task definition ARN. + + Returns: + The updated configuration. + """ + config.task_definition_arn = task_definition_arn + path = save_config(config) + console.print(f"[green]Saved task definition to {path}[/green]") + return config + + +def _update_config_with_cluster(config: CliConfig, cluster_arn: str) -> CliConfig: + """Persist cluster ARN to config. + + Args: + config: CLI configuration values. + cluster_arn: Cluster ARN. + + Returns: + The updated configuration. + """ + config.cluster_arn = cluster_arn + path = save_config(config) + console.print(f"[green]Saved cluster configuration to {path}[/green]") + return config + + +def _update_config_with_network(config: CliConfig, network: NetworkSelection) -> CliConfig: + """Persist network selection to config. + + Args: + config: CLI configuration values. + network: Selected network configuration. + + Returns: + The updated configuration. + """ + config.vpc_id = network.vpc_id + config.private_subnet_ids = network.private_subnet_ids + path = save_config(config) + console.print(f"[green]Saved network configuration to {path}[/green]") + return config + + +def _update_config_with_security_group(config: CliConfig, group: Any) -> CliConfig: + """Persist security group selection to config. + + Args: + config: CLI configuration values. + group: Security group result. + + Returns: + The updated configuration. + """ + config.security_group_id = group.group_id + path = save_config(config) + console.print(f"[green]Saved security group to {path}[/green]") + return config + + +def _report_step(message: str) -> None: + """Report deployment progress to the user. + + Args: + message: Progress message to display. + """ + console.print(f"[bold cyan]•[/bold cyan] {message}") diff --git a/src/sre_agent/cli/ui.py b/src/sre_agent/cli/ui.py new file mode 100644 index 00000000..96527c8f --- /dev/null +++ b/src/sre_agent/cli/ui.py @@ -0,0 +1,5 @@ +"""Shared Rich console for the CLI.""" + +from rich.console import Console + +console = Console() diff --git a/src/sre_agent/core/__init__.py b/src/sre_agent/core/__init__.py new file mode 100644 index 00000000..e75ff9b1 --- /dev/null +++ b/src/sre_agent/core/__init__.py @@ -0,0 +1,15 @@ +"""SRE Agent core modules.""" + +from sre_agent.core.agent import create_sre_agent, diagnose_error +from sre_agent.core.config import AgentConfig, get_config +from sre_agent.core.models import ErrorDiagnosis, LogEntry, LogQueryResult + +__all__ = [ + "create_sre_agent", + "diagnose_error", + "AgentConfig", + "get_config", + "ErrorDiagnosis", + "LogEntry", + "LogQueryResult", +] diff --git a/src/sre_agent/core/agent.py b/src/sre_agent/core/agent.py new file mode 100644 index 00000000..91fca431 --- /dev/null +++ b/src/sre_agent/core/agent.py @@ -0,0 +1,65 @@ +"""SRE Agent using pydantic-ai.""" + +from pydantic_ai import Agent + +from sre_agent.core.config import AgentConfig, get_config +from sre_agent.core.models import ErrorDiagnosis +from sre_agent.core.prompts import SYSTEM_PROMPT, build_diagnosis_prompt +from sre_agent.core.tools import ( + create_cloudwatch_toolset, + create_github_mcp_toolset, + create_slack_mcp_toolset, +) + + +def create_sre_agent(config: AgentConfig | None = None) -> Agent[None, ErrorDiagnosis]: + """Create the SRE Agent with all toolsets configured. + + Args: + config: Optional AgentConfig. If not provided, loads from environment. + + Returns: + Configured pydantic-ai Agent with structured output. + """ + if config is None: + config = get_config() + + toolsets = [ + create_cloudwatch_toolset(config), + create_github_mcp_toolset(config), + create_slack_mcp_toolset(config), + ] + + return Agent( + config.model, + system_prompt=SYSTEM_PROMPT, + output_type=ErrorDiagnosis, + toolsets=toolsets, + ) + + +async def diagnose_error( + log_group: str, + service_name: str, + time_range_minutes: int = 10, + config: AgentConfig | None = None, +) -> ErrorDiagnosis: + """Run a diagnosis for errors in a specific log group. + + Args: + log_group: CloudWatch log group to analyse. + service_name: Service name to filter. + time_range_minutes: How far back to look for errors. + config: Optional agent configuration. + + Returns: + ErrorDiagnosis with findings and suggested fixes. + """ + if config is None: + config = get_config() + + agent = create_sre_agent(config) + prompt = build_diagnosis_prompt(config, log_group, service_name, time_range_minutes) + + result = await agent.run(prompt) + return result.output diff --git a/src/sre_agent/core/api.py b/src/sre_agent/core/api.py new file mode 100644 index 00000000..b2bbb692 --- /dev/null +++ b/src/sre_agent/core/api.py @@ -0,0 +1,87 @@ +"""FastAPI HTTP service for the SRE Agent.""" + +import logging +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager + +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, Field + +from sre_agent.core.agent import diagnose_error +from sre_agent.core.config import get_config +from sre_agent.core.models import ErrorDiagnosis + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class DiagnoseRequest(BaseModel): + """Request body for the diagnose endpoint.""" + + log_group: str = Field(description="CloudWatch log group to analyse") + service_name: str = Field(description="Service to that triggers the error") + time_range_minutes: int = Field(default=10, ge=1, le=1440, description="Time range in minutes") + + +class HealthResponse(BaseModel): + """Health check response.""" + + status: str = "ok" + version: str = "0.2.0" + + +@asynccontextmanager +async def lifespan(app: FastAPI) -> AsyncIterator[None]: + """Application lifespan handler.""" + # Validate config on startup + try: + config = get_config() + logger.info(f"SRE Agent starting with model: {config.model}") + logger.info(f"AWS Region: {config.aws.region}") + logger.info(f"Slack MCP URL: {config.slack.mcp_url}") + logger.info(f"GitHub MCP URL: {config.github.mcp_url}") + except Exception as e: + logger.error(f"Configuration error: {e}") + raise + + yield + + logger.info("SRE Agent shutting down") + + +app = FastAPI( + title="SRE Agent API", + description="AI-powered Site Reliability Engineering agent for error diagnosis", + version="0.2.0", + lifespan=lifespan, +) + + +@app.get("/health", response_model=HealthResponse) +async def health_check() -> HealthResponse: + """Health check endpoint for liveness/readiness probes.""" + return HealthResponse() + + +@app.post("/diagnose", response_model=ErrorDiagnosis) +async def diagnose(request: DiagnoseRequest) -> ErrorDiagnosis: + """Diagnose errors in a CloudWatch log group. + + The agent will: + 1. Create a Slack thread to report the investigation + 2. Query CloudWatch for error logs + 3. Search GitHub for relevant code (if errors found) + 4. Reply to the Slack thread with findings + """ + logger.info(f"Diagnosing errors in {request.log_group}") + + try: + result = await diagnose_error( + log_group=request.log_group, + service_name=request.service_name, + time_range_minutes=request.time_range_minutes, + ) + return result + except Exception as e: + logger.exception(f"Diagnosis failed: {e}") + raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/sre_agent/core/config.py b/src/sre_agent/core/config.py new file mode 100644 index 00000000..4ceb0eb0 --- /dev/null +++ b/src/sre_agent/core/config.py @@ -0,0 +1,65 @@ +"""Configuration management for the SRE Agent.""" + +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class AWSConfig(BaseSettings): + """AWS configuration for CloudWatch access.""" + + model_config = SettingsConfigDict(env_prefix="AWS_", env_file=".env", extra="ignore") + + region: str = Field(default="eu-west-2", description="AWS region") + access_key_id: str | None = Field(default=None, description="AWS Access Key ID") + secret_access_key: str | None = Field(default=None, description="AWS Secret Access Key") + session_token: str | None = Field(default=None, description="AWS Session Token") + + +class GitHubConfig(BaseSettings): + """GitHub configuration for MCP server via SSE.""" + + model_config = SettingsConfigDict(env_prefix="GITHUB_", env_file=".env", extra="ignore") + + # Required: cannot be empty + personal_access_token: str = Field(description="GitHub Personal Access Token") + mcp_url: str = Field(description="URL of GitHub MCP server (SSE)") + + +class SlackConfig(BaseSettings): + """Slack configuration for korotovsky/slack-mcp-server.""" + + model_config = SettingsConfigDict(env_prefix="SLACK_", env_file=".env", extra="ignore") + + # Required: cannot be empty + channel_id: str = Field(description="Slack channel ID (Cxxxxxxxxxx)") + mcp_url: str = Field(description="URL of Slack MCP server (SSE)") + + +class AgentConfig(BaseSettings): + """Main agent configuration.""" + + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") + + # LLM Provider + anthropic_api_key: str | None = Field(default=None, alias="ANTHROPIC_API_KEY") + model: str = Field(default="claude-sonnet-4-5-20250929", alias="MODEL") + + # Sub-configs (required) + aws: AWSConfig + github: GitHubConfig + slack: SlackConfig + + +def get_config() -> AgentConfig: + """Load and return the agent configuration. + + The sub-configs are automatically populated from the environment + thanks to pydantic-settings. + """ + # We use type: ignore[call-arg] because mypy doesn't know BaseSettings + # will populate these fields from the environment variables. + return AgentConfig( + aws=AWSConfig(), + github=GitHubConfig(), # type: ignore[call-arg] + slack=SlackConfig(), # type: ignore[call-arg] + ) diff --git a/src/sre_agent/core/deployments/__init__.py b/src/sre_agent/core/deployments/__init__.py new file mode 100644 index 00000000..ad79a29d --- /dev/null +++ b/src/sre_agent/core/deployments/__init__.py @@ -0,0 +1,57 @@ +"""Deployment helpers for the SRE Agent.""" + +from sre_agent.core.deployments.aws_ecs import ( + EcsDeploymentConfig, + ImageBuildConfig, + NetworkSelection, + SecurityGroupInfo, + SubnetInfo, + VpcInfo, + build_and_push_images, + check_deployment, + cleanup_resources, + create_basic_vpc, + create_secret, + create_security_group, + create_session, + deploy_agent, + ensure_cluster, + ensure_repository, + ensure_roles, + ensure_service_linked_role, + get_identity, + get_secret_arn, + list_private_subnets, + list_security_groups, + list_vpcs, + register_task_definition, + run_task, +) + +__all__ = [ + "EcsDeploymentConfig", + "ImageBuildConfig", + "NetworkSelection", + "SecurityGroupInfo", + "SubnetInfo", + "VpcInfo", + "build_and_push_images", + "check_deployment", + "cleanup_resources", + "create_basic_vpc", + "create_security_group", + "create_secret", + "create_session", + "deploy_agent", + "ensure_cluster", + "ensure_repository", + "ensure_roles", + "ensure_service_linked_role", + "get_identity", + "get_secret_arn", + "list_private_subnets", + "list_security_groups", + "list_vpcs", + "register_task_definition", + "run_task", +] diff --git a/src/sre_agent/core/deployments/aws_ecs/__init__.py b/src/sre_agent/core/deployments/aws_ecs/__init__.py new file mode 100644 index 00000000..d4bb1571 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/__init__.py @@ -0,0 +1,59 @@ +"""AWS ECS deployment helpers.""" + +from sre_agent.core.deployments.aws_ecs.cleanup import cleanup_resources +from sre_agent.core.deployments.aws_ecs.deploy import deploy_agent +from sre_agent.core.deployments.aws_ecs.ecr import ensure_repository +from sre_agent.core.deployments.aws_ecs.ecs_tasks import ( + ensure_cluster, + register_task_definition, + run_task, +) +from sre_agent.core.deployments.aws_ecs.iam import ensure_roles, ensure_service_linked_role +from sre_agent.core.deployments.aws_ecs.images import ImageBuildConfig, build_and_push_images +from sre_agent.core.deployments.aws_ecs.models import ( + EcsDeploymentConfig, + NetworkSelection, + SecurityGroupInfo, + SubnetInfo, + VpcInfo, +) +from sre_agent.core.deployments.aws_ecs.network import ( + create_basic_vpc, + list_private_subnets, + list_vpcs, +) +from sre_agent.core.deployments.aws_ecs.secrets import create_secret, get_secret_arn +from sre_agent.core.deployments.aws_ecs.security_groups import ( + create_security_group, + list_security_groups, +) +from sre_agent.core.deployments.aws_ecs.session import create_session, get_identity +from sre_agent.core.deployments.aws_ecs.status import check_deployment + +__all__ = [ + "EcsDeploymentConfig", + "NetworkSelection", + "ImageBuildConfig", + "SecurityGroupInfo", + "SubnetInfo", + "VpcInfo", + "build_and_push_images", + "cleanup_resources", + "check_deployment", + "create_basic_vpc", + "create_security_group", + "create_secret", + "create_session", + "deploy_agent", + "ensure_cluster", + "ensure_repository", + "ensure_roles", + "ensure_service_linked_role", + "get_identity", + "get_secret_arn", + "list_private_subnets", + "list_security_groups", + "list_vpcs", + "register_task_definition", + "run_task", +] diff --git a/src/sre_agent/core/deployments/aws_ecs/cleanup.py b/src/sre_agent/core/deployments/aws_ecs/cleanup.py new file mode 100644 index 00000000..4685706d --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/cleanup.py @@ -0,0 +1,326 @@ +"""Clean-up helpers for ECS deployment resources.""" + +import time +from collections.abc import Callable +from typing import Any + +from botocore.exceptions import ClientError + +from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig +from sre_agent.core.deployments.aws_ecs.session import create_session + + +def cleanup_resources( + config: EcsDeploymentConfig, + reporter: Callable[[str], None], + force_delete_secrets: bool, +) -> None: + """Clean up resources created for ECS deployment.""" + session = create_session(config) + reporter("Stopping ECS tasks (if any)") + _stop_tasks(session, config.cluster_name, reporter) + + if config.task_definition_arn: + reporter("Deregistering task definition") + _deregister_task_definition(session, config.task_definition_arn, reporter) + + reporter("Deleting ECS cluster (if it exists)") + _delete_cluster(session, config.cluster_name, reporter) + + if config.log_group_name: + reporter("Deleting CloudWatch log group") + _delete_log_group(session, config.log_group_name, reporter) + + reporter("Deleting ECR repositories (if they exist)") + _delete_ecr_repo(session, config.ecr_repo_sre_agent, reporter) + _delete_ecr_repo(session, config.ecr_repo_slack_mcp, reporter) + + reporter("Deleting IAM roles (if they exist)") + _delete_roles(session, config, reporter) + + reporter("Deleting Secrets Manager secrets (if they exist)") + _delete_secret(session, config.secret_anthropic_name, force_delete_secrets, reporter) + _delete_secret(session, config.secret_slack_bot_name, force_delete_secrets, reporter) + _delete_secret(session, config.secret_github_token_name, force_delete_secrets, reporter) + + if config.vpc_id: + reporter("Deleting VPC resources") + _cleanup_vpc(session, config.vpc_id, reporter) + + +def _stop_tasks(session: Any, cluster_name: str, reporter: Callable[[str], None]) -> None: + """Stop running ECS tasks in the cluster.""" + ecs = session.client("ecs") + try: + response = ecs.list_tasks(cluster=cluster_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "ClusterNotFoundException": + return + raise RuntimeError(f"Failed to list ECS tasks: {exc}") from exc + + task_arns = response.get("taskArns", []) + for task_arn in task_arns: + reporter(f"Stopping task {task_arn}") + try: + ecs.stop_task(cluster=cluster_name, task=task_arn, reason="Clean up") + except ClientError as exc: + reporter(f"Failed to stop task {task_arn}: {exc}") + + +def _deregister_task_definition( + session: Any, + task_definition_arn: str, + reporter: Callable[[str], None], +) -> None: + """Deregister an ECS task definition.""" + ecs = session.client("ecs") + try: + ecs.deregister_task_definition(taskDefinition=task_definition_arn) + except ClientError as exc: + reporter(f"Failed to deregister task definition: {exc}") + + +def _delete_cluster(session: Any, cluster_name: str, reporter: Callable[[str], None]) -> None: + """Delete an ECS cluster if it exists.""" + ecs = session.client("ecs") + try: + ecs.delete_cluster(cluster=cluster_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code in {"ClusterNotFoundException"}: + return + reporter(f"Failed to delete cluster: {exc}") + + +def _delete_log_group(session: Any, log_group_name: str, reporter: Callable[[str], None]) -> None: + """Delete a CloudWatch log group.""" + logs = session.client("logs") + try: + logs.delete_log_group(logGroupName=log_group_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "ResourceNotFoundException": + reporter(f"Failed to delete log group: {exc}") + + +def _delete_ecr_repo(session: Any, name: str, reporter: Callable[[str], None]) -> None: + """Delete an ECR repository if it exists.""" + if not name: + return + ecr = session.client("ecr") + try: + ecr.delete_repository(repositoryName=name, force=True) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "RepositoryNotFoundException": + reporter(f"Failed to delete ECR repo {name}: {exc}") + + +def _delete_roles( + session: Any, + config: EcsDeploymentConfig, + reporter: Callable[[str], None], +) -> None: + """Delete IAM roles created for ECS tasks.""" + iam = session.client("iam") + role_names = _role_names(config) + for role_name in role_names: + reporter(f"Removing IAM role {role_name}") + _detach_managed_policies(iam, role_name, reporter) + _delete_inline_policies(iam, role_name, reporter) + try: + iam.delete_role(RoleName=role_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "NoSuchEntity": + reporter(f"Failed to delete role {role_name}: {exc}") + + +def _role_names(config: EcsDeploymentConfig) -> set[str]: + """Return role names for clean-up.""" + names = set() + if config.exec_role_arn: + names.add(config.exec_role_arn.split("/")[-1]) + if config.task_role_arn: + names.add(config.task_role_arn.split("/")[-1]) + names.add(f"{config.project_name}-task-execution") + names.add(f"{config.project_name}-task") + return names + + +def _detach_managed_policies(iam: Any, role_name: str, reporter: Callable[[str], None]) -> None: + """Detach managed policies from a role.""" + try: + response = iam.list_attached_role_policies(RoleName=role_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "NoSuchEntity": + return + reporter(f"Failed to list attached policies for {role_name}: {exc}") + return + + for policy in response.get("AttachedPolicies", []): + policy_arn = policy["PolicyArn"] + try: + iam.detach_role_policy(RoleName=role_name, PolicyArn=policy_arn) + except ClientError as exc: + reporter(f"Failed to detach policy {policy_arn} from {role_name}: {exc}") + + +def _delete_inline_policies(iam: Any, role_name: str, reporter: Callable[[str], None]) -> None: + """Delete inline policies for a role.""" + try: + response = iam.list_role_policies(RoleName=role_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "NoSuchEntity": + return + reporter(f"Failed to list inline policies for {role_name}: {exc}") + return + + for policy_name in response.get("PolicyNames", []): + try: + iam.delete_role_policy(RoleName=role_name, PolicyName=policy_name) + except ClientError as exc: + reporter(f"Failed to delete policy {policy_name} from {role_name}: {exc}") + + +def _delete_secret( + session: Any, + name: str, + force_delete: bool, + reporter: Callable[[str], None], +) -> None: + """Delete a secret if it exists.""" + if not name: + return + secrets = session.client("secretsmanager") + try: + secrets.delete_secret( + SecretId=name, + ForceDeleteWithoutRecovery=force_delete, + ) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "ResourceNotFoundException": + reporter(f"Failed to delete secret {name}: {exc}") + + +def _cleanup_vpc(session: Any, vpc_id: str, reporter: Callable[[str], None]) -> None: + """Delete a VPC and its dependent resources.""" + ec2 = session.client("ec2") + + nat_gateways = _list_nat_gateways(ec2, vpc_id) + allocation_ids = [allocation for _, allocation in nat_gateways if allocation] + for nat_gateway_id, _ in nat_gateways: + reporter(f"Deleting NAT gateway {nat_gateway_id}") + ec2.delete_nat_gateway(NatGatewayId=nat_gateway_id) + + if nat_gateways: + _wait_for_nat_gateways(ec2, [nat_id for nat_id, _ in nat_gateways], reporter) + + for allocation_id in allocation_ids: + reporter(f"Releasing Elastic IP {allocation_id}") + try: + ec2.release_address(AllocationId=allocation_id) + except ClientError as exc: + reporter(f"Failed to release Elastic IP {allocation_id}: {exc}") + + igw_ids = _list_internet_gateways(ec2, vpc_id) + for igw_id in igw_ids: + reporter(f"Detaching and deleting internet gateway {igw_id}") + try: + ec2.detach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + ec2.delete_internet_gateway(InternetGatewayId=igw_id) + except ClientError as exc: + reporter(f"Failed to delete internet gateway {igw_id}: {exc}") + + _delete_route_tables(ec2, vpc_id, reporter) + _delete_subnets(ec2, vpc_id, reporter) + _delete_security_groups(ec2, vpc_id, reporter) + + reporter(f"Deleting VPC {vpc_id}") + try: + ec2.delete_vpc(VpcId=vpc_id) + except ClientError as exc: + reporter(f"Failed to delete VPC {vpc_id}: {exc}") + + +def _list_nat_gateways(ec2: Any, vpc_id: str) -> list[tuple[str, str | None]]: + """Return NAT gateway IDs and allocation IDs.""" + response = ec2.describe_nat_gateways(Filter=[{"Name": "vpc-id", "Values": [vpc_id]}]) + gateways = [] + for nat_gateway in response.get("NatGateways", []): + nat_id = nat_gateway["NatGatewayId"] + allocation_id = None + for address in nat_gateway.get("NatGatewayAddresses", []): + allocation_id = address.get("AllocationId") + gateways.append((nat_id, allocation_id)) + return gateways + + +def _wait_for_nat_gateways(ec2: Any, nat_ids: list[str], reporter: Callable[[str], None]) -> None: + """Wait for NAT gateways to delete.""" + attempts = 30 + delay = 10 + for _ in range(attempts): + response = ec2.describe_nat_gateways(NatGatewayIds=nat_ids) + states = {gw["NatGatewayId"]: gw["State"] for gw in response.get("NatGateways", [])} + if all(state == "deleted" for state in states.values()): + return + reporter("Waiting for NAT gateways to delete...") + time.sleep(delay) + + +def _list_internet_gateways(ec2: Any, vpc_id: str) -> list[str]: + """List internet gateways attached to a VPC.""" + response = ec2.describe_internet_gateways( + Filters=[{"Name": "attachment.vpc-id", "Values": [vpc_id]}] + ) + return [igw["InternetGatewayId"] for igw in response.get("InternetGateways", [])] + + +def _delete_route_tables(ec2: Any, vpc_id: str, reporter: Callable[[str], None]) -> None: + """Delete non-main route tables.""" + response = ec2.describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) + for route_table in response.get("RouteTables", []): + associations = route_table.get("Associations", []) + is_main = any(assoc.get("Main") for assoc in associations) + for assoc in associations: + assoc_id = assoc.get("RouteTableAssociationId") + if assoc_id and not assoc.get("Main"): + try: + ec2.disassociate_route_table(AssociationId=assoc_id) + except ClientError as exc: + reporter(f"Failed to disassociate route table: {exc}") + if is_main: + continue + try: + ec2.delete_route_table(RouteTableId=route_table["RouteTableId"]) + except ClientError as exc: + reporter(f"Failed to delete route table: {exc}") + + +def _delete_subnets(ec2: Any, vpc_id: str, reporter: Callable[[str], None]) -> None: + """Delete all subnets in a VPC.""" + response = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) + for subnet in response.get("Subnets", []): + subnet_id = subnet["SubnetId"] + try: + ec2.delete_subnet(SubnetId=subnet_id) + except ClientError as exc: + reporter(f"Failed to delete subnet {subnet_id}: {exc}") + + +def _delete_security_groups(ec2: Any, vpc_id: str, reporter: Callable[[str], None]) -> None: + """Delete non-default security groups in a VPC.""" + response = ec2.describe_security_groups(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) + for group in response.get("SecurityGroups", []): + if group.get("GroupName") == "default": + continue + group_id = group["GroupId"] + try: + ec2.delete_security_group(GroupId=group_id) + except ClientError as exc: + reporter(f"Failed to delete security group {group_id}: {exc}") diff --git a/src/sre_agent/core/deployments/aws_ecs/deploy.py b/src/sre_agent/core/deployments/aws_ecs/deploy.py new file mode 100644 index 00000000..07246747 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/deploy.py @@ -0,0 +1,16 @@ +"""Deployment entrypoint for ECS.""" + +from collections.abc import Callable + +from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig +from sre_agent.core.deployments.aws_ecs.session import create_session, get_identity + + +def deploy_agent(config: EcsDeploymentConfig, reporter: Callable[[str], None]) -> None: + """Deploy the SRE Agent to ECS.""" + reporter("Checking AWS credentials") + session = create_session(config) + identity = get_identity(session) + reporter(f"Using AWS account {identity['Account']} ({identity['Arn']})") + + reporter("Deployment steps are not implemented yet.") diff --git a/src/sre_agent/core/deployments/aws_ecs/ecr.py b/src/sre_agent/core/deployments/aws_ecs/ecr.py new file mode 100644 index 00000000..6d3ec1fe --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/ecr.py @@ -0,0 +1,20 @@ +"""ECR helpers for ECS deployment.""" + +from typing import Any, cast + +from botocore.exceptions import ClientError + + +def ensure_repository(session: Any, name: str) -> str: + """Ensure an ECR repository exists and return its URI.""" + ecr = session.client("ecr") + try: + response = ecr.describe_repositories(repositoryNames=[name]) + return cast(str, response["repositories"][0]["repositoryUri"]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "RepositoryNotFoundException": + raise RuntimeError(f"Failed to read ECR repo {name}: {exc}") from exc + + response = ecr.create_repository(repositoryName=name) + return cast(str, response["repository"]["repositoryUri"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py new file mode 100644 index 00000000..0c0f29e5 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py @@ -0,0 +1,157 @@ +"""ECS task and cluster helpers.""" + +from collections.abc import Callable +from typing import Any, cast + +from botocore.exceptions import ClientError + +from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig + + +def ensure_log_group(session: Any, log_group_name: str) -> None: + """Ensure a CloudWatch log group exists.""" + logs = session.client("logs") + try: + logs.create_log_group(logGroupName=log_group_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "ResourceAlreadyExistsException": + raise RuntimeError(f"Failed to create log group: {exc}") from exc + + +def register_task_definition( + session: Any, + config: EcsDeploymentConfig, + reporter: Callable[[str], None], +) -> str: + """Register the ECS task definition.""" + if not config.exec_role_arn or not config.task_role_arn: + raise RuntimeError("Task roles must be created before registering the task definition.") + if not config.ecr_sre_agent_uri or not config.ecr_slack_mcp_uri: + raise RuntimeError( + "ECR repositories must be created before registering the task definition." + ) + if ( + not config.secret_anthropic_arn + or not config.secret_github_token_arn + or not config.secret_slack_bot_arn + ): + raise RuntimeError("Secrets must be created before registering the task definition.") + if not config.slack_channel_id: + raise RuntimeError("Slack channel ID is required for the task definition.") + + reporter("Ensuring CloudWatch log group for task logs") + ensure_log_group(session, config.log_group_name) + + ecs = session.client("ecs") + slack_mcp_url = f"http://localhost:{config.slack_mcp_port}/sse" + + container_definitions = [ + { + "name": "sre-agent", + "image": f"{config.ecr_sre_agent_uri}:{config.image_tag}", + "essential": True, + "portMappings": [{"containerPort": 8000, "protocol": "tcp"}], + "environment": [ + {"name": "AWS_REGION", "value": config.aws_region}, + {"name": "MODEL", "value": config.model}, + {"name": "SLACK_CHANNEL_ID", "value": config.slack_channel_id}, + {"name": "SLACK_MCP_URL", "value": slack_mcp_url}, + {"name": "GITHUB_MCP_URL", "value": config.github_mcp_url}, + ], + "secrets": [ + { + "name": "ANTHROPIC_API_KEY", + "valueFrom": config.secret_anthropic_arn, + }, + { + "name": "GITHUB_PERSONAL_ACCESS_TOKEN", + "valueFrom": config.secret_github_token_arn, + }, + ], + "dependsOn": [{"containerName": "slack", "condition": "START"}], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": config.log_group_name, + "awslogs-region": config.aws_region, + "awslogs-stream-prefix": "sre-agent", + }, + }, + }, + { + "name": "slack", + "image": f"{config.ecr_slack_mcp_uri}:{config.image_tag}", + "essential": True, + "portMappings": [{"containerPort": config.slack_mcp_port, "protocol": "tcp"}], + "environment": [ + {"name": "SLACK_MCP_ADD_MESSAGE_TOOL", "value": config.slack_channel_id}, + {"name": "SLACK_MCP_HOST", "value": config.slack_mcp_host}, + {"name": "SLACK_MCP_PORT", "value": str(config.slack_mcp_port)}, + ], + "secrets": [ + {"name": "SLACK_MCP_XOXB_TOKEN", "valueFrom": config.secret_slack_bot_arn}, + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": config.log_group_name, + "awslogs-region": config.aws_region, + "awslogs-stream-prefix": "slack", + }, + }, + }, + ] + + response = ecs.register_task_definition( + family=config.task_family, + networkMode="awsvpc", + requiresCompatibilities=["FARGATE"], + cpu=str(config.task_cpu), + memory=str(config.task_memory), + executionRoleArn=config.exec_role_arn, + taskRoleArn=config.task_role_arn, + containerDefinitions=container_definitions, + ) + return cast(str, response["taskDefinition"]["taskDefinitionArn"]) + + +def ensure_cluster(session: Any, cluster_name: str) -> str: + """Ensure an ECS cluster exists.""" + ecs = session.client("ecs") + response = ecs.describe_clusters(clusters=[cluster_name]) + clusters = response.get("clusters", []) + if clusters: + return cast(str, clusters[0]["clusterArn"]) + + response = ecs.create_cluster(clusterName=cluster_name) + return cast(str, response["cluster"]["clusterArn"]) + + +def run_task( + session: Any, + cluster_name: str, + task_definition_arn: str, + subnet_ids: list[str], + security_group_id: str, +) -> str: + """Run a one-off ECS task.""" + ecs = session.client("ecs") + response = ecs.run_task( + cluster=cluster_name, + launchType="FARGATE", + taskDefinition=task_definition_arn, + count=1, + networkConfiguration={ + "awsvpcConfiguration": { + "subnets": subnet_ids, + "securityGroups": [security_group_id], + "assignPublicIp": "DISABLED", + } + }, + ) + tasks = response.get("tasks", []) + if not tasks: + failures = response.get("failures", []) + raise RuntimeError(f"Failed to run task: {failures}") + return cast(str, tasks[0]["taskArn"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/iam.py b/src/sre_agent/core/deployments/aws_ecs/iam.py new file mode 100644 index 00000000..282c5b7d --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/iam.py @@ -0,0 +1,158 @@ +"""IAM role helpers for ECS deployment.""" + +import json +from collections.abc import Callable +from typing import Any, cast + +from botocore.exceptions import ClientError + + +def ensure_roles( + session: Any, + project_name: str, + region: str, + secret_arns: list[str], + reporter: Callable[[str], None], +) -> tuple[str, str]: + """Ensure execution and task roles exist.""" + if not secret_arns: + raise RuntimeError("Secret ARNs are required before creating roles.") + + iam = session.client("iam") + exec_role_name = f"{project_name}-task-execution" + task_role_name = f"{project_name}-task" + + reporter("Ensuring task execution role") + exec_role_arn = _ensure_role(iam, exec_role_name, _ecs_trust_policy()) + _attach_managed_policy( + iam, + exec_role_name, + "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", + ) + _put_inline_policy( + iam, + exec_role_name, + f"{project_name}-secrets", + _secrets_policy(secret_arns), + ) + + reporter("Ensuring task role for CloudWatch access") + task_role_arn = _ensure_role(iam, task_role_name, _ecs_trust_policy()) + account_id = _get_account_id(session) + _put_inline_policy( + iam, + task_role_name, + f"{project_name}-logs", + _logs_policy(region, account_id), + ) + + return exec_role_arn, task_role_arn + + +def ensure_service_linked_role(session: Any, reporter: Callable[[str], None]) -> None: + """Ensure the ECS service-linked role exists.""" + iam = session.client("iam") + role_name = "AWSServiceRoleForECS" + try: + iam.get_role(RoleName=role_name) + reporter("ECS service-linked role already exists") + return + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "NoSuchEntity": + raise RuntimeError(f"Failed to read service-linked role: {exc}") from exc + + reporter("Creating ECS service-linked role") + try: + iam.create_service_linked_role(AWSServiceName="ecs.amazonaws.com") + except ClientError as exc: + raise RuntimeError(f"Failed to create service-linked role: {exc}") from exc + + +def _ensure_role(iam: Any, role_name: str, trust_policy: dict[str, Any]) -> str: + """Create a role if needed and return its ARN.""" + try: + response = iam.get_role(RoleName=role_name) + return cast(str, response["Role"]["Arn"]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code != "NoSuchEntity": + raise RuntimeError(f"Failed to read role {role_name}: {exc}") from exc + + response = iam.create_role( + RoleName=role_name, + AssumeRolePolicyDocument=json.dumps(trust_policy), + ) + return cast(str, response["Role"]["Arn"]) + + +def _attach_managed_policy(iam: Any, role_name: str, policy_arn: str) -> None: + """Attach a managed policy if it is missing.""" + response = iam.list_attached_role_policies(RoleName=role_name) + attached = {policy["PolicyArn"] for policy in response.get("AttachedPolicies", [])} + if policy_arn in attached: + return + iam.attach_role_policy(RoleName=role_name, PolicyArn=policy_arn) + + +def _put_inline_policy( + iam: Any, + role_name: str, + policy_name: str, + policy_doc: dict[str, Any], +) -> None: + """Attach or update an inline policy.""" + iam.put_role_policy( + RoleName=role_name, + PolicyName=policy_name, + PolicyDocument=json.dumps(policy_doc), + ) + + +def _ecs_trust_policy() -> dict[str, Any]: + """Return the ECS task trust policy.""" + return { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"Service": "ecs-tasks.amazonaws.com"}, + "Action": "sts:AssumeRole", + } + ], + } + + +def _secrets_policy(secret_arns: list[str]) -> dict[str, Any]: + """Allow read access to Secrets Manager.""" + return { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["secretsmanager:GetSecretValue"], + "Resource": secret_arns, + } + ], + } + + +def _logs_policy(region: str, account_id: str) -> dict[str, Any]: + """Allow CloudWatch Logs queries.""" + return { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["logs:FilterLogEvents"], + "Resource": f"arn:aws:logs:{region}:{account_id}:log-group:*", + } + ], + } + + +def _get_account_id(session: Any) -> str: + """Return the AWS account ID.""" + client = session.client("sts") + response = client.get_caller_identity() + return cast(str, response["Account"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/images.py b/src/sre_agent/core/deployments/aws_ecs/images.py new file mode 100644 index 00000000..c9d44ec3 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/images.py @@ -0,0 +1,115 @@ +"""Docker build and push helpers.""" + +import base64 +import shutil +import subprocess # nosec B404 +from collections.abc import Callable +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from botocore.exceptions import ClientError + + +@dataclass(frozen=True) +class ImageBuildConfig: + """Image build settings for the ECS deployment.""" + + sre_agent_uri: str + slack_mcp_uri: str + image_tag: str + + +def build_and_push_images( + session: Any, + root_dir: Path, + image_config: ImageBuildConfig, + reporter: Callable[[str], None], +) -> None: + """Build and push container images to ECR.""" + _require_docker() + + reporter("Authenticating Docker with ECR") + username, password, proxy_endpoint = _ecr_login(session) + _run( + [ + "docker", + "login", + "--username", + username, + "--password-stdin", + proxy_endpoint, + ], + reporter, + input_bytes=password.encode("utf-8"), + ) + + reporter("Building and pushing sre-agent image") + _run( + [ + "docker", + "build", + "-t", + f"{image_config.sre_agent_uri}:{image_config.image_tag}", + str(root_dir), + ], + reporter, + ) + _run( + ["docker", "push", f"{image_config.sre_agent_uri}:{image_config.image_tag}"], + reporter, + ) + + reporter("Mirroring Slack MCP image into ECR") + _run(["docker", "pull", "ghcr.io/korotovsky/slack-mcp-server:latest"], reporter) + _run( + [ + "docker", + "tag", + "ghcr.io/korotovsky/slack-mcp-server:latest", + f"{image_config.slack_mcp_uri}:{image_config.image_tag}", + ], + reporter, + ) + _run( + ["docker", "push", f"{image_config.slack_mcp_uri}:{image_config.image_tag}"], + reporter, + ) + + +def _require_docker() -> None: + """Ensure Docker is installed.""" + if not shutil.which("docker"): + raise RuntimeError("Docker is required to build and push images.") + + +def _ecr_login(session: Any) -> tuple[str, str, str]: + """Return Docker login credentials for ECR.""" + ecr = session.client("ecr") + try: + # spellchecker:ignore-next-line + response = ecr.get_authorization_token() + except ClientError as exc: + raise RuntimeError(f"Failed to authenticate with ECR: {exc}") from exc + + # spellchecker:ignore-next-line + auth_data = response["authorizationData"][0] + # spellchecker:ignore-next-line + token = base64.b64decode(auth_data["authorizationToken"]).decode("utf-8") + username, password = token.split(":", 1) + proxy_endpoint = auth_data["proxyEndpoint"] + return username, password, proxy_endpoint + + +def _run( + command: list[str], + reporter: Callable[[str], None], + input_bytes: bytes | None = None, +) -> None: + """Run a subprocess command.""" + executable = shutil.which(command[0]) + if not executable: + raise RuntimeError(f"Executable not found: {command[0]}") + resolved_command = [executable, *command[1:]] + reporter(f"Running: {' '.join(resolved_command)}") + subprocess.run(resolved_command, check=True, input=input_bytes) # nosec B603 diff --git a/src/sre_agent/core/deployments/aws_ecs/models.py b/src/sre_agent/core/deployments/aws_ecs/models.py new file mode 100644 index 00000000..172cf334 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/models.py @@ -0,0 +1,76 @@ +"""Data models for ECS deployment.""" + +from dataclasses import dataclass, field + + +@dataclass +class EcsDeploymentConfig: + """Configuration for ECS deployment.""" + + aws_region: str + aws_profile: str | None + project_name: str + cluster_name: str + task_family: str + task_cpu: int + task_memory: int + image_tag: str + vpc_id: str | None + private_subnet_ids: list[str] + security_group_id: str | None + ecr_repo_sre_agent: str + ecr_repo_slack_mcp: str + secret_anthropic_name: str + secret_slack_bot_name: str + secret_github_token_name: str + secret_anthropic_arn: str | None + secret_slack_bot_arn: str | None + secret_github_token_arn: str | None + exec_role_arn: str | None + task_role_arn: str | None + ecr_sre_agent_uri: str | None + ecr_slack_mcp_uri: str | None + task_definition_arn: str | None + cluster_arn: str | None + model: str + slack_channel_id: str | None + github_mcp_url: str + log_group_name: str + slack_mcp_host: str + slack_mcp_port: int + + +@dataclass +class VpcInfo: + """Representation of a VPC.""" + + vpc_id: str + cidr_block: str + name: str | None + + +@dataclass +class SubnetInfo: + """Representation of a subnet.""" + + subnet_id: str + cidr_block: str + availability_zone: str + name: str | None + + +@dataclass +class SecurityGroupInfo: + """Representation of a security group.""" + + group_id: str + name: str + description: str + + +@dataclass +class NetworkSelection: + """Selected network configuration.""" + + vpc_id: str + private_subnet_ids: list[str] = field(default_factory=list) diff --git a/src/sre_agent/core/deployments/aws_ecs/network.py b/src/sre_agent/core/deployments/aws_ecs/network.py new file mode 100644 index 00000000..6ad56876 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/network.py @@ -0,0 +1,142 @@ +"""VPC and subnet management for ECS.""" + +from collections.abc import Callable +from typing import Any + +from sre_agent.core.deployments.aws_ecs.models import NetworkSelection, SubnetInfo, VpcInfo + + +def list_vpcs(session: Any) -> list[VpcInfo]: + """List VPCs for the current account.""" + ec2 = session.client("ec2") + response = ec2.describe_vpcs() + vpcs = [] + for vpc in response.get("Vpcs", []): + vpcs.append( + VpcInfo( + vpc_id=vpc["VpcId"], + cidr_block=vpc["CidrBlock"], + name=_tag_value(vpc.get("Tags")), + ) + ) + return vpcs + + +def list_private_subnets(session: Any, vpc_id: str) -> list[SubnetInfo]: + """List private subnets for a VPC.""" + ec2 = session.client("ec2") + response = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) + subnets = [] + for subnet in response.get("Subnets", []): + if subnet.get("MapPublicIpOnLaunch") is True: + continue + subnets.append( + SubnetInfo( + subnet_id=subnet["SubnetId"], + cidr_block=subnet["CidrBlock"], + availability_zone=subnet["AvailabilityZone"], + name=_tag_value(subnet.get("Tags")), + ) + ) + return subnets + + +def create_basic_vpc( + session: Any, + project_name: str, + reporter: Callable[[str], None], +) -> NetworkSelection: + """Create a simple VPC with one public and one private subnet.""" + ec2 = session.client("ec2") + + reporter("Creating VPC (private networking foundation)") + vpc_id = ec2.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + ec2.modify_vpc_attribute(VpcId=vpc_id, EnableDnsSupport={"Value": True}) + ec2.modify_vpc_attribute(VpcId=vpc_id, EnableDnsHostnames={"Value": True}) + _tag_resource(ec2, vpc_id, f"{project_name}-vpc") + + reporter("Creating internet gateway (public subnet access)") + igw_id = ec2.create_internet_gateway()["InternetGateway"]["InternetGatewayId"] + ec2.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + _tag_resource(ec2, igw_id, f"{project_name}-igw") + + availability_zone = _first_availability_zone(ec2) + + reporter("Creating public subnet (used by NAT gateway)") + public_subnet_id = ec2.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.0.0/24", + AvailabilityZone=availability_zone, + )["Subnet"]["SubnetId"] + ec2.modify_subnet_attribute( + SubnetId=public_subnet_id, + MapPublicIpOnLaunch={"Value": True}, + ) + _tag_resource(ec2, public_subnet_id, f"{project_name}-public") + + reporter("Creating private subnet (where ECS tasks will run)") + private_subnet_id = ec2.create_subnet( + VpcId=vpc_id, + CidrBlock="10.0.1.0/24", + AvailabilityZone=availability_zone, + )["Subnet"]["SubnetId"] + ec2.modify_subnet_attribute( + SubnetId=private_subnet_id, + MapPublicIpOnLaunch={"Value": False}, + ) + _tag_resource(ec2, private_subnet_id, f"{project_name}-private") + + reporter("Creating routes for public subnet") + public_route_table_id = ec2.create_route_table(VpcId=vpc_id)["RouteTable"]["RouteTableId"] + ec2.create_route( + RouteTableId=public_route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + ec2.associate_route_table(RouteTableId=public_route_table_id, SubnetId=public_subnet_id) + _tag_resource(ec2, public_route_table_id, f"{project_name}-public-rt") + + reporter("Creating NAT gateway for outbound access (this can take a few minutes)") + allocation_id = ec2.allocate_address(Domain="vpc")["AllocationId"] + nat_gateway_id = ec2.create_nat_gateway( + SubnetId=public_subnet_id, + AllocationId=allocation_id, + )["NatGateway"]["NatGatewayId"] + ec2.get_waiter("nat_gateway_available").wait(NatGatewayIds=[nat_gateway_id]) + + reporter("Creating routes for private subnet") + private_route_table_id = ec2.create_route_table(VpcId=vpc_id)["RouteTable"]["RouteTableId"] + ec2.create_route( + RouteTableId=private_route_table_id, + DestinationCidrBlock="0.0.0.0/0", + NatGatewayId=nat_gateway_id, + ) + ec2.associate_route_table(RouteTableId=private_route_table_id, SubnetId=private_subnet_id) + _tag_resource(ec2, private_route_table_id, f"{project_name}-private-rt") + + reporter("VPC created successfully") + return NetworkSelection(vpc_id=vpc_id, private_subnet_ids=[private_subnet_id]) + + +def _tag_value(tags: list[dict[str, Any]] | None) -> str | None: + """Extract a Name tag from a tag list.""" + if not tags: + return None + for tag in tags: + if tag.get("Key") == "Name": + return tag.get("Value") + return None + + +def _tag_resource(ec2: Any, resource_id: str, name: str) -> None: + """Apply a Name tag to a resource.""" + ec2.create_tags(Resources=[resource_id], Tags=[{"Key": "Name", "Value": name}]) + + +def _first_availability_zone(ec2: Any) -> str: + """Fetch the first availability zone.""" + response = ec2.describe_availability_zones() + zones = response.get("AvailabilityZones", []) + if not zones: + raise RuntimeError("No availability zones found for this region.") + return str(zones[0]["ZoneName"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/secrets.py b/src/sre_agent/core/deployments/aws_ecs/secrets.py new file mode 100644 index 00000000..14598c0f --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/secrets.py @@ -0,0 +1,28 @@ +"""Secrets Manager helpers for ECS deployment.""" + +from typing import Any, cast + +from botocore.exceptions import ClientError + + +def get_secret_arn(session: Any, name: str) -> str | None: + """Fetch a secret ARN by name.""" + client = session.client("secretsmanager") + try: + response = client.describe_secret(SecretId=name) + return cast(str, response["ARN"]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "ResourceNotFoundException": + return None + raise RuntimeError(f"Failed to read secret {name}: {exc}") from exc + + +def create_secret(session: Any, name: str, value: str) -> str: + """Create a secret and return its ARN.""" + client = session.client("secretsmanager") + try: + response = client.create_secret(Name=name, SecretString=value) + except ClientError as exc: + raise RuntimeError(f"Failed to create secret {name}: {exc}") from exc + return cast(str, response["ARN"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/security_groups.py b/src/sre_agent/core/deployments/aws_ecs/security_groups.py new file mode 100644 index 00000000..487f5b18 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/security_groups.py @@ -0,0 +1,50 @@ +"""Security group management for ECS.""" + +from typing import Any + +from botocore.exceptions import ClientError + +from sre_agent.core.deployments.aws_ecs.models import SecurityGroupInfo + + +def list_security_groups(session: Any, vpc_id: str) -> list[SecurityGroupInfo]: + """List security groups for a VPC.""" + ec2 = session.client("ec2") + response = ec2.describe_security_groups(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) + groups = [] + for group in response.get("SecurityGroups", []): + groups.append( + SecurityGroupInfo( + group_id=group["GroupId"], + name=group["GroupName"], + description=group.get("Description", ""), + ) + ) + return groups + + +def create_security_group( + session: Any, + vpc_id: str, + name: str, + description: str, +) -> SecurityGroupInfo: + """Create a security group with default outbound access.""" + ec2 = session.client("ec2") + try: + response = ec2.create_security_group( + VpcId=vpc_id, + GroupName=name, + Description=description, + ) + except ClientError as exc: + raise RuntimeError(f"Failed to create security group: {exc}") from exc + + group_id = response["GroupId"] + ec2.create_tags(Resources=[group_id], Tags=[{"Key": "Name", "Value": name}]) + + return SecurityGroupInfo( + group_id=group_id, + name=name, + description=description, + ) diff --git a/src/sre_agent/core/deployments/aws_ecs/session.py b/src/sre_agent/core/deployments/aws_ecs/session.py new file mode 100644 index 00000000..50e1a600 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/session.py @@ -0,0 +1,32 @@ +"""AWS session helpers.""" + +import boto3 +from botocore.exceptions import ClientError + +from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig + + +def create_session(config: EcsDeploymentConfig) -> boto3.session.Session: + """Create a boto3 session.""" + if config.aws_profile: + return boto3.session.Session( + profile_name=config.aws_profile, + region_name=config.aws_region, + ) + + return boto3.session.Session(region_name=config.aws_region) + + +def get_identity(session: boto3.session.Session) -> dict[str, str]: + """Fetch the current AWS identity.""" + client = session.client("sts") + try: + response = client.get_caller_identity() + except ClientError as exc: + raise RuntimeError(f"Failed to read AWS identity: {exc}") from exc + + return { + "Account": str(response.get("Account", "")), + "Arn": str(response.get("Arn", "")), + "UserId": str(response.get("UserId", "")), + } diff --git a/src/sre_agent/core/deployments/aws_ecs/status.py b/src/sre_agent/core/deployments/aws_ecs/status.py new file mode 100644 index 00000000..f6583f95 --- /dev/null +++ b/src/sre_agent/core/deployments/aws_ecs/status.py @@ -0,0 +1,168 @@ +"""Deployment status checks for ECS.""" + +from typing import Any + +from botocore.exceptions import ClientError + +from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig + + +def check_deployment(session: Any, config: EcsDeploymentConfig) -> dict[str, str]: + """Check whether deployment resources exist.""" + results: dict[str, str] = {} + + results["VPC"] = _check_vpc(session, config.vpc_id) + results["Private subnets"] = _check_subnets(session, config.private_subnet_ids) + results["Security group"] = _check_security_group(session, config.security_group_id) + results["Secrets"] = _check_secrets( + session, + [ + config.secret_anthropic_name, + config.secret_slack_bot_name, + config.secret_github_token_name, + ], + ) + results["IAM roles"] = _check_roles(session, config) + results["ECR repositories"] = _check_ecr_repos( + session, + [config.ecr_repo_sre_agent, config.ecr_repo_slack_mcp], + ) + results["Log group"] = _check_log_group(session, config.log_group_name) + results["Task definition"] = _check_task_definition(session, config.task_definition_arn) + results["ECS cluster"] = _check_cluster(session, config.cluster_name) + + return results + + +def _check_vpc(session: Any, vpc_id: str | None) -> str: + if not vpc_id: + return "not set" + ec2 = session.client("ec2") + try: + ec2.describe_vpcs(VpcIds=[vpc_id]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "InvalidVpcID.NotFound": + return "missing" + return f"error: {code}" + return "present" + + +def _check_subnets(session: Any, subnet_ids: list[str]) -> str: + if not subnet_ids: + return "not set" + ec2 = session.client("ec2") + missing = 0 + for subnet_id in subnet_ids: + try: + ec2.describe_subnets(SubnetIds=[subnet_id]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "InvalidSubnetID.NotFound": + missing += 1 + else: + return f"error: {code}" + if missing == 0: + return "present" + return f"missing {missing}/{len(subnet_ids)}" + + +def _check_security_group(session: Any, group_id: str | None) -> str: + if not group_id: + return "not set" + ec2 = session.client("ec2") + try: + ec2.describe_security_groups(GroupIds=[group_id]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "InvalidGroup.NotFound": + return "missing" + return f"error: {code}" + return "present" + + +def _check_secrets(session: Any, names: list[str]) -> str: + client = session.client("secretsmanager") + missing = 0 + for name in names: + try: + client.describe_secret(SecretId=name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "ResourceNotFoundException": + missing += 1 + else: + return f"error: {code}" + if missing == 0: + return "present" + return f"missing {missing}/{len(names)}" + + +def _check_roles(session: Any, config: EcsDeploymentConfig) -> str: + iam = session.client("iam") + role_names = { + f"{config.project_name}-task-execution", + f"{config.project_name}-task", + } + missing = 0 + for role_name in role_names: + try: + iam.get_role(RoleName=role_name) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "NoSuchEntity": + missing += 1 + else: + return f"error: {code}" + if missing == 0: + return "present" + return f"missing {missing}/{len(role_names)}" + + +def _check_ecr_repos(session: Any, names: list[str]) -> str: + ecr = session.client("ecr") + missing = 0 + for name in names: + try: + ecr.describe_repositories(repositoryNames=[name]) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "RepositoryNotFoundException": + missing += 1 + else: + return f"error: {code}" + if missing == 0: + return "present" + return f"missing {missing}/{len(names)}" + + +def _check_log_group(session: Any, log_group_name: str) -> str: + logs = session.client("logs") + response = logs.describe_log_groups(logGroupNamePrefix=log_group_name) + groups = [group["logGroupName"] for group in response.get("logGroups", [])] + return "present" if log_group_name in groups else "missing" + + +def _check_task_definition(session: Any, task_definition_arn: str | None) -> str: + if not task_definition_arn: + return "not set" + ecs = session.client("ecs") + try: + ecs.describe_task_definition(taskDefinition=task_definition_arn) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code in {"ClientException", "InvalidParameterException"}: + return "missing" + return f"error: {code}" + return "present" + + +def _check_cluster(session: Any, cluster_name: str) -> str: + ecs = session.client("ecs") + response = ecs.describe_clusters(clusters=[cluster_name]) + clusters = response.get("clusters", []) + if not clusters: + return "missing" + if clusters[0].get("status") != "ACTIVE": + return f"status {clusters[0].get('status')}" + return "present" diff --git a/src/sre_agent/core/interfaces.py b/src/sre_agent/core/interfaces.py new file mode 100644 index 00000000..e42d322a --- /dev/null +++ b/src/sre_agent/core/interfaces.py @@ -0,0 +1,54 @@ +"""Abstract interfaces for direct API implementations. + +These interfaces define contracts for tools that use direct API calls +(not MCP servers). If using an MCP server, no interface is needed. + +Currently used by: +- CloudWatch (LoggingInterface) + +Not needed for MCP-based tools: +- GitHub (MCP server) +- Slack (MCP server) +""" + +from abc import ABC, abstractmethod + +from sre_agent.core.models import LogQueryResult + + +class LoggingInterface(ABC): + """Interface for logging platforms (CloudWatch, Cloud Monitoring, Azure Monitor, etc.).""" + + @abstractmethod + async def query_errors( + self, + source: str, + service_name: str, + time_range_minutes: int = 10, + ) -> LogQueryResult: + """Query error logs from the platform.""" + raise NotImplementedError + + +class RepositoryInterface(ABC): + """Interface for code repositories (GitLab, Bitbucket, etc.). + + Note: GitHub uses MCP server, so this interface is for other providers. + """ + + @abstractmethod + async def get_file(self, repo: str, path: str, ref: str | None = None) -> str: + """Get file content from the repository.""" + raise NotImplementedError + + +class MessagingInterface(ABC): + """Interface for messaging platforms (Discord, Teams, PagerDuty, etc.). + + Note: Slack uses MCP server, so this interface is for other providers. + """ + + @abstractmethod + async def send_message(self, channel: str, message: str) -> None: + """Send a message to a channel.""" + raise NotImplementedError diff --git a/src/sre_agent/core/models.py b/src/sre_agent/core/models.py new file mode 100644 index 00000000..5fec44b2 --- /dev/null +++ b/src/sre_agent/core/models.py @@ -0,0 +1,52 @@ +"""Data models for the SRE Agent.""" + +from datetime import datetime + +from pydantic import BaseModel, Field + + +class LogEntry(BaseModel): + """A single log entry from CloudWatch.""" + + timestamp: str = Field(description="ISO 8601 timestamp of the log entry") + message: str = Field(description="The log message content") + log_stream: str | None = Field(default=None, description="The log stream name") + + +class LogQueryResult(BaseModel): + """Result from querying CloudWatch logs.""" + + entries: list[LogEntry] = Field(default_factory=list, description="Log entries found") + log_group: str = Field(description="The log group queried") + query: str = Field(description="The query that was executed") + + +class SuggestedFix(BaseModel): + """A suggested fix for an error.""" + + description: str = Field(description="What the fix involves") + file_path: str | None = Field(default=None, description="File to modify, if applicable") + code_snippet: str | None = Field(default=None, description="Example code change") + + +class ErrorDiagnosis(BaseModel): + """Complete diagnosis of an error from the SRE agent.""" + + summary: str = Field(description="Brief summary of the issue") + root_cause: str = Field(description="Identified root cause") + affected_services: list[str] = Field( + default_factory=list, + description="Services affected by this issue", + ) + suggested_fixes: list[SuggestedFix] = Field( + default_factory=list, + description="Suggested fixes for the issue", + ) + related_logs: list[str] = Field( + default_factory=list, + description="Key log messages related to the issue", + ) + timestamp: datetime = Field( + default_factory=datetime.now, + description="When the diagnosis was created", + ) diff --git a/src/sre_agent/core/prompts.py b/src/sre_agent/core/prompts.py new file mode 100644 index 00000000..8def7fff --- /dev/null +++ b/src/sre_agent/core/prompts.py @@ -0,0 +1,35 @@ +"""System prompts for the SRE Agent.""" + +from pathlib import Path + +from sre_agent.core.config import AgentConfig + +PROMPTS_DIR = Path(__file__).parent / "prompts" + + +def _load_prompt(filename: str) -> str: + """Load a prompt from a text file.""" + return (PROMPTS_DIR / filename).read_text(encoding="utf-8").strip() + + +SYSTEM_PROMPT = _load_prompt("system_prompt.txt") +DIAGNOSIS_PROMPT_TEMPLATE = _load_prompt("diagnosis_prompt.txt") + + +def build_diagnosis_prompt( + config: AgentConfig, + log_group: str, + service_name: str, + time_range_minutes: int = 10, +) -> str: + """Build a diagnosis prompt for the agent.""" + prompt = DIAGNOSIS_PROMPT_TEMPLATE.format( + log_group=log_group, + time_range_minutes=time_range_minutes, + service_display=service_name, + ) + + # Add Slack context + prompt += f"\n\nSlack Context:\n- Channel ID: {config.slack.channel_id}" + + return prompt diff --git a/src/sre_agent/core/prompts/diagnosis_prompt.txt b/src/sre_agent/core/prompts/diagnosis_prompt.txt new file mode 100644 index 00000000..0c2241ad --- /dev/null +++ b/src/sre_agent/core/prompts/diagnosis_prompt.txt @@ -0,0 +1,10 @@ +Diagnose errors in CloudWatch log group '{log_group}' from the last {time_range_minutes} minutes. +Service: {service_display} + +ORDER OF OPERATIONS: +1. Call `conversations_add_message` to start a thread in Slack. +2. Search error logs for {service_display}. +3. If logs found, search GitHub and diagnose. +4. Reply to the Slack thread with the result. + +DO NOT skip step 1. Start the thread before doing anything else. diff --git a/src/sre_agent/core/prompts/system_prompt.txt b/src/sre_agent/core/prompts/system_prompt.txt new file mode 100644 index 00000000..9b629faa --- /dev/null +++ b/src/sre_agent/core/prompts/system_prompt.txt @@ -0,0 +1,28 @@ +You are an expert Site Reliability Engineer (SRE) AI agent. + +STRICT WORKFLOW - YOU MUST FOLLOW THIS SEQUENCE: + +1. **IMMEDIATELY create a Slack Thread**: + - Use the `conversations_add_message` tool. + - Arguments: + - `channel_id`: The Channel ID provided in your context. + - `payload`: "🚨 Error detected in [service_name] - investigating..." + - **MANDATORY**: You MUST capture the returned `ts` (timestamp). + - **ERROR HANDLING**: If this fails with "not_in_channel", stop and inform the user they must invite the bot to the channel with `/invite @bot_name`. + +2. **Diagnose**: + - ONLY after the thread is created, call `search_error_logs`. + - If logs are found, search GitHub for the relevant code. + +3. **Reply to the SAME Thread**: + - Use `conversations_add_message` again. + - Arguments: + - `channel_id`: Same Channel ID. + - `payload`: Your diagnosis (Summary, Root Cause, Suggested Fix). + - `thread_ts`: The `ts` you captured in Step 1. + +CRITICAL RULES: +- Never provide a diagnosis without evidence from logs. +- If `search_error_logs` returns no logs, reply "No error logs found" to the thread (Step 3) and finish. +- Always start with the Slack thread. No thread = No investigation. +- Use `payload` for the message content. diff --git a/src/sre_agent/core/tools/__init__.py b/src/sre_agent/core/tools/__init__.py new file mode 100644 index 00000000..0bee3ac1 --- /dev/null +++ b/src/sre_agent/core/tools/__init__.py @@ -0,0 +1,29 @@ +"""Tool modules for the SRE Agent. + +## Adding a new tool + +Follow one of these patterns: + +1. **MCP Server** + - Just return MCPServerStdio + - No interface implementation needed + - Example: github.py, slack.py + +2. **Direct API** + - Implement the relevant interface from interfaces.py + - Create a FunctionToolset with agent-callable tools + - Example: cloudwatch.py +""" + +from sre_agent.core.tools.cloudwatch import CloudWatchLogging, create_cloudwatch_toolset +from sre_agent.core.tools.github import create_github_mcp_toolset +from sre_agent.core.tools.slack import create_slack_mcp_toolset + +__all__ = [ + # Interface implementations (Direct API) + "CloudWatchLogging", + # Toolset factories + "create_cloudwatch_toolset", + "create_github_mcp_toolset", + "create_slack_mcp_toolset", +] diff --git a/src/sre_agent/core/tools/cloudwatch.py b/src/sre_agent/core/tools/cloudwatch.py new file mode 100644 index 00000000..0f8c286f --- /dev/null +++ b/src/sre_agent/core/tools/cloudwatch.py @@ -0,0 +1,122 @@ +"""CloudWatch implementation of the LoggingInterface.""" + +import logging +from datetime import UTC, datetime, timedelta +from typing import Any + +import boto3 +from botocore.exceptions import ClientError +from pydantic_ai import FunctionToolset + +from sre_agent.core.config import AgentConfig +from sre_agent.core.interfaces import LoggingInterface +from sre_agent.core.models import LogEntry, LogQueryResult + +logger = logging.getLogger(__name__) + + +class CloudWatchLogging(LoggingInterface): + """CloudWatch Logs implementation.""" + + def __init__(self, region: str | None = None) -> None: + """Initialise CloudWatch client.""" + self._client: Any = boto3.client("logs", region_name=region) + + async def query_errors( + self, + source: str, + service_name: str, + time_range_minutes: int = 10, + ) -> LogQueryResult: + """Query error logs from CloudWatch. + + Args: + source: The CloudWatch log group name. + service_name: Service name to filter log entries. + time_range_minutes: How far back to search. + + Returns: + LogQueryResult with matching error entries. + """ + end_time = datetime.now(UTC) + start_time = end_time - timedelta(minutes=time_range_minutes) + + service_filter = service_name.replace('"', '\\"') + filter_pattern = ( + "{ " + '$.log_processed.severity = "error" ' + f'&& $.log_processed.service = "{service_filter}" ' + "}" + ) + + logger.info(f"CloudWatch filter pattern: {filter_pattern}") + logger.info(f"Log Group: {source}") + logger.info(f"Time Range: {start_time} to {end_time}") + + try: + response = self._client.filter_log_events( + logGroupName=source, + startTime=int(start_time.timestamp() * 1000), + endTime=int(end_time.timestamp() * 1000), + filterPattern=filter_pattern, + limit=20, + ) + entries = self._parse_events(response.get("events", [])) + logger.info(f"Found {len(entries)} log entries") + + return LogQueryResult( + entries=entries, + log_group=source, + query=filter_pattern, + ) + except ClientError as e: + logger.error(f"CloudWatch query failed: {e}") + raise RuntimeError(f"Failed to query CloudWatch: {e}") from e + except Exception as e: + logger.error(f"Unexpected error: {e}") + raise RuntimeError(f"Unexpected error querying logs: {e}") from e + + def _parse_events(self, events: list[dict[str, Any]]) -> list[LogEntry]: + """Parse filter_log_events entries into LogEntry objects.""" + entries = [] + for event in events: + timestamp_ms = event.get("timestamp") + if timestamp_ms is None: + timestamp = "" + else: + timestamp = datetime.fromtimestamp(timestamp_ms / 1000, UTC).isoformat() + entries.append( + LogEntry( + timestamp=timestamp, + message=event.get("message", ""), + log_stream=event.get("logStreamName"), + ) + ) + entries.sort(key=lambda entry: entry.timestamp, reverse=True) + return entries + + +def create_cloudwatch_toolset(config: AgentConfig) -> FunctionToolset: + """Create a FunctionToolset with CloudWatch tools for pydantic-ai.""" + toolset = FunctionToolset() + cw_logging = CloudWatchLogging(region=config.aws.region) + + @toolset.tool + async def search_error_logs( + log_group: str, + service_name: str, + time_range_minutes: int = 10, + ) -> LogQueryResult: + """Search CloudWatch logs for errors. + + Args: + log_group: The CloudWatch log group name + service_name: Service name to filter log entries (e.g., 'cartservice') + time_range_minutes: How far back to search (default: 10 minutes) + + Returns: + LogQueryResult containing matching error log entries + """ + return await cw_logging.query_errors(log_group, service_name, time_range_minutes) + + return toolset diff --git a/src/sre_agent/core/tools/github.py b/src/sre_agent/core/tools/github.py new file mode 100644 index 00000000..78fca83d --- /dev/null +++ b/src/sre_agent/core/tools/github.py @@ -0,0 +1,29 @@ +"""GitHub integration using MCP server via Streamable HTTP.""" + +import logging + +from pydantic_ai.mcp import MCPServerStreamableHTTP + +from sre_agent.core.config import AgentConfig + +logger = logging.getLogger(__name__) + + +def create_github_mcp_toolset(config: AgentConfig) -> MCPServerStreamableHTTP: + """Create GitHub MCP server toolset for pydantic-ai. + + Connects to an external GitHub MCP server via Streamable HTTP. + """ + if not config.github.mcp_url: + logger.warning("GITHUB_MCP_URL not set, GitHub tools will be unavailable") + + logger.info(f"Connecting to GitHub MCP server (Streamable HTTP) at {config.github.mcp_url}") + + # spellchecker:ignore-next-line + headers = {"Authorization": f"Bearer {config.github.personal_access_token}"} + + return MCPServerStreamableHTTP( + config.github.mcp_url, + timeout=60, + headers=headers, + ) diff --git a/src/sre_agent/core/tools/slack.py b/src/sre_agent/core/tools/slack.py new file mode 100644 index 00000000..dd7c591e --- /dev/null +++ b/src/sre_agent/core/tools/slack.py @@ -0,0 +1,30 @@ +"""Slack integration using korotovsky/slack-mcp-server.""" + +import logging + +from pydantic_ai.mcp import MCPServerSSE +from pydantic_ai.toolsets import FilteredToolset + +from sre_agent.core.config import AgentConfig + +logger = logging.getLogger(__name__) + +# Only these tools are allowed for the agent +ALLOWED_SLACK_TOOLS = {"conversations_add_message"} + + +def create_slack_mcp_toolset(config: AgentConfig) -> FilteredToolset: + """Create Slack MCP server toolset for pydantic-ai. + + Connects to an external Slack MCP server via SSE. + """ + if not config.slack.mcp_url: + logger.warning("SLACK_MCP_URL not set, Slack tools will be unavailable") + + logger.info(f"Connecting to Slack MCP server at {config.slack.mcp_url}") + + # Increase timeout to 60s for SSE tools + mcp_server = MCPServerSSE(config.slack.mcp_url, timeout=60) + + # Filter to only allowed tools + return mcp_server.filtered(filter_func=lambda _ctx, tool: tool.name in ALLOWED_SLACK_TOOLS) diff --git a/typos.toml b/typos.toml new file mode 100644 index 00000000..4fcc7e83 --- /dev/null +++ b/typos.toml @@ -0,0 +1,22 @@ +[files] +extend-exclude = [ + ".gitignore", + "LICENSE", + "legacy", + "*.csv" +] +ignore-hidden = true +ignore-dot = true + +[default] +locale = "en-gb" +extend-ignore-re = [ + # Ignore lines that end with `# spellchecker:disable-line` + "(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", + # Ignore lines with HTML `` + "(?Rm)^.*.*$", + # Ignore the line after `# spellchecker:ignore-next-line`: + "(#|//)\\s*spellchecker:ignore-next-line\\n.*", + # Ignore blocks between `# spellchecker:off` and `# spellchecker:on` + "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on", +] diff --git a/uv.lock b/uv.lock index 5120b808..8851b5ed 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,115 @@ version = 1 revision = 3 -requires-python = ">=3.12, <4.0" +requires-python = ">=3.13, <4.0" -[manifest] -members = [ - "client", - "firewall", - "llm", - "prompt-server", - "sre-agent", +[[package]] +name = "ag-ui-protocol" +version = "0.1.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/bb/5a5ec893eea5805fb9a3db76a9888c3429710dfb6f24bbb37568f2cf7320/ag_ui_protocol-0.1.10.tar.gz", hash = "sha256:3213991c6b2eb24bb1a8c362ee270c16705a07a4c5962267a083d0959ed894f4", size = 6945, upload-time = "2025-11-06T15:17:17.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/78/eb55fabaab41abc53f52c0918a9a8c0f747807e5306273f51120fd695957/ag_ui_protocol-0.1.10-py3-none-any.whl", hash = "sha256:c81e6981f30aabdf97a7ee312bfd4df0cd38e718d9fc10019c7d438128b93ab5", size = 7889, upload-time = "2025-11-06T15:17:15.325Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, ] [[package]] @@ -22,459 +123,713 @@ wheels = [ [[package]] name = "anthropic" -version = "0.53.0" +version = "0.77.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "distro" }, + { name = "docstring-parser" }, { name = "httpx" }, { name = "jiter" }, { name = "pydantic" }, { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/f6/a78ff9e23981fde136c3ae5427a39b27df92ebe5e5997c6203796449f1e5/anthropic-0.53.0.tar.gz", hash = "sha256:f5d1499fc45b2e05801fcbbeae25679f72f7479763e3c706126a7a7c8de06eff", size = 307716, upload-time = "2025-06-09T16:20:31.689Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/85/6cb5da3cf91de2eeea89726316e8c5c8c31e2d61ee7cb1233d7e95512c31/anthropic-0.77.0.tar.gz", hash = "sha256:ce36efeb80cb1e25430a88440dc0f9aa5c87f10d080ab70a1bdfd5c2c5fbedb4", size = 504575, upload-time = "2026-01-29T18:20:41.507Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/3f/82c21f74afa3541d69d20b8265c7fdfd078a687e9eea48fda30f1838d0b7/anthropic-0.53.0-py3-none-any.whl", hash = "sha256:b3a84751885a81d96bbddef180c3ce559c9140f7f230cdd825385405bd6d312e", size = 287248, upload-time = "2025-06-09T16:20:29.98Z" }, + { url = "https://files.pythonhosted.org/packages/ac/27/9df785d3f94df9ac72f43ee9e14b8120b37d992b18f4952774ed46145022/anthropic-0.77.0-py3-none-any.whl", hash = "sha256:65cc83a3c82ce622d5c677d0d7706c77d29dc83958c6b10286e12fda6ffb2651", size = 397867, upload-time = "2026-01-29T18:20:39.481Z" }, ] [[package]] name = "anyio" -version = "4.9.0" +version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, - { name = "sniffio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] -name = "appdirs" -version = "1.4.4" +name = "argcomplete" +version = "3.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, ] [[package]] name = "attrs" -version = "23.2.0" +version = "25.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820, upload-time = "2023-12-31T06:30:32.926Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752, upload-time = "2023-12-31T06:30:30.772Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] [[package]] -name = "boltons" -version = "21.0.0" +name = "authlib" +version = "1.6.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ad/1f/6c0608d86e0fc77c982a2923ece80eef85f091f2332fc13cbce41d70d502/boltons-21.0.0.tar.gz", hash = "sha256:65e70a79a731a7fe6e98592ecfb5ccf2115873d01dbc576079874629e5c90f13", size = 180201, upload-time = "2021-05-17T01:20:17.802Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/a7/1a31561d10a089fcb46fe286766dd4e053a12f6e23b4fd1c26478aff2475/boltons-21.0.0-py2.py3-none-any.whl", hash = "sha256:b9bb7b58b2b420bbe11a6025fdef6d3e5edc9f76a42fb467afe7ca212ef9948b", size = 193723, upload-time = "2021-05-17T01:20:20.023Z" }, +dependencies = [ + { name = "cryptography" }, ] - -[[package]] -name = "bracex" -version = "2.5.post1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/6c/57418c4404cd22fe6275b8301ca2b46a8cdaa8157938017a9ae0b3edf363/bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6", size = 26641, upload-time = "2024-09-28T21:41:22.017Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/02/8db98cdc1a58e0abd6716d5e63244658e6e63513c65f469f34b6f1053fd0/bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6", size = 11558, upload-time = "2024-09-28T21:41:21.016Z" }, + { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, ] [[package]] -name = "braq" -version = "0.0.12" +name = "beartype" +version = "0.22.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/3b/1b918c408e11ca33f9b9dcecc8e08eac7762887dd42b584f0efb6fe26c55/braq-0.0.12.tar.gz", hash = "sha256:51dae51b863cbba2cd37da163df06b7dc5124904d2c26b92bda54c1bde66d74b", size = 15272, upload-time = "2024-12-10T20:48:53.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/53/ed5082619966b1d15b5c039ac722ba99956d92d4b08a9bd5eb4c3535cc1f/braq-0.0.12-py3-none-any.whl", hash = "sha256:41b7bdd0d004faef693751615fbb11c53ac0b886c772b83aea61ea6dc2f6e518", size = 26392, upload-time = "2024-12-10T20:48:50.813Z" }, + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, ] [[package]] -name = "cachetools" -version = "5.5.2" +name = "boto3" +version = "1.42.39" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380, upload-time = "2025-02-20T21:01:19.524Z" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/ea/b96c77da49fed28744ee0347374d8223994a2b8570e76e8380a4064a8c4a/boto3-1.42.39.tar.gz", hash = "sha256:d03f82363314759eff7f84a27b9e6428125f89d8119e4588e8c2c1d79892c956", size = 112783, upload-time = "2026-01-30T20:38:31.226Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080, upload-time = "2025-02-20T21:01:16.647Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c4/3493b5c86e32d6dd558b30d16b55503e24a6e6cd7115714bc102b247d26e/boto3-1.42.39-py3-none-any.whl", hash = "sha256:d9d6ce11df309707b490d2f5f785b761cfddfd6d1f665385b78c9d8ed097184b", size = 140606, upload-time = "2026-01-30T20:38:28.635Z" }, ] [[package]] -name = "cattrs" -version = "24.1.3" +name = "botocore" +version = "1.42.39" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/7b/da4aa2f95afb2f28010453d03d6eedf018f9e085bd001f039e15731aba89/cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff", size = 426684, upload-time = "2025-03-25T15:01:00.325Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/a6/3a34d1b74effc0f759f5ff4e91c77729d932bc34dd3207905e9ecbba1103/botocore-1.42.39.tar.gz", hash = "sha256:0f00355050821e91a5fe6d932f7bf220f337249b752899e3e4cf6ed54326249e", size = 14914927, upload-time = "2026-01-30T20:38:19.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ee/d68a3de23867a9156bab7e0a22fb9a0305067ee639032a22982cf7f725e7/cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5", size = 66462, upload-time = "2025-03-25T15:00:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/9a2c88abb5fe47b46168b262254d5b5d635de371eba4bd01ea5c8c109575/botocore-1.42.39-py3-none-any.whl", hash = "sha256:9e0d0fed9226449cc26fcf2bbffc0392ac698dd8378e8395ce54f3ec13f81d58", size = 14591958, upload-time = "2026-01-30T20:38:14.814Z" }, ] [[package]] -name = "certifi" -version = "2025.4.26" +name = "cachetools" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/af/df70e9b65bc77a1cbe0768c0aa4617147f30f8306ded98c1744bcdc0ae1e/cachetools-7.0.0.tar.gz", hash = "sha256:a9abf18ff3b86c7d05b27ead412e235e16ae045925e531fae38d5fada5ed5b08", size = 35796, upload-time = "2026-02-01T18:59:47.411Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, + { url = "https://files.pythonhosted.org/packages/28/df/2dd32cce20cbcf6f2ec456b58d44368161ad28320729f64e5e1d5d7bd0ae/cachetools-7.0.0-py3-none-any.whl", hash = "sha256:d52fef60e6e964a1969cfb61ccf6242a801b432790fe520d78720d757c81cbd2", size = 13487, upload-time = "2026-02-01T18:59:45.981Z" }, ] [[package]] -name = "cfgv" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +name = "certifi" +version = "2026.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, - { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, - { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, - { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, - { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, - { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, - { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, - { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, - { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, - { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, - { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, - { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, - { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, - { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, - { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, - { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, ] [[package]] name = "click" -version = "8.2.1" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] -name = "click-option-group" -version = "0.5.7" +name = "cloudpickle" +version = "3.1.2" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/9f/1f917934da4e07ae7715a982347e3c2179556d8a58d1108c5da3e8f09c76/click_option_group-0.5.7.tar.gz", hash = "sha256:8dc780be038712fc12c9fecb3db4fe49e0d0723f9c171d7cda85c20369be693c", size = 22110, upload-time = "2025-03-24T13:24:55.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/27/bf74dc1494625c3b14dbcdb93740defd7b8c58dae3736be8d264f2a643fb/click_option_group-0.5.7-py3-none-any.whl", hash = "sha256:96b9f52f397ef4d916f81929bd6c1f85e89046c7a401a64e72a61ae74ad35c24", size = 11483, upload-time = "2025-03-24T13:24:54.611Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, ] [[package]] -name = "client" -version = "0.1.0" -source = { virtual = "sre_agent/client" } +name = "cohere" +version = "5.20.2" +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "fastapi" }, - { name = "huggingface-hub" }, - { name = "llamafirewall" }, - { name = "mcp", extra = ["cli"] }, - { name = "python-dotenv" }, - { name = "python-multipart" }, + { name = "fastavro" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "pydantic-core" }, { name = "requests" }, - { name = "shared" }, + { name = "tokenizers" }, { name = "types-requests" }, - { name = "uvicorn" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/52/08564d1820970010d30421cd6e36f2e4ca552646504d3fe532eef282c88d/cohere-5.20.2.tar.gz", hash = "sha256:0aa9f3735626b70eedf15c231c61f3a58e7f8bbe5f0509fe7b2e6606c5d420f1", size = 180820, upload-time = "2026-01-23T13:42:51.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/10/d76f045eefe42fb3f4e271d17ab41b5e73a3b6de69c98e15ab1cb0c8e6f6/cohere-5.20.2-py3-none-any.whl", hash = "sha256:26156d83bf3e3e4475e4caa1d8c4148475c5b0a253aee6066d83c643e9045be6", size = 318986, upload-time = "2026-01-23T13:42:50.151Z" }, ] -[package.metadata] -requires-dist = [ - { name = "fastapi", specifier = ">=0.115.12" }, - { name = "huggingface-hub" }, - { name = "llamafirewall", specifier = ">=1.0.2" }, - { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, - { name = "python-dotenv", specifier = ">=1.1.0" }, - { name = "python-multipart", specifier = ">=0.0.20" }, - { name = "requests", specifier = ">=2.32.3" }, - { name = "shared" }, - { name = "types-requests", specifier = ">=2.32.0.20250328" }, - { name = "uvicorn", specifier = ">=0.34.2" }, +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] -name = "codeshield" -version = "1.0.1" +name = "cryptography" +version = "46.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, + { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, + { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, + { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, + { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, + { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, + { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, + { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, + { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, + { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, + { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, + { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, + { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, + { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, + { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, + { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, + { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, + { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, +] + +[[package]] +name = "cyclopts" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml" }, - { name = "semgrep" }, + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/0e/cb79d48ba05eda459a5a2e90b6056019cf7f41441cdee2a17e8dd63e5502/codeshield-1.0.1.tar.gz", hash = "sha256:61866b9281c506f9e176995408daab931d52832e625f6056bba273e80a81139f", size = 274198, upload-time = "2024-04-19T15:10:35.326Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/a8/1ce1dcbdc8593e04048b1ea469db8eb92783e82e351405a375e929979f0b/codeshield-1.0.1-py3-none-any.whl", hash = "sha256:cd3516a5006002e0e7400a98e5a4592256a37ce6caf5e162d45ed093eb548377", size = 173368, upload-time = "2024-04-19T15:10:33.413Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, ] [[package]] -name = "colorama" -version = "0.4.6" +name = "diskcache" +version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, ] [[package]] -name = "coverage" -version = "7.8.2" +name = "distro" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, - { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, - { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, - { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, - { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, - { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, - { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, - { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, - { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, - { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, - { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, - { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, - { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, - { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, - { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, - { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, - { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, - { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, - { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, - { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, - { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, - { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, - { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, - { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] [[package]] -name = "defusedxml" -version = "0.7.1" +name = "dnspython" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] [[package]] -name = "deprecated" -version = "1.2.18" +name = "docstring-parser" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } + +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, ] [[package]] -name = "distlib" -version = "0.3.9" +name = "email-validator" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, ] [[package]] -name = "distro" -version = "1.9.0" +name = "eval-type-backport" +version = "0.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/a3/cafafb4558fd638aadfe4121dc6cefb8d743368c085acb2f521df0f3d9d7/eval_type_backport-0.3.1.tar.gz", hash = "sha256:57e993f7b5b69d271e37482e62f74e76a0276c82490cf8e4f0dffeb6b332d5ed", size = 9445, upload-time = "2025-12-02T11:51:42.987Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, + { url = "https://files.pythonhosted.org/packages/cf/22/fdc2e30d43ff853720042fa15baa3e6122722be1a7950a98233ebb55cd71/eval_type_backport-0.3.1-py3-none-any.whl", hash = "sha256:279ab641905e9f11129f56a8a78f493518515b83402b860f6f06dd7c011fdfa8", size = 6063, upload-time = "2025-12-02T11:51:41.665Z" }, ] [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] -name = "face" -version = "24.0.0" +name = "executing" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "boltons" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/79/2484075a8549cd64beae697a8f664dee69a5ccf3a7439ee40c8f93c1978a/face-24.0.0.tar.gz", hash = "sha256:611e29a01ac5970f0077f9c577e746d48c082588b411b33a0dd55c4d872949f6", size = 62732, upload-time = "2024-11-02T05:24:26.095Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/47/21867c2e5fd006c8d36a560df9e32cb4f1f566b20c5dd41f5f8a2124f7de/face-24.0.0-py3-none-any.whl", hash = "sha256:0e2c17b426fa4639a4e77d1de9580f74a98f4869ba4c7c8c175b810611622cd3", size = 54742, upload-time = "2024-11-02T05:24:24.939Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] -name = "fastapi" -version = "0.115.12" +name = "fakeredis" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, + { name = "redis" }, + { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236, upload-time = "2025-03-23T22:55:43.822Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, +] + +[package.optional-dependencies] +lua = [ + { name = "lupa" }, ] [[package]] -name = "fhconfparser" -version = "2024.1" +name = "fastapi" +version = "0.128.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, - { name = "tomli" }, + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4b/b3/ca177719df2db0050599c576023858b86cabe4f54d3beda0e7e673a6892f/fhconfparser-2024.1.tar.gz", hash = "sha256:de8af019f0071e614d523985e1d93e0fce20a409d1c64dead03b1b665d4b2e4d", size = 8357, upload-time = "2024-01-24T21:48:56.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/2b/fd360e1b65ba44179424aa0a8c227c17d7df384f20bb8d38a5cbe23e3ba2/fhconfparser-2024.1-py3-none-any.whl", hash = "sha256:f6048cb646e69a3422a581bc0102150c2b79fe7ff26b82233e5ef52f72820e3e", size = 9221, upload-time = "2024-01-24T21:48:54.81Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, ] [[package]] -name = "filelock" -version = "3.18.0" +name = "fastavro" +version = "1.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/8b/fa2d3287fd2267be6261d0177c6809a7fa12c5600ddb33490c8dc29e77b2/fastavro-1.12.1.tar.gz", hash = "sha256:2f285be49e45bc047ab2f6bed040bb349da85db3f3c87880e4b92595ea093b2b", size = 1025661, upload-time = "2025-10-10T15:40:55.41Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, + { url = "https://files.pythonhosted.org/packages/bb/57/26d5efef9182392d5ac9f253953c856ccb66e4c549fd3176a1e94efb05c9/fastavro-1.12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:78df838351e4dff9edd10a1c41d1324131ffecbadefb9c297d612ef5363c049a", size = 1000599, upload-time = "2025-10-10T15:41:36.554Z" }, + { url = "https://files.pythonhosted.org/packages/33/cb/8ab55b21d018178eb126007a56bde14fd01c0afc11d20b5f2624fe01e698/fastavro-1.12.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:780476c23175d2ae457c52f45b9ffa9d504593499a36cd3c1929662bf5b7b14b", size = 3335933, upload-time = "2025-10-10T15:41:39.07Z" }, + { url = "https://files.pythonhosted.org/packages/fe/03/9c94ec9bf873eb1ffb0aa694f4e71940154e6e9728ddfdc46046d7e8ced4/fastavro-1.12.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0714b285160fcd515eb0455540f40dd6dac93bdeacdb03f24e8eac3d8aa51f8d", size = 3402066, upload-time = "2025-10-10T15:41:41.608Z" }, + { url = "https://files.pythonhosted.org/packages/75/c8/cb472347c5a584ccb8777a649ebb28278fccea39d005fc7df19996f41df8/fastavro-1.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a8bc2dcec5843d499f2489bfe0747999108f78c5b29295d877379f1972a3d41a", size = 3240038, upload-time = "2025-10-10T15:41:43.743Z" }, + { url = "https://files.pythonhosted.org/packages/e1/77/569ce9474c40304b3a09e109494e020462b83e405545b78069ddba5f614e/fastavro-1.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b1921ac35f3d89090a5816b626cf46e67dbecf3f054131f84d56b4e70496f45", size = 3369398, upload-time = "2025-10-10T15:41:45.719Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1f/9589e35e9ea68035385db7bdbf500d36b8891db474063fb1ccc8215ee37c/fastavro-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:5aa777b8ee595b50aa084104cd70670bf25a7bbb9fd8bb5d07524b0785ee1699", size = 444220, upload-time = "2025-10-10T15:41:47.39Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d2/78435fe737df94bd8db2234b2100f5453737cffd29adee2504a2b013de84/fastavro-1.12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c3d67c47f177e486640404a56f2f50b165fe892cc343ac3a34673b80cc7f1dd6", size = 1086611, upload-time = "2025-10-10T15:41:48.818Z" }, + { url = "https://files.pythonhosted.org/packages/b6/be/428f99b10157230ddac77ec8cc167005b29e2bd5cbe228345192bb645f30/fastavro-1.12.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5217f773492bac43dae15ff2931432bce2d7a80be7039685a78d3fab7df910bd", size = 3541001, upload-time = "2025-10-10T15:41:50.871Z" }, + { url = "https://files.pythonhosted.org/packages/16/08/a2eea4f20b85897740efe44887e1ac08f30dfa4bfc3de8962bdcbb21a5a1/fastavro-1.12.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:469fecb25cba07f2e1bfa4c8d008477cd6b5b34a59d48715e1b1a73f6160097d", size = 3432217, upload-time = "2025-10-10T15:41:53.149Z" }, + { url = "https://files.pythonhosted.org/packages/87/bb/b4c620b9eb6e9838c7f7e4b7be0762834443adf9daeb252a214e9ad3178c/fastavro-1.12.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d71c8aa841ef65cfab709a22bb887955f42934bced3ddb571e98fdbdade4c609", size = 3366742, upload-time = "2025-10-10T15:41:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d1/e69534ccdd5368350646fea7d93be39e5f77c614cca825c990bd9ca58f67/fastavro-1.12.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b81fc04e85dfccf7c028e0580c606e33aa8472370b767ef058aae2c674a90746", size = 3383743, upload-time = "2025-10-10T15:41:57.68Z" }, + { url = "https://files.pythonhosted.org/packages/58/54/b7b4a0c3fb5fcba38128542da1b26c4e6d69933c923f493548bdfd63ab6a/fastavro-1.12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9445da127751ba65975d8e4bdabf36bfcfdad70fc35b2d988e3950cce0ec0e7c", size = 1001377, upload-time = "2025-10-10T15:41:59.241Z" }, + { url = "https://files.pythonhosted.org/packages/1e/4f/0e589089c7df0d8f57d7e5293fdc34efec9a3b758a0d4d0c99a7937e2492/fastavro-1.12.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed924233272719b5d5a6a0b4d80ef3345fc7e84fc7a382b6232192a9112d38a6", size = 3320401, upload-time = "2025-10-10T15:42:01.682Z" }, + { url = "https://files.pythonhosted.org/packages/f9/19/260110d56194ae29d7e423a336fccea8bcd103196d00f0b364b732bdb84e/fastavro-1.12.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3616e2f0e1c9265e92954fa099db79c6e7817356d3ff34f4bcc92699ae99697c", size = 3350894, upload-time = "2025-10-10T15:42:04.073Z" }, + { url = "https://files.pythonhosted.org/packages/d0/96/58b0411e8be9694d5972bee3167d6c1fd1fdfdf7ce253c1a19a327208f4f/fastavro-1.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cb0337b42fd3c047fcf0e9b7597bd6ad25868de719f29da81eabb6343f08d399", size = 3229644, upload-time = "2025-10-10T15:42:06.221Z" }, + { url = "https://files.pythonhosted.org/packages/5b/db/38660660eac82c30471d9101f45b3acfdcbadfe42d8f7cdb129459a45050/fastavro-1.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:64961ab15b74b7c168717bbece5660e0f3d457837c3cc9d9145181d011199fa7", size = 3329704, upload-time = "2025-10-10T15:42:08.384Z" }, + { url = "https://files.pythonhosted.org/packages/9d/a9/1672910f458ecb30b596c9e59e41b7c00309b602a0494341451e92e62747/fastavro-1.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:792356d320f6e757e89f7ac9c22f481e546c886454a6709247f43c0dd7058004", size = 452911, upload-time = "2025-10-10T15:42:09.795Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8d/2e15d0938ded1891b33eff252e8500605508b799c2e57188a933f0bd744c/fastavro-1.12.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:120aaf82ac19d60a1016afe410935fe94728752d9c2d684e267e5b7f0e70f6d9", size = 3541999, upload-time = "2025-10-10T15:42:11.794Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1c/6dfd082a205be4510543221b734b1191299e6a1810c452b6bc76dfa6968e/fastavro-1.12.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6a3462934b20a74f9ece1daa49c2e4e749bd9a35fa2657b53bf62898fba80f5", size = 3433972, upload-time = "2025-10-10T15:42:14.485Z" }, + { url = "https://files.pythonhosted.org/packages/24/90/9de694625a1a4b727b1ad0958d220cab25a9b6cf7f16a5c7faa9ea7b2261/fastavro-1.12.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1f81011d54dd47b12437b51dd93a70a9aa17b61307abf26542fc3c13efbc6c51", size = 3368752, upload-time = "2025-10-10T15:42:16.618Z" }, + { url = "https://files.pythonhosted.org/packages/fa/93/b44f67589e4d439913dab6720f7e3507b0fa8b8e56d06f6fc875ced26afb/fastavro-1.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43ded16b3f4a9f1a42f5970c2aa618acb23ea59c4fcaa06680bdf470b255e5a8", size = 3386636, upload-time = "2025-10-10T15:42:18.974Z" }, ] [[package]] -name = "firewall" -version = "0.1.0" -source = { virtual = "sre_agent/firewall" } +name = "fastmcp" +version = "2.14.4" +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "fastapi" }, - { name = "huggingface-hub", extra = ["hf-xet"] }, - { name = "llamafirewall" }, - { name = "pydantic" }, - { name = "transformers" }, + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "jsonref" }, + { name = "jsonschema-path" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, + { name = "pydantic", extra = ["email"] }, + { name = "pydocket" }, + { name = "pyperclip" }, + { name = "python-dotenv" }, + { name = "rich" }, { name = "uvicorn" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/a9/a57d5e5629ebd4ef82b495a7f8e346ce29ef80cc86b15c8c40570701b94d/fastmcp-2.14.4.tar.gz", hash = "sha256:c01f19845c2adda0a70d59525c9193be64a6383014c8d40ce63345ac664053ff", size = 8302239, upload-time = "2026-01-22T17:29:37.024Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/41/c4d407e2218fd60d84acb6cc5131d28ff876afecf325e3fd9d27b8318581/fastmcp-2.14.4-py3-none-any.whl", hash = "sha256:5858cff5e4c8ea8107f9bca2609d71d6256e0fce74495912f6e51625e466c49a", size = 417788, upload-time = "2026-01-22T17:29:35.159Z" }, ] -[package.metadata] -requires-dist = [ - { name = "fastapi", specifier = ">=0.115.12" }, - { name = "huggingface-hub", extras = ["hf-xet"], specifier = ">=0.31.1" }, - { name = "llamafirewall", specifier = ">=1.0.2" }, - { name = "pydantic", specifier = ">=2.11.3" }, - { name = "transformers", specifier = ">=4.51.3" }, - { name = "uvicorn", specifier = ">=0.34.2" }, +[[package]] +name = "filelock" +version = "3.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] [[package]] name = "fsspec" -version = "2025.5.1" +version = "2026.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033, upload-time = "2025-05-24T12:03:23.792Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052, upload-time = "2025-05-24T12:03:21.66Z" }, + { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, ] [[package]] -name = "glom" -version = "22.1.0" +name = "genai-prices" +version = "0.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, - { name = "boltons" }, - { name = "face" }, + { name = "httpx" }, + { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/d1/69432deefa6f5283ec75b246d0540097ae26f618b915519ee3824c4c5dd6/glom-22.1.0.tar.gz", hash = "sha256:1510c6587a8f9c64a246641b70033cbc5ebde99f02ad245693678038e821aeb5", size = 189738, upload-time = "2022-01-24T09:34:04.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/87/bdc11c1671e3a3fe701c3c4aaae4aa2bb7a84a6bb1812dfb5693c87d3872/genai_prices-0.0.52.tar.gz", hash = "sha256:0df7420b555fa3a48d09e5c7802ba35b5dfa9fd49b0c3bb2c150c59060d83f52", size = 58364, upload-time = "2026-01-28T12:07:49.386Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/e8/68e274b2a30e1fdfd25bdc27194382be3f233929c8f727c0440d58ac074f/glom-22.1.0-py2.py3-none-any.whl", hash = "sha256:5339da206bf3532e01a83a35aca202960ea885156986d190574b779598e9e772", size = 100687, upload-time = "2022-01-24T09:34:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/35/33/6316b4907a0bffc1bcc99074c7e2d01184fdfeee401c864146a40d55ad10/genai_prices-0.0.52-py3-none-any.whl", hash = "sha256:639e7a2ae7eddf5710febb9779b9c9e31ff5acf464b4eb1f6018798ea642e6d3", size = 60937, upload-time = "2026-01-28T12:07:47.921Z" }, ] [[package]] name = "google-auth" -version = "2.40.3" +version = "2.48.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachetools" }, + { name = "cryptography" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/9b/e92ef23b84fa10a64ce4831390b7a4c2e53c0132568d99d4ae61d04c8855/google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77", size = 281029, upload-time = "2025-06-04T18:04:57.577Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/41/242044323fbd746615884b1c16639749e73665b718209946ebad7ba8a813/google_auth-2.48.0.tar.gz", hash = "sha256:4f7e706b0cd3208a3d940a19a822c37a476ddba5450156c3e6624a71f7c841ce", size = 326522, upload-time = "2026-01-26T19:22:47.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/63/b19553b658a1692443c62bd07e5868adaa0ad746a0751ba62c59568cd45b/google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca", size = 216137, upload-time = "2025-06-04T18:04:55.573Z" }, + { url = "https://files.pythonhosted.org/packages/83/1d/d6466de3a5249d35e832a52834115ca9d1d0de6abc22065f049707516d47/google_auth-2.48.0-py3-none-any.whl", hash = "sha256:2e2a537873d449434252a9632c28bfc268b0adb1e53f9fb62afc5333a975903f", size = 236499, upload-time = "2026-01-26T19:22:45.099Z" }, +] + +[package.optional-dependencies] +requests = [ + { name = "requests" }, ] [[package]] name = "google-genai" -version = "1.22.0" +version = "1.61.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "google-auth" }, + { name = "distro" }, + { name = "google-auth", extra = ["requests"] }, { name = "httpx" }, { name = "pydantic" }, { name = "requests" }, + { name = "sniffio" }, { name = "tenacity" }, { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/37/98742eeae25556d7558f336f9cdbb8e7276d32a5699b03cabc3ffa9f12ea/google_genai-1.22.0.tar.gz", hash = "sha256:1ece195e7be97cb94dbecce43dd88e3f4e376afd31045e54d1dd0ef272a6ee6b", size = 221720, upload-time = "2025-06-26T00:09:27.666Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/38/421cd7e70952a536be87a0249409f87297d84f523754a25b08fe94b97e7f/google_genai-1.61.0.tar.gz", hash = "sha256:5773a4e8ad5b2ebcd54a633a67d8e9c4f413032fef07977ee47ffa34a6d3bbdf", size = 489672, upload-time = "2026-01-30T20:50:27.177Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/fa/ad39a0457a9c3e21438062076cc216d41c4f8b414aa3d2ec481c721ca5f7/google_genai-1.22.0-py3-none-any.whl", hash = "sha256:6627bea9451775a2af78c6cb1992f5a31b90c50d64fb1f1435a385737a69fce4", size = 222848, upload-time = "2025-06-26T00:09:25.955Z" }, + { url = "https://files.pythonhosted.org/packages/0e/87/78dd70cb59f7acf3350f53c5144a7aa7bc39c6f425cd7dc1224b59fcdac3/google_genai-1.61.0-py3-none-any.whl", hash = "sha256:cb073ef8287581476c1c3f4d8e735426ee34478e500a56deef218fa93071e3ca", size = 721948, upload-time = "2026-01-30T20:50:25.551Z" }, ] [[package]] name = "googleapis-common-protos" -version = "1.70.0" +version = "1.72.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" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, +] + +[[package]] +name = "griffe" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112, upload-time = "2025-11-10T15:03:15.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" }, +] + +[[package]] +name = "groq" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/12/f4099a141677fcd2ed79dcc1fcec431e60c52e0e90c9c5d935f0ffaf8c0e/groq-1.0.0.tar.gz", hash = "sha256:66cb7bb729e6eb644daac7ce8efe945e99e4eb33657f733ee6f13059ef0c25a9", size = 146068, upload-time = "2025-12-17T23:34:23.115Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/4a/88/3175759d2ef30406ea721f4d837bfa1ba4339fde3b81ba8c5640a96ed231/groq-1.0.0-py3-none-any.whl", hash = "sha256:6e22bf92ffad988f01d2d4df7729add66b8fd5dbfb2154b5bbf3af245b72c731", size = 138292, upload-time = "2025-12-17T23:34:21.957Z" }, +] + +[[package]] +name = "grpcio" +version = "1.76.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" }, + { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" }, + { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" }, + { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" }, + { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" }, + { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" }, + { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" }, + { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" }, + { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" }, + { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" }, + { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" }, + { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" }, + { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" }, + { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" }, + { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" }, ] [[package]] @@ -488,17 +843,31 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.1.3" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/dc/dc091aeeb671e71cbec30e84963f9c0202c17337b24b0a800e7d205543e8/hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3", size = 488127, upload-time = "2025-06-04T00:47:27.456Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/1f/bc01a4c0894973adebbcd4aa338a06815c76333ebb3921d94dcbd40dae6a/hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b", size = 2256929, upload-time = "2025-06-04T00:47:21.206Z" }, - { url = "https://files.pythonhosted.org/packages/78/07/6ef50851b5c6b45b77a6e018fa299c69a2db3b8bbd0d5af594c0238b1ceb/hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1", size = 2153719, upload-time = "2025-06-04T00:47:19.302Z" }, - { url = "https://files.pythonhosted.org/packages/52/48/e929e6e3db6e4758c2adf0f2ca2c59287f1b76229d8bdc1a4c9cfc05212e/hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534", size = 4820519, upload-time = "2025-06-04T00:47:17.244Z" }, - { url = "https://files.pythonhosted.org/packages/28/2e/03f89c5014a5aafaa9b150655f811798a317036646623bdaace25f485ae8/hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841", size = 4964121, upload-time = "2025-06-04T00:47:15.17Z" }, - { url = "https://files.pythonhosted.org/packages/47/8b/5cd399a92b47d98086f55fc72d69bc9ea5e5c6f27a9ed3e0cdd6be4e58a3/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b", size = 5283017, upload-time = "2025-06-04T00:47:23.239Z" }, - { url = "https://files.pythonhosted.org/packages/53/e3/2fcec58d2fcfd25ff07feb876f466cfa11f8dcf9d3b742c07fe9dd51ee0a/hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1", size = 4970349, upload-time = "2025-06-04T00:47:25.383Z" }, - { url = "https://files.pythonhosted.org/packages/53/bf/10ca917e335861101017ff46044c90e517b574fbb37219347b83be1952f6/hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3", size = 2310934, upload-time = "2025-06-04T00:47:29.632Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" }, + { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" }, + { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" }, + { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" }, + { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" }, + { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" }, + { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" }, ] [[package]] @@ -531,16 +900,16 @@ wheels = [ [[package]] name = "httpx-sse" -version = "0.4.0" +version = "0.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] [[package]] name = "huggingface-hub" -version = "0.32.4" +version = "0.36.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -552,118 +921,160 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/c8/4f7d270285c46324fd66f62159eb16739aa5696f422dba57678a8c6b78e9/huggingface_hub-0.32.4.tar.gz", hash = "sha256:f61d45cd338736f59fb0e97550b74c24ee771bcc92c05ae0766b9116abe720be", size = 424494, upload-time = "2025-06-03T09:59:46.105Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/54/096903f02ca14eb2670a4d11729da44a026c0bababec8c15f160441124c5/huggingface_hub-0.36.1.tar.gz", hash = "sha256:5a3b8bf87e182ad6f1692c196bb9ec9ade7755311d5d5e792dc45045f77283ad", size = 649681, upload-time = "2026-02-02T10:46:58.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/8b/222140f3cfb6f17b0dd8c4b9a0b36bd4ebefe9fb0098ba35d6960abcda0f/huggingface_hub-0.32.4-py3-none-any.whl", hash = "sha256:37abf8826b38d971f60d3625229221c36e53fe58060286db9baf619cfbf39767", size = 512101, upload-time = "2025-06-03T09:59:44.099Z" }, + { url = "https://files.pythonhosted.org/packages/94/cb/8f5141b3c21d1ecdf87852506eb583fec497c7e9803a168fe4aec64252bb/huggingface_hub-0.36.1-py3-none-any.whl", hash = "sha256:c6fa8a8f7b8559bc624ebb7e218fb72171b30f6049ebe08f8bfc2a44b38ece50", size = 566283, upload-time = "2026-02-02T10:46:56.459Z" }, ] [package.optional-dependencies] -hf-xet = [ - { name = "hf-xet" }, +inference = [ + { name = "aiohttp" }, ] [[package]] -name = "identify" -version = "2.6.12" +name = "idna" +version = "3.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] -name = "idna" -version = "3.10" +name = "importlib-metadata" +version = "8.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] [[package]] -name = "importlib-metadata" -version = "7.1.0" +name = "invoke" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/bd/b461d3424a24c80490313fd77feeb666ca4f6a28c7e72713e3d9095719b4/invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707", size = 304762, upload-time = "2025-10-11T00:36:35.172Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/4b/b99e37f88336009971405cbb7630610322ed6fbfa31e1d7ab3fbf3049a2d/invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8", size = 160287, upload-time = "2025-10-11T00:36:33.703Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120, upload-time = "2024-03-20T19:51:32.429Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409, upload-time = "2024-03-20T19:51:30.241Z" }, + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, ] [[package]] -name = "iniconfig" -version = "2.1.0" +name = "jaraco-context" +version = "6.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, ] [[package]] -name = "jinja2" -version = "3.1.6" +name = "jaraco-functools" +version = "4.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markupsafe" }, + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, ] [[package]] name = "jiter" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, + { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, + { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, + { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, + { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, + { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, + { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, + { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, + { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, + { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, + { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, + { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, + { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, + { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, + { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, + { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, + { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, + { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, + { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, ] [[package]] name = "jsonschema" -version = "4.24.0" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -671,202 +1082,204 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [[package]] -name = "jsonschema-specifications" -version = "2025.4.1" +name = "jsonschema-path" +version = "0.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, { name = "referencing" }, + { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, + { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" }, ] [[package]] -name = "kvf" -version = "0.0.3" +name = "jsonschema-specifications" +version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "braq" }, - { name = "paradict" }, + { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/f8/e1826c156d4f97cf4662a6110cbbcfd91b5e5570c8a88bf0a8270718621e/kvf-0.0.3.tar.gz", hash = "sha256:f4885b1bbe66c8c20fdabe5cedeb3c0e5d12a54ac495f9e5fcf6fed0e0c51b73", size = 4938, upload-time = "2024-12-10T20:49:13.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/db/4a8d3b1fef45cabcadf36f9a2231b2cde3dddd3a58ab1723119c7fbce34f/kvf-0.0.3-py3-none-any.whl", hash = "sha256:9d666e51cae512e3f95c55b77524e34d0095b278c81f96f7bbc7d37b5bd545c6", size = 4716, upload-time = "2024-12-10T20:49:11.815Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] -name = "licensecheck" -version = "2024.3" +name = "keyring" +version = "25.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "appdirs" }, - { name = "fhconfparser" }, - { name = "loguru" }, - { name = "markdown" }, - { name = "packaging" }, - { name = "requests" }, - { name = "requests-cache" }, - { name = "requirements-parser" }, - { name = "rich" }, - { name = "tomli" }, - { name = "uv" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/77/a73c2b8285525b80920c25e78aa93685a16738c79c9021868e478eeae90c/licensecheck-2024.3.tar.gz", hash = "sha256:e838e1c87a7ede553df376ad35a69d7c4b02676df0fba9dd1c6a6866eb0e0ee5", size = 21052, upload-time = "2024-08-26T20:53:02.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/aa/88b9cb56e47db1327b65773d72d78c850ce26e7adcf5e7963220c48f5d1b/licensecheck-2024.3-py3-none-any.whl", hash = "sha256:0baef4c1865e0325a35ff25ed12a0c7094035b7dcfbab9a1abfe43d7735adebe", size = 24984, upload-time = "2024-08-26T20:53:00.819Z" }, + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, ] [[package]] -name = "llamafirewall" -version = "1.0.3" +name = "librt" +version = "0.7.8" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "codeshield" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "torch" }, - { name = "transformers" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/f5/9dbd3b0a74c11323d967b0e1210a9fac0de068abc0c2e5dc08a6ee2094a5/llamafirewall-1.0.3.tar.gz", hash = "sha256:54fe55c8636fb0b7e78734fdbb96f0036de7ad6613e3dc23ab8531fcf73e6ec1", size = 20805, upload-time = "2025-05-29T16:49:14.281Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/d2/3c4fe84430f2bd77b5c40e77f197ed3b0bb7f0979abe06d66b831727026a/llamafirewall-1.0.3-py2.py3-none-any.whl", hash = "sha256:7bc110f4322c5eefb112fcde83b21da67176de4fa9036c184b14bf04f1ea4b30", size = 34421, upload-time = "2025-05-29T16:49:12.692Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, + { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, + { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, + { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, + { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, + { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, + { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, + { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, + { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, + { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, + { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, + { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, + { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, + { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, ] [[package]] -name = "llm" -version = "0.1.0" -source = { virtual = "sre_agent/llm" } +name = "logfire" +version = "4.21.0" +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anthropic" }, - { name = "fastapi" }, - { name = "google-genai" }, - { name = "mcp", extra = ["cli"] }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-dotenv" }, - { name = "shared" }, - { name = "uvicorn" }, + { name = "executing" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-sdk" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/6a/387d114faf39a13f8e81f09dedc1ed89fe81c2d9eb63ee625e1abc7c79d2/logfire-4.21.0.tar.gz", hash = "sha256:57051f10e7faae4ab4905893d13d3ebeca96ca822ecf35ab68a0b7da4e5d3550", size = 651979, upload-time = "2026-01-28T18:55:43.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/03/af72df300c4659ea26cb2e1f1f212e26b1b373e89b82a64912bd9e898be5/logfire-4.21.0-py3-none-any.whl", hash = "sha256:cfd0ce7048ed7b415bd569cb2f20fe487e9dfcad926666c66c3c3f124d6a6238", size = 241687, upload-time = "2026-01-28T18:55:40.753Z" }, ] -[package.metadata] -requires-dist = [ - { name = "anthropic", specifier = ">=0.49.0" }, - { name = "fastapi", specifier = ">=0.115.12" }, - { name = "google-genai", specifier = ">=1.19.0" }, - { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, - { name = "pydantic", specifier = ">=2.11.3" }, - { name = "pydantic-settings", specifier = ">=2.9.1" }, - { name = "python-dotenv", specifier = ">=1.1.0" }, - { name = "shared" }, - { name = "uvicorn", specifier = ">=0.34.2" }, +[package.optional-dependencies] +httpx = [ + { name = "opentelemetry-instrumentation-httpx" }, ] [[package]] -name = "loguru" -version = "0.7.3" +name = "logfire-api" +version = "4.21.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/d5/c183261d5560e33335443b377c921aa6a15e9890ceac63024237e8c1279b/logfire_api-4.21.0.tar.gz", hash = "sha256:5d709a0d3adfd573db70964cb48c03b750966de395ed9c8da4de111707a75fab", size = 59331, upload-time = "2026-01-28T18:55:44.985Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, + { url = "https://files.pythonhosted.org/packages/d7/00/5045f889be4a450b321db998d0a5581d30423138a04dffe18b52730cb926/logfire_api-4.21.0-py3-none-any.whl", hash = "sha256:32f9b48e6b73c270d1aeb6478dcbecc5f82120b8eae70559e0d1b05d1b86541e", size = 98061, upload-time = "2026-01-28T18:55:42.342Z" }, ] [[package]] -name = "markdown" -version = "3.8" +name = "lupa" +version = "2.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload-time = "2025-04-11T14:42:50.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload-time = "2025-04-11T14:42:49.178Z" }, + { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, + { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, + { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, + { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, + { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, + { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, + { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, + { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, + { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, + { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, + { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, + { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, + { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, + { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, + { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, + { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, + { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, + { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, + { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, ] [[package]] name = "markdown-it-py" -version = "3.0.0" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "mcp" -version = "1.9.3" +version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "httpx" }, { name = "httpx-sse" }, + { name = "jsonschema" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "sse-starlette" }, { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045, upload-time = "2025-06-05T15:48:25.681Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063, upload-time = "2025-06-05T15:48:24.171Z" }, -] - -[package.optional-dependencies] -cli = [ - { name = "python-dotenv" }, - { name = "typer" }, + { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, ] [[package]] @@ -879,38 +1292,138 @@ wheels = [ ] [[package]] -name = "mpmath" -version = "1.3.0" +name = "mistralai" +version = "1.9.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +dependencies = [ + { name = "eval-type-backport" }, + { name = "httpx" }, + { name = "invoke" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "pyyaml" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/8d/d8b7af67a966b6f227024e1cb7287fc19901a434f87a5a391dcfe635d338/mistralai-1.9.11.tar.gz", hash = "sha256:3df9e403c31a756ec79e78df25ee73cea3eb15f86693773e16b16adaf59c9b8a", size = 208051, upload-time = "2025-10-02T15:53:40.473Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/76/4ce12563aea5a76016f8643eff30ab731e6656c845e9e4d090ef10c7b925/mistralai-1.9.11-py3-none-any.whl", hash = "sha256:7a3dc2b8ef3fceaa3582220234261b5c4e3e03a972563b07afa150e44a25a6d3", size = 442796, upload-time = "2025-10-02T15:53:39.134Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] [[package]] name = "mypy" -version = "1.16.0" +version = "1.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/38/13c2f1abae94d5ea0354e146b95a1be9b2137a0d506728e0da037c4276f6/mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab", size = 3323139, upload-time = "2025-05-29T13:46:12.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/cf/158e5055e60ca2be23aec54a3010f89dcffd788732634b344fc9cb1e85a0/mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13", size = 11062927, upload-time = "2025-05-29T13:35:52.328Z" }, - { url = "https://files.pythonhosted.org/packages/94/34/cfff7a56be1609f5d10ef386342ce3494158e4d506516890142007e6472c/mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090", size = 10083082, upload-time = "2025-05-29T13:35:33.378Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7f/7242062ec6288c33d8ad89574df87c3903d394870e5e6ba1699317a65075/mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1", size = 11828306, upload-time = "2025-05-29T13:21:02.164Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5f/b392f7b4f659f5b619ce5994c5c43caab3d80df2296ae54fa888b3d17f5a/mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8", size = 12702764, upload-time = "2025-05-29T13:20:42.826Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c0/7646ef3a00fa39ac9bc0938626d9ff29d19d733011be929cfea59d82d136/mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730", size = 12896233, upload-time = "2025-05-29T13:18:37.446Z" }, - { url = "https://files.pythonhosted.org/packages/6d/38/52f4b808b3fef7f0ef840ee8ff6ce5b5d77381e65425758d515cdd4f5bb5/mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec", size = 9565547, upload-time = "2025-05-29T13:20:02.836Z" }, - { url = "https://files.pythonhosted.org/packages/97/9c/ca03bdbefbaa03b264b9318a98950a9c683e06472226b55472f96ebbc53d/mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b", size = 11059753, upload-time = "2025-05-29T13:18:18.167Z" }, - { url = "https://files.pythonhosted.org/packages/36/92/79a969b8302cfe316027c88f7dc6fee70129490a370b3f6eb11d777749d0/mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0", size = 10073338, upload-time = "2025-05-29T13:19:48.079Z" }, - { url = "https://files.pythonhosted.org/packages/14/9b/a943f09319167da0552d5cd722104096a9c99270719b1afeea60d11610aa/mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b", size = 11827764, upload-time = "2025-05-29T13:46:04.47Z" }, - { url = "https://files.pythonhosted.org/packages/ec/64/ff75e71c65a0cb6ee737287c7913ea155845a556c64144c65b811afdb9c7/mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d", size = 12701356, upload-time = "2025-05-29T13:35:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ad/0e93c18987a1182c350f7a5fab70550852f9fabe30ecb63bfbe51b602074/mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52", size = 12900745, upload-time = "2025-05-29T13:17:24.409Z" }, - { url = "https://files.pythonhosted.org/packages/28/5d/036c278d7a013e97e33f08c047fe5583ab4f1fc47c9a49f985f1cdd2a2d7/mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb", size = 9572200, upload-time = "2025-05-29T13:33:44.92Z" }, - { url = "https://files.pythonhosted.org/packages/99/a3/6ed10530dec8e0fdc890d81361260c9ef1f5e5c217ad8c9b21ecb2b8366b/mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031", size = 2265773, upload-time = "2025-05-29T13:35:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, ] [[package]] @@ -923,200 +1436,20 @@ wheels = [ ] [[package]] -name = "networkx" -version = "3.5" +name = "nexus-rpc" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +dependencies = [ + { name = "typing-extensions" }, ] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/50/95d7bc91f900da5e22662c82d9bf0f72a4b01f2a552708bf2f43807707a1/nexus_rpc-1.2.0.tar.gz", hash = "sha256:b4ddaffa4d3996aaeadf49b80dfcdfbca48fe4cb616defaf3b3c5c2c8fc61890", size = 74142, upload-time = "2025-11-17T19:17:06.798Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/13/04/eaac430d0e6bf21265ae989427d37e94be5e41dc216879f1fbb6c5339942/nexus_rpc-1.2.0-py3-none-any.whl", hash = "sha256:977876f3af811ad1a09b2961d3d1ac9233bda43ff0febbb0c9906483b9d9f8a3", size = 28166, upload-time = "2025-11-17T19:17:05.64Z" }, ] [[package]] -name = "numpy" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/db/8e12381333aea300890829a0a36bfa738cac95475d88982d538725143fd9/numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6", size = 20382813, upload-time = "2025-06-07T14:54:32.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/59/9df493df81ac6f76e9f05cdbe013cdb0c9a37b434f6e594f5bd25e278908/numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba", size = 20897025, upload-time = "2025-06-07T14:40:33.558Z" }, - { url = "https://files.pythonhosted.org/packages/2f/86/4ff04335901d6cf3a6bb9c748b0097546ae5af35e455ae9b962ebff4ecd7/numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e", size = 14129882, upload-time = "2025-06-07T14:40:55.034Z" }, - { url = "https://files.pythonhosted.org/packages/71/8d/a942cd4f959de7f08a79ab0c7e6cecb7431d5403dce78959a726f0f57aa1/numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2", size = 5110181, upload-time = "2025-06-07T14:41:04.4Z" }, - { url = "https://files.pythonhosted.org/packages/86/5d/45850982efc7b2c839c5626fb67fbbc520d5b0d7c1ba1ae3651f2f74c296/numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459", size = 6647581, upload-time = "2025-06-07T14:41:14.695Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c871d4a83f93b00373d3eebe4b01525eee8ef10b623a335ec262b58f4dc1/numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a", size = 14262317, upload-time = "2025-06-07T14:41:35.862Z" }, - { url = "https://files.pythonhosted.org/packages/b7/f6/bc47f5fa666d5ff4145254f9e618d56e6a4ef9b874654ca74c19113bb538/numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a", size = 16633919, upload-time = "2025-06-07T14:42:00.622Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b4/65f48009ca0c9b76df5f404fccdea5a985a1bb2e34e97f21a17d9ad1a4ba/numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67", size = 15567651, upload-time = "2025-06-07T14:42:24.429Z" }, - { url = "https://files.pythonhosted.org/packages/f1/62/5367855a2018578e9334ed08252ef67cc302e53edc869666f71641cad40b/numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc", size = 18361723, upload-time = "2025-06-07T14:42:51.167Z" }, - { url = "https://files.pythonhosted.org/packages/d4/75/5baed8cd867eabee8aad1e74d7197d73971d6a3d40c821f1848b8fab8b84/numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570", size = 6318285, upload-time = "2025-06-07T14:43:02.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/49/d5781eaa1a15acb3b3a3f49dc9e2ff18d92d0ce5c2976f4ab5c0a7360250/numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd", size = 12732594, upload-time = "2025-06-07T14:43:21.071Z" }, - { url = "https://files.pythonhosted.org/packages/c2/1c/6d343e030815c7c97a1f9fbad00211b47717c7fe446834c224bd5311e6f1/numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea", size = 9891498, upload-time = "2025-06-07T14:43:36.332Z" }, - { url = "https://files.pythonhosted.org/packages/73/fc/1d67f751fd4dbafc5780244fe699bc4084268bad44b7c5deb0492473127b/numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a", size = 20889633, upload-time = "2025-06-07T14:44:06.839Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/73ffdb69e5c3f19ec4530f8924c4386e7ba097efc94b9c0aff607178ad94/numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959", size = 14151683, upload-time = "2025-06-07T14:44:28.847Z" }, - { url = "https://files.pythonhosted.org/packages/64/d5/06d4bb31bb65a1d9c419eb5676173a2f90fd8da3c59f816cc54c640ce265/numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe", size = 5102683, upload-time = "2025-06-07T14:44:38.417Z" }, - { url = "https://files.pythonhosted.org/packages/12/8b/6c2cef44f8ccdc231f6b56013dff1d71138c48124334aded36b1a1b30c5a/numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb", size = 6640253, upload-time = "2025-06-07T14:44:49.359Z" }, - { url = "https://files.pythonhosted.org/packages/62/aa/fca4bf8de3396ddb59544df9b75ffe5b73096174de97a9492d426f5cd4aa/numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0", size = 14258658, upload-time = "2025-06-07T14:45:10.156Z" }, - { url = "https://files.pythonhosted.org/packages/1c/12/734dce1087eed1875f2297f687e671cfe53a091b6f2f55f0c7241aad041b/numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f", size = 16628765, upload-time = "2025-06-07T14:45:35.076Z" }, - { url = "https://files.pythonhosted.org/packages/48/03/ffa41ade0e825cbcd5606a5669962419528212a16082763fc051a7247d76/numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8", size = 15564335, upload-time = "2025-06-07T14:45:58.797Z" }, - { url = "https://files.pythonhosted.org/packages/07/58/869398a11863310aee0ff85a3e13b4c12f20d032b90c4b3ee93c3b728393/numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270", size = 18360608, upload-time = "2025-06-07T14:46:25.687Z" }, - { url = "https://files.pythonhosted.org/packages/2f/8a/5756935752ad278c17e8a061eb2127c9a3edf4ba2c31779548b336f23c8d/numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f", size = 6310005, upload-time = "2025-06-07T14:50:13.138Z" }, - { url = "https://files.pythonhosted.org/packages/08/60/61d60cf0dfc0bf15381eaef46366ebc0c1a787856d1db0c80b006092af84/numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5", size = 12729093, upload-time = "2025-06-07T14:50:31.82Z" }, - { url = "https://files.pythonhosted.org/packages/66/31/2f2f2d2b3e3c32d5753d01437240feaa32220b73258c9eef2e42a0832866/numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e", size = 9885689, upload-time = "2025-06-07T14:50:47.888Z" }, - { url = "https://files.pythonhosted.org/packages/f1/89/c7828f23cc50f607ceb912774bb4cff225ccae7131c431398ad8400e2c98/numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8", size = 20986612, upload-time = "2025-06-07T14:46:56.077Z" }, - { url = "https://files.pythonhosted.org/packages/dd/46/79ecf47da34c4c50eedec7511e53d57ffdfd31c742c00be7dc1d5ffdb917/numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3", size = 14298953, upload-time = "2025-06-07T14:47:18.053Z" }, - { url = "https://files.pythonhosted.org/packages/59/44/f6caf50713d6ff4480640bccb2a534ce1d8e6e0960c8f864947439f0ee95/numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f", size = 5225806, upload-time = "2025-06-07T14:47:27.524Z" }, - { url = "https://files.pythonhosted.org/packages/a6/43/e1fd1aca7c97e234dd05e66de4ab7a5be54548257efcdd1bc33637e72102/numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808", size = 6735169, upload-time = "2025-06-07T14:47:38.057Z" }, - { url = "https://files.pythonhosted.org/packages/84/89/f76f93b06a03177c0faa7ca94d0856c4e5c4bcaf3c5f77640c9ed0303e1c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8", size = 14330701, upload-time = "2025-06-07T14:47:59.113Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f5/4858c3e9ff7a7d64561b20580cf7cc5d085794bd465a19604945d6501f6c/numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad", size = 16692983, upload-time = "2025-06-07T14:48:24.196Z" }, - { url = "https://files.pythonhosted.org/packages/08/17/0e3b4182e691a10e9483bcc62b4bb8693dbf9ea5dc9ba0b77a60435074bb/numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b", size = 15641435, upload-time = "2025-06-07T14:48:47.712Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d5/463279fda028d3c1efa74e7e8d507605ae87f33dbd0543cf4c4527c8b882/numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555", size = 18433798, upload-time = "2025-06-07T14:49:14.866Z" }, - { url = "https://files.pythonhosted.org/packages/0e/1e/7a9d98c886d4c39a2b4d3a7c026bffcf8fbcaf518782132d12a301cfc47a/numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61", size = 6438632, upload-time = "2025-06-07T14:49:25.67Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ab/66fc909931d5eb230107d016861824f335ae2c0533f422e654e5ff556784/numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb", size = 12868491, upload-time = "2025-06-07T14:49:44.898Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e8/2c8a1c9e34d6f6d600c83d5ce5b71646c32a13f34ca5c518cc060639841c/numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944", size = 9935345, upload-time = "2025-06-07T14:50:02.311Z" }, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.6.4.1" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/eb/ff4b8c503fa1f1796679dce648854d58751982426e4e4b37d6fce49d259c/nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb", size = 393138322, upload-time = "2024-11-20T17:40:25.65Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.6.80" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/60/7b6497946d74bcf1de852a21824d63baad12cd417db4195fc1bfe59db953/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132", size = 8917980, upload-time = "2024-11-20T17:36:04.019Z" }, - { url = "https://files.pythonhosted.org/packages/a5/24/120ee57b218d9952c379d1e026c4479c9ece9997a4fb46303611ee48f038/nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73", size = 8917972, upload-time = "2024-10-01T16:58:06.036Z" }, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/2e/46030320b5a80661e88039f59060d1790298b4718944a65a7f2aeda3d9e9/nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53", size = 23650380, upload-time = "2024-10-01T17:00:14.643Z" }, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/23/e717c5ac26d26cf39a27fbc076240fad2e3b817e5889d671b67f4f9f49c5/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7", size = 897690, upload-time = "2024-11-20T17:35:30.697Z" }, - { url = "https://files.pythonhosted.org/packages/f0/62/65c05e161eeddbafeca24dc461f47de550d9fa8a7e04eb213e32b55cfd99/nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8", size = 897678, upload-time = "2024-10-01T16:57:33.821Z" }, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "9.5.1.17" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/78/4535c9c7f859a64781e43c969a3a7e84c54634e319a996d43ef32ce46f83/nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2", size = 570988386, upload-time = "2024-10-25T19:54:26.39Z" }, -] - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.3.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/16/73727675941ab8e6ffd86ca3a4b7b47065edcca7a997920b831f8147c99d/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5", size = 200221632, upload-time = "2024-11-20T17:41:32.357Z" }, - { url = "https://files.pythonhosted.org/packages/60/de/99ec247a07ea40c969d904fc14f3a356b3e2a704121675b75c366b694ee1/nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca", size = 200221622, upload-time = "2024-10-01T17:03:58.79Z" }, -] - -[[package]] -name = "nvidia-cufile-cu12" -version = "1.11.1.6" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/66/cc9876340ac68ae71b15c743ddb13f8b30d5244af344ec8322b449e35426/nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159", size = 1142103, upload-time = "2024-11-20T17:42:11.83Z" }, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.7.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/1b/44a01c4e70933637c93e6e1a8063d1e998b50213a6b65ac5a9169c47e98e/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf", size = 56279010, upload-time = "2024-11-20T17:42:50.958Z" }, - { url = "https://files.pythonhosted.org/packages/4a/aa/2c7ff0b5ee02eaef890c0ce7d4f74bc30901871c5e45dee1ae6d0083cd80/nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117", size = 56279000, upload-time = "2024-10-01T17:04:45.274Z" }, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.7.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/6e/c2cf12c9ff8b872e92b4a5740701e51ff17689c4d726fca91875b07f655d/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c", size = 158229790, upload-time = "2024-11-20T17:43:43.211Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/baba53585da791d043c10084cf9553e074548408e04ae884cfe9193bd484/nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6", size = 158229780, upload-time = "2024-10-01T17:05:39.875Z" }, -] - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.5.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/1e/b8b7c2f4099a37b96af5c9bb158632ea9e5d9d27d7391d7eb8fc45236674/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73", size = 216561367, upload-time = "2024-11-20T17:44:54.824Z" }, - { url = "https://files.pythonhosted.org/packages/43/ac/64c4316ba163e8217a99680c7605f779accffc6a4bcd0c778c12948d3707/nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f", size = 216561357, upload-time = "2024-10-01T17:06:29.861Z" }, -] - -[[package]] -name = "nvidia-cusparselt-cu12" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/9a/72ef35b399b0e183bc2e8f6f558036922d453c4d8237dab26c666a04244b/nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46", size = 156785796, upload-time = "2024-10-15T21:29:17.709Z" }, -] - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.26.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/ca/f42388aed0fddd64ade7493dbba36e1f534d4e6fdbdd355c6a90030ae028/nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6", size = 201319755, upload-time = "2025-03-13T00:29:55.296Z" }, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.6.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971, upload-time = "2024-11-20T17:46:53.366Z" }, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/9a/fff8376f8e3d084cd1530e1ef7b879bb7d6d265620c95c1b322725c694f4/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2", size = 89276, upload-time = "2024-11-20T17:38:27.621Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4e/0d0c945463719429b7bd21dece907ad0bde437a2ff12b9b12fee94722ab0/nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1", size = 89265, upload-time = "2024-10-01T17:00:38.172Z" }, -] - -[[package]] -name = "openai" -version = "1.85.0" +name = "openai" +version = "2.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1128,245 +1461,357 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/3c/1143dc0a865d06482454fddb35d739c9260b18d721f01287f79cc53a315f/openai-1.85.0.tar.gz", hash = "sha256:6ba76e4ebc5725f71f2f6126c7cb5169ca8de60dd5aa61f350f9448ad162c913", size = 468207, upload-time = "2025-06-09T16:51:17.361Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/73/b4427c7873f4f778ec7a6d2b1724fd3aadc85719a12e324615b9c2bc614f/openai-1.85.0-py3-none-any.whl", hash = "sha256:7dc3e839cb8bb8747979a90c63ad4cb25a8e0cbec17b53eec009532c9965cecf", size = 730229, upload-time = "2025-06-09T16:51:15.678Z" }, + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, ] [[package]] name = "opentelemetry-api" -version = "1.25.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "importlib-metadata" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/0d/10357006dc10fc65f7c7b46c18232e466e355f9e606ac461cfc7193b4cbe/opentelemetry_api-1.25.0.tar.gz", hash = "sha256:77c4985f62f2614e42ce77ee4c9da5fa5f0bc1e1821085e9a47533a9323ae869", size = 60383, upload-time = "2024-05-31T01:40:38.766Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b2/4bc5e52c9a23df0ac17dbb23923e609a8269cd67000a712b4f5bcfae1490/opentelemetry_api-1.25.0-py3-none-any.whl", hash = "sha256:757fa1aa020a0f8fa139f8959e53dec2051cc26b832e76fa839a6d76ecefd737", size = 59910, upload-time = "2024-05-31T01:40:00.911Z" }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.25.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/a7/85ffaaacd712e4634fa1c56cbf79a02cf90b8a178fe1eee2cabfb0b7f44d/opentelemetry_exporter_otlp_proto_common-1.25.0.tar.gz", hash = "sha256:c93f4e30da4eee02bacd1e004eb82ce4da143a2f8e15b987a9f603e0a85407d3", size = 17152, upload-time = "2024-05-31T01:40:42.259Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/02/74ac6619eec78c82a923324f916d3eccd2f2254cf4270b669e96b76bf717/opentelemetry_exporter_otlp_proto_common-1.25.0-py3-none-any.whl", hash = "sha256:15637b7d580c2675f70246563363775b4e6de947871e01d0f4e3881d1848d693", size = 17762, upload-time = "2024-05-31T01:40:13.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-http" -version = "1.25.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated" }, { name = "googleapis-common-protos" }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp-proto-common" }, { name = "opentelemetry-proto" }, { name = "opentelemetry-sdk" }, { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/04/2a08fa9c0214ae38880df01e8bfae12b067ec0793446578575e5080d6545/opentelemetry_exporter_otlp_proto_http-1.39.1.tar.gz", hash = "sha256:31bdab9745c709ce90a49a0624c2bd445d31a28ba34275951a6a362d16a0b9cb", size = 17288, upload-time = "2025-12-11T13:32:42.029Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/f1/b27d3e2e003cd9a3592c43d099d2ed8d0a947c15281bf8463a256db0b46c/opentelemetry_exporter_otlp_proto_http-1.39.1-py3-none-any.whl", hash = "sha256:d9f5207183dd752a412c4cd564ca8875ececba13be6e9c6c370ffb752fd59985", size = 19641, upload-time = "2025-12-11T13:32:22.248Z" }, +] + +[[package]] +name = "opentelemetry-exporter-prometheus" +version = "0.60b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "prometheus-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/d9/1c3c518853c27d323a46813d3e99d601959ca2c6963d5217fe2110f0d579/opentelemetry_exporter_otlp_proto_http-1.25.0.tar.gz", hash = "sha256:9f8723859e37c75183ea7afa73a3542f01d0fd274a5b97487ea24cb683d7d684", size = 14048, upload-time = "2024-05-31T01:40:43.749Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/39/7dafa6fff210737267bed35a8855b6ac7399b9e582b8cf1f25f842517012/opentelemetry_exporter_prometheus-0.60b1.tar.gz", hash = "sha256:a4011b46906323f71724649d301b4dc188aaa068852e814f4df38cc76eac616b", size = 14976, upload-time = "2025-12-11T13:32:42.944Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/b9/a47734f7c5a45619d8c64c227f119092b4679b2c49d37116fda7c0fc4573/opentelemetry_exporter_otlp_proto_http-1.25.0-py3-none-any.whl", hash = "sha256:2eca686ee11b27acd28198b3ea5e5863a53d1266b91cda47c839d95d5e0541a6", size = 16790, upload-time = "2024-05-31T01:40:16.649Z" }, + { url = "https://files.pythonhosted.org/packages/9b/0d/4be6bf5477a3eb3d917d2f17d3c0b6720cd6cb97898444a61d43cc983f5c/opentelemetry_exporter_prometheus-0.60b1-py3-none-any.whl", hash = "sha256:49f59178de4f4590e3cef0b8b95cf6e071aae70e1f060566df5546fad773b8fd", size = 13019, upload-time = "2025-12-11T13:32:23.974Z" }, ] [[package]] name = "opentelemetry-instrumentation" -version = "0.46b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, - { name = "setuptools" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "packaging" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/20/0a5d980843e048e9516443a91c63a559b40e5d50a730e73e72a5bde727fd/opentelemetry_instrumentation-0.46b0.tar.gz", hash = "sha256:974e0888fb2a1e01c38fbacc9483d024bb1132aad92d6d24e2e5543887a7adda", size = 24048, upload-time = "2024-05-31T16:17:29.807Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/0f/7e6b713ac117c1f5e4e3300748af699b9902a2e5e34c9cf443dde25a01fa/opentelemetry_instrumentation-0.60b1.tar.gz", hash = "sha256:57ddc7974c6eb35865af0426d1a17132b88b2ed8586897fee187fd5b8944bd6a", size = 31706, upload-time = "2025-12-11T13:36:42.515Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/e5/d6fff0a6f6fbddf03c7fb48ab47925581c4f1a8268f9ad98e5ea4a8b90a5/opentelemetry_instrumentation-0.46b0-py3-none-any.whl", hash = "sha256:89cd721b9c18c014ca848ccd11181e6b3fd3f6c7669e35d59c48dc527408c18b", size = 29108, upload-time = "2024-05-31T16:16:14.042Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/6788e83c5c86a2690101681aeef27eeb2a6bf22df52d3f263a22cee20915/opentelemetry_instrumentation-0.60b1-py3-none-any.whl", hash = "sha256:04480db952b48fb1ed0073f822f0ee26012b7be7c3eac1a3793122737c78632d", size = 33096, upload-time = "2025-12-11T13:35:33.067Z" }, ] [[package]] -name = "opentelemetry-instrumentation-requests" -version = "0.46b0" +name = "opentelemetry-instrumentation-httpx" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-semantic-conventions" }, { name = "opentelemetry-util-http" }, + { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/28/5b5e9fb74639e47f026a3fd6550bba965ca18b316a8178907540e711855c/opentelemetry_instrumentation_requests-0.46b0.tar.gz", hash = "sha256:ef0ad63bfd0d52631daaf7d687e763dbd89b465f5cb052f12a4e67e5e3d181e4", size = 13709, upload-time = "2024-05-31T16:18:08.594Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/08/11208bcfcab4fc2023252c3f322aa397fd9ad948355fea60f5fc98648603/opentelemetry_instrumentation_httpx-0.60b1.tar.gz", hash = "sha256:a506ebaf28c60112cbe70ad4f0338f8603f148938cb7b6794ce1051cd2b270ae", size = 20611, upload-time = "2025-12-11T13:37:01.661Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/42/5eee8720eccd4b94dd3d4908364785db8b22c9ae512ee47caff29064ca4c/opentelemetry_instrumentation_requests-0.46b0-py3-none-any.whl", hash = "sha256:a8c2472800d8686f3f286cd524b8746b386154092e85a791ba14110d1acc9b81", size = 12170, upload-time = "2024-05-31T16:17:07.341Z" }, + { url = "https://files.pythonhosted.org/packages/43/59/b98e84eebf745ffc75397eaad4763795bff8a30cbf2373a50ed4e70646c5/opentelemetry_instrumentation_httpx-0.60b1-py3-none-any.whl", hash = "sha256:f37636dd742ad2af83d896ba69601ed28da51fa4e25d1ab62fde89ce413e275b", size = 15701, upload-time = "2025-12-11T13:36:04.56Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.25.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/3c/28c9ce40eb8ab287471af81659089ca98ef4f7ce289669e23b19c29f24a8/opentelemetry_proto-1.25.0.tar.gz", hash = "sha256:35b6ef9dc4a9f7853ecc5006738ad40443701e52c26099e197895cbda8b815a3", size = 35062, upload-time = "2024-05-31T01:40:52.737Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/ae/d6b5f11ecbffafe8b6d54130fed0cc78aad3711e00074d63a7359d6dcf3b/opentelemetry_proto-1.25.0-py3-none-any.whl", hash = "sha256:f07e3341c78d835d9b86665903b199893befa5e98866f63d22b00d0b7ca4972f", size = 52450, upload-time = "2024-05-31T01:40:30.987Z" }, + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.25.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/3c/77076b77f1d73141adc119f62370ec9456ef314ba0b4e7072e3775c36ef7/opentelemetry_sdk-1.25.0.tar.gz", hash = "sha256:ce7fc319c57707ef5bf8b74fb9f8ebdb8bfafbe11898410e0d2a761d08a98ec7", size = 141042, upload-time = "2024-05-31T01:40:53.73Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/b2/729a959a8aa032bce246c791f977161099ab60fb0188408ccec1bf283b00/opentelemetry_sdk-1.25.0-py3-none-any.whl", hash = "sha256:d97ff7ec4b351692e9d5a15af570c693b8715ad78b8aafbec5c7100fe966b4c9", size = 107028, upload-time = "2024-05-31T01:40:33.281Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.46b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/ea/a4a5277247b3d2ed2e23a58b0d509c2eafa4ebb56038ba5b23c0f9ea6242/opentelemetry_semantic_conventions-0.46b0.tar.gz", hash = "sha256:fbc982ecbb6a6e90869b15c1673be90bd18c8a56ff1cffc0864e38e2edffaefa", size = 80198, upload-time = "2024-05-31T01:40:54.722Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/41/28dae1ec1fe0151373f06bd06d9170ca14b52d5b3a6c2dc55f85bc219619/opentelemetry_semantic_conventions-0.46b0-py3-none-any.whl", hash = "sha256:6daef4ef9fa51d51855d9f8e0ccd3a1bd59e0e545abe99ac6203804e36ab3e07", size = 130549, upload-time = "2024-05-31T01:40:35.348Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, ] [[package]] name = "opentelemetry-util-http" -version = "0.46b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/91/45bf243850463b2c83000ca129442255eaef7c446bd0f59a2ab54b15abff/opentelemetry_util_http-0.46b0.tar.gz", hash = "sha256:03b6e222642f9c7eae58d9132343e045b50aca9761fcb53709bd2b663571fdf6", size = 7387, upload-time = "2024-05-31T16:18:21.321Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/fc/c47bb04a1d8a941a4061307e1eddfa331ed4d0ab13d8a9781e6db256940a/opentelemetry_util_http-0.60b1.tar.gz", hash = "sha256:0d97152ca8c8a41ced7172d29d3622a219317f74ae6bb3027cfbdcf22c3cc0d6", size = 11053, upload-time = "2025-12-11T13:37:25.115Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/7f/26d3d8880ea79adde8bb7bc306b25ca5134d6f6c3006ba464716405b4729/opentelemetry_util_http-0.46b0-py3-none-any.whl", hash = "sha256:8dc1949ce63caef08db84ae977fdc1848fe6dc38e6bbaad0ae3e6ecd0d451629", size = 6920, upload-time = "2024-05-31T16:17:25.344Z" }, + { url = "https://files.pythonhosted.org/packages/16/5c/d3f1733665f7cd582ef0842fb1d2ed0bc1fba10875160593342d22bba375/opentelemetry_util_http-0.60b1-py3-none-any.whl", hash = "sha256:66381ba28550c91bee14dcba8979ace443444af1ed609226634596b4b0faf199", size = 8947, upload-time = "2025-12-11T13:36:37.151Z" }, ] [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] -name = "paradict" -version = "0.0.16" +name = "pathable" +version = "0.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/83/8cf8d94be55ab9ea783e1f8ece06059cd986bb482ad69f7be549839b9e07/paradict-0.0.16.tar.gz", hash = "sha256:d909d122bf47028a45334eb2280d1e1bcb401fda89986af42c39fd2fadf9de4d", size = 61471, upload-time = "2024-12-10T21:23:49.007Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/f9/a9807d307ba1837bb8799e1337f41edcdbb92ef6090668dc50f483a168bf/paradict-0.0.16-py3-none-any.whl", hash = "sha256:28df79f0dc0e68c8f8a3e9b7c75e67a85305ef7298653fc7a369a1bf4f58cb20", size = 61735, upload-time = "2024-12-10T21:23:45.408Z" }, + { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, ] [[package]] name = "pathspec" -version = "0.12.1" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] -name = "peewee" -version = "3.18.1" +name = "pathvalidate" +version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/ce/c2bb58d00cb12d19dea28d5a98d05a14350197a3d03eba60be9bae708bac/peewee-3.18.1.tar.gz", hash = "sha256:a76a694b3b3012ce22f00d51fd83e55bf80b595275a90ed62cd36eb45496cf1d", size = 3026130, upload-time = "2025-04-30T15:40:35.06Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, +] [[package]] name = "platformdirs" -version = "4.3.8" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] [[package]] -name = "pluggy" -version = "1.6.0" +name = "prometheus-client" +version = "0.24.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, ] [[package]] -name = "pre-commit" -version = "4.2.0" +name = "prompt-toolkit" +version = "3.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, + { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, ] [[package]] -name = "prompt-server" -version = "0.1.0" -source = { virtual = "sre_agent/servers/prompt_server" } -dependencies = [ - { name = "fastapi" }, - { name = "mcp", extra = ["cli"] }, +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] -[package.metadata] -requires-dist = [ - { name = "fastapi", specifier = ">=0.115.12" }, - { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, +[[package]] +name = "protobuf" +version = "6.33.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, + { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, ] [[package]] -name = "prompt-toolkit" -version = "3.0.52" +name = "py-key-value-aio" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "wcwidth" }, + { name = "beartype" }, + { name = "py-key-value-shared" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, +] + +[package.optional-dependencies] +disk = [ + { name = "diskcache" }, + { name = "pathvalidate" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] +redis = [ + { name = "redis" }, ] [[package]] -name = "protobuf" -version = "4.25.8" +name = "py-key-value-shared" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/01/34c8d2b6354906d728703cb9d546a0e534de479e25f1b581e4094c4a85cc/protobuf-4.25.8.tar.gz", hash = "sha256:6135cf8affe1fc6f76cced2641e4ea8d3e59518d1f24ae41ba97bcad82d397cd", size = 380920, upload-time = "2025-05-28T14:22:25.153Z" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/ff/05f34305fe6b85bbfbecbc559d423a5985605cad5eda4f47eae9e9c9c5c5/protobuf-4.25.8-cp310-abi3-win32.whl", hash = "sha256:504435d831565f7cfac9f0714440028907f1975e4bed228e58e72ecfff58a1e0", size = 392745, upload-time = "2025-05-28T14:22:10.524Z" }, - { url = "https://files.pythonhosted.org/packages/08/35/8b8a8405c564caf4ba835b1fdf554da869954712b26d8f2a98c0e434469b/protobuf-4.25.8-cp310-abi3-win_amd64.whl", hash = "sha256:bd551eb1fe1d7e92c1af1d75bdfa572eff1ab0e5bf1736716814cdccdb2360f9", size = 413736, upload-time = "2025-05-28T14:22:13.156Z" }, - { url = "https://files.pythonhosted.org/packages/28/d7/ab27049a035b258dab43445eb6ec84a26277b16105b277cbe0a7698bdc6c/protobuf-4.25.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ca809b42f4444f144f2115c4c1a747b9a404d590f18f37e9402422033e464e0f", size = 394537, upload-time = "2025-05-28T14:22:14.768Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6d/a4a198b61808dd3d1ee187082ccc21499bc949d639feb948961b48be9a7e/protobuf-4.25.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:9ad7ef62d92baf5a8654fbb88dac7fa5594cfa70fd3440488a5ca3bfc6d795a7", size = 294005, upload-time = "2025-05-28T14:22:16.052Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c6/c9deaa6e789b6fc41b88ccbdfe7a42d2b82663248b715f55aa77fbc00724/protobuf-4.25.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:83e6e54e93d2b696a92cad6e6efc924f3850f82b52e1563778dfab8b355101b0", size = 294924, upload-time = "2025-05-28T14:22:17.105Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c1/6aece0ab5209981a70cd186f164c133fdba2f51e124ff92b73de7fd24d78/protobuf-4.25.8-py3-none-any.whl", hash = "sha256:15a0af558aa3b13efef102ae6e4f3efac06f1eea11afb3a57db2901447d9fb59", size = 156757, upload-time = "2025-05-28T14:22:24.135Z" }, + { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, ] [[package]] name = "pyasn1" -version = "0.6.1" +version = "0.6.2" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } 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" }, + { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, ] [[package]] @@ -1381,9 +1826,18 @@ 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 = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + [[package]] name = "pydantic" -version = "2.11.5" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1391,147 +1845,360 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-ai" +version = "1.51.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic-ai-slim", extra = ["ag-ui", "anthropic", "bedrock", "cli", "cohere", "evals", "fastmcp", "google", "groq", "huggingface", "logfire", "mcp", "mistral", "openai", "retries", "temporal", "ui", "vertexai", "xai"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/35/eb8e70dbf82658938b47616b3f92de775b6c10e46a9cd6f9af470755f652/pydantic_ai-1.51.0.tar.gz", hash = "sha256:cb3312af009b71fe3f8174512bc4ac1ee977a0a101bf0aaeaa2ea3b8f31603da", size = 11794, upload-time = "2026-01-31T02:06:24.431Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b5/960a0eb7f3a5cc15643e7353e97f27b225edc308bf6aa0d9510a411a6d8c/pydantic_ai-1.51.0-py3-none-any.whl", hash = "sha256:217a683b5c7a95d219980e56c0b81f6a9160fda542d7292c38708947a8e992e9", size = 7219, upload-time = "2026-01-31T02:06:16.497Z" }, +] + +[[package]] +name = "pydantic-ai-slim" +version = "1.51.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "genai-prices" }, + { name = "griffe" }, + { name = "httpx" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "pydantic-graph" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/93/82246bf2b4c1550dfb03f0ec6fcd6d38d5841475044a2561061fb3e92a49/pydantic_ai_slim-1.51.0.tar.gz", hash = "sha256:55c6059917559580bcfc39232dbe28ee00b4963a2eb1d9554718edabde4e082a", size = 404501, upload-time = "2026-01-31T02:06:26.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/05/0f2a718b117d8c4f89871848d8bde5f9dd7b1e0903f3cba9f9d425726307/pydantic_ai_slim-1.51.0-py3-none-any.whl", hash = "sha256:09aa368a034f7adbd6fbf23ae8415cbce0de13999ca1b0ba1ae5a42157293318", size = 528636, upload-time = "2026-01-31T02:06:19.583Z" }, +] + +[package.optional-dependencies] +ag-ui = [ + { name = "ag-ui-protocol" }, + { name = "starlette" }, +] +anthropic = [ + { name = "anthropic" }, +] +bedrock = [ + { name = "boto3" }, +] +cli = [ + { name = "argcomplete" }, + { name = "prompt-toolkit" }, + { name = "pyperclip" }, + { name = "rich" }, +] +cohere = [ + { name = "cohere", marker = "sys_platform != 'emscripten'" }, +] +evals = [ + { name = "pydantic-evals" }, +] +fastmcp = [ + { name = "fastmcp" }, +] +google = [ + { name = "google-genai" }, +] +groq = [ + { name = "groq" }, +] +huggingface = [ + { name = "huggingface-hub", extra = ["inference"] }, +] +logfire = [ + { name = "logfire", extra = ["httpx"] }, +] +mcp = [ + { name = "mcp" }, +] +mistral = [ + { name = "mistralai" }, +] +openai = [ + { name = "openai" }, + { name = "tiktoken" }, +] +retries = [ + { name = "tenacity" }, +] +temporal = [ + { name = "temporalio" }, +] +ui = [ + { name = "starlette" }, +] +vertexai = [ + { name = "google-auth" }, + { name = "requests" }, +] +xai = [ + { name = "xai-sdk" }, ] [[package]] name = "pydantic-core" -version = "2.33.2" +version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, +] + +[[package]] +name = "pydantic-evals" +version = "1.51.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "logfire-api" }, + { name = "pydantic" }, + { name = "pydantic-ai-slim" }, + { name = "pyyaml" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/72/bf5edba48c2fbaf0a337db79cb73bb150a054d0ae896f10ffeb67689f53b/pydantic_evals-1.51.0.tar.gz", hash = "sha256:3a96c70dec9e36ea5bc346490239a6e8d7fadcfdd5ea09d86b92da7a7a8d8db2", size = 47184, upload-time = "2026-01-31T02:06:28.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/44/b5af240324736c13011b2da1b9bb3249b83c53b036fbf44bf6d169a9b314/pydantic_evals-1.51.0-py3-none-any.whl", hash = "sha256:67d89d024d1d65691312a46f2a1130d0a882ed5e61dd40e78e168a67b398c7f6", size = 56378, upload-time = "2026-01-31T02:06:21.408Z" }, +] + +[[package]] +name = "pydantic-graph" +version = "1.51.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "logfire-api" }, + { name = "pydantic" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/b0/830861f07789c97240bcc8403547f68f9ee670b7db403fd3ead30ed5844b/pydantic_graph-1.51.0.tar.gz", hash = "sha256:6b6220c858e552df1ea76f8191bb12b13027f7e301d4f14ee593b0e55452a1a1", size = 58457, upload-time = "2026-01-31T02:06:29.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/f0/5256d6dcc4f669504183c11b67fd016d2a007b687198f500a7ec22cf6851/pydantic_graph-1.51.0-py3-none-any.whl", hash = "sha256:fcd6b94ddd1fd261f25888a2b7882a21e677b9718045e40af6321238538752d1", size = 72345, upload-time = "2026-01-31T02:06:22.539Z" }, ] [[package]] name = "pydantic-settings" -version = "2.9.1" +version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" }, + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, +] + +[[package]] +name = "pydocket" +version = "0.16.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, + { name = "fakeredis", extra = ["lua"] }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-prometheus" }, + { name = "opentelemetry-instrumentation" }, + { name = "prometheus-client" }, + { name = "py-key-value-aio", extra = ["memory", "redis"] }, + { name = "python-json-logger" }, + { name = "redis" }, + { name = "rich" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/00/26befe5f58df7cd1aeda4a8d10bc7d1908ffd86b80fd995e57a2a7b3f7bd/pydocket-0.16.6.tar.gz", hash = "sha256:b96c96ad7692827214ed4ff25fcf941ec38371314db5dcc1ae792b3e9d3a0294", size = 299054, upload-time = "2026-01-09T22:09:15.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/3f/7483e5a6dc6326b6e0c640619b5c5bd1d6e3c20e54d58f5fb86267cef00e/pydocket-0.16.6-py3-none-any.whl", hash = "sha256:683d21e2e846aa5106274e7d59210331b242d7fb0dce5b08d3b82065663ed183", size = 67697, upload-time = "2026-01-09T22:09:13.436Z" }, ] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] -name = "pytest" -version = "8.4.0" +name = "pyjwt" +version = "2.11.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232, upload-time = "2025-06-02T17:36:30.03Z" } + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload-time = "2025-06-02T17:36:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, ] [[package]] -name = "pytest-cov" -version = "6.1.1" +name = "python-dateutil" +version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage" }, - { name = "pytest" }, + { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] name = "python-dotenv" -version = "1.1.0" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + +[[package]] +name = "python-json-logger" +version = "4.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" }, + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, ] [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, ] [[package]] name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] [[package]] @@ -1546,6 +2213,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/26/1062c7ec1b053db9e499b4d2d5bc231743201b74051c973dadeac80a8f43/questionary-2.1.1-py3-none-any.whl", hash = "sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59", size = 36753, upload-time = "2025-08-28T19:00:19.56Z" }, ] +[[package]] +name = "redis" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, +] + [[package]] name = "referencing" version = "0.36.2" @@ -1553,7 +2229,6 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } wheels = [ @@ -1562,45 +2237,79 @@ wheels = [ [[package]] name = "regex" -version = "2024.11.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494, upload-time = "2024-11-06T20:12:31.635Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781, upload-time = "2024-11-06T20:10:07.07Z" }, - { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455, upload-time = "2024-11-06T20:10:09.117Z" }, - { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759, upload-time = "2024-11-06T20:10:11.155Z" }, - { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976, upload-time = "2024-11-06T20:10:13.24Z" }, - { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077, upload-time = "2024-11-06T20:10:15.37Z" }, - { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160, upload-time = "2024-11-06T20:10:19.027Z" }, - { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896, upload-time = "2024-11-06T20:10:21.85Z" }, - { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997, upload-time = "2024-11-06T20:10:24.329Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725, upload-time = "2024-11-06T20:10:28.067Z" }, - { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481, upload-time = "2024-11-06T20:10:31.612Z" }, - { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896, upload-time = "2024-11-06T20:10:34.054Z" }, - { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138, upload-time = "2024-11-06T20:10:36.142Z" }, - { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692, upload-time = "2024-11-06T20:10:38.394Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135, upload-time = "2024-11-06T20:10:40.367Z" }, - { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567, upload-time = "2024-11-06T20:10:43.467Z" }, - { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525, upload-time = "2024-11-06T20:10:45.19Z" }, - { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324, upload-time = "2024-11-06T20:10:47.177Z" }, - { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617, upload-time = "2024-11-06T20:10:49.312Z" }, - { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023, upload-time = "2024-11-06T20:10:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072, upload-time = "2024-11-06T20:10:52.926Z" }, - { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130, upload-time = "2024-11-06T20:10:54.828Z" }, - { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857, upload-time = "2024-11-06T20:10:56.634Z" }, - { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006, upload-time = "2024-11-06T20:10:59.369Z" }, - { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650, upload-time = "2024-11-06T20:11:02.042Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545, upload-time = "2024-11-06T20:11:03.933Z" }, - { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045, upload-time = "2024-11-06T20:11:06.497Z" }, - { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182, upload-time = "2024-11-06T20:11:09.06Z" }, - { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733, upload-time = "2024-11-06T20:11:11.256Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122, upload-time = "2024-11-06T20:11:13.161Z" }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545, upload-time = "2024-11-06T20:11:15Z" }, +version = "2026.1.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811, upload-time = "2026-01-14T23:18:02.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164, upload-time = "2026-01-14T23:15:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218, upload-time = "2026-01-14T23:15:15.647Z" }, + { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895, upload-time = "2026-01-14T23:15:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680, upload-time = "2026-01-14T23:15:19.342Z" }, + { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210, upload-time = "2026-01-14T23:15:22.005Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358, upload-time = "2026-01-14T23:15:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583, upload-time = "2026-01-14T23:15:26.526Z" }, + { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782, upload-time = "2026-01-14T23:15:29.352Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978, upload-time = "2026-01-14T23:15:31.295Z" }, + { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550, upload-time = "2026-01-14T23:15:33.558Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747, upload-time = "2026-01-14T23:15:35.206Z" }, + { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615, upload-time = "2026-01-14T23:15:37.523Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951, upload-time = "2026-01-14T23:15:39.582Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275, upload-time = "2026-01-14T23:15:42.108Z" }, + { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145, upload-time = "2026-01-14T23:15:44.244Z" }, + { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411, upload-time = "2026-01-14T23:15:45.858Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068, upload-time = "2026-01-14T23:15:47.616Z" }, + { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756, upload-time = "2026-01-14T23:15:49.307Z" }, + { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114, upload-time = "2026-01-14T23:15:51.484Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524, upload-time = "2026-01-14T23:15:53.102Z" }, + { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455, upload-time = "2026-01-14T23:15:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007, upload-time = "2026-01-14T23:15:57.041Z" }, + { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794, upload-time = "2026-01-14T23:15:58.916Z" }, + { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159, upload-time = "2026-01-14T23:16:00.817Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558, upload-time = "2026-01-14T23:16:03.267Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427, upload-time = "2026-01-14T23:16:05.646Z" }, + { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939, upload-time = "2026-01-14T23:16:07.542Z" }, + { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753, upload-time = "2026-01-14T23:16:10.395Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559, upload-time = "2026-01-14T23:16:12.347Z" }, + { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879, upload-time = "2026-01-14T23:16:14.049Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317, upload-time = "2026-01-14T23:16:15.718Z" }, + { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551, upload-time = "2026-01-14T23:16:17.533Z" }, + { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170, upload-time = "2026-01-14T23:16:19.835Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146, upload-time = "2026-01-14T23:16:21.541Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986, upload-time = "2026-01-14T23:16:23.381Z" }, + { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098, upload-time = "2026-01-14T23:16:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980, upload-time = "2026-01-14T23:16:27.297Z" }, + { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607, upload-time = "2026-01-14T23:16:29.235Z" }, + { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358, upload-time = "2026-01-14T23:16:31.369Z" }, + { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833, upload-time = "2026-01-14T23:16:33.141Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045, upload-time = "2026-01-14T23:16:35.005Z" }, + { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374, upload-time = "2026-01-14T23:16:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940, upload-time = "2026-01-14T23:16:38.685Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112, upload-time = "2026-01-14T23:16:40.646Z" }, + { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586, upload-time = "2026-01-14T23:16:42.799Z" }, + { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691, upload-time = "2026-01-14T23:16:44.671Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422, upload-time = "2026-01-14T23:16:46.607Z" }, + { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467, upload-time = "2026-01-14T23:16:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073, upload-time = "2026-01-14T23:16:51.154Z" }, + { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757, upload-time = "2026-01-14T23:16:53.08Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122, upload-time = "2026-01-14T23:16:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761, upload-time = "2026-01-14T23:16:57.251Z" }, + { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538, upload-time = "2026-01-14T23:16:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066, upload-time = "2026-01-14T23:17:01.607Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938, upload-time = "2026-01-14T23:17:04.366Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314, upload-time = "2026-01-14T23:17:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652, upload-time = "2026-01-14T23:17:08.521Z" }, + { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550, upload-time = "2026-01-14T23:17:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981, upload-time = "2026-01-14T23:17:12.871Z" }, + { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780, upload-time = "2026-01-14T23:17:14.837Z" }, + { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778, upload-time = "2026-01-14T23:17:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667, upload-time = "2026-01-14T23:17:19.301Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386, upload-time = "2026-01-14T23:17:21.231Z" }, + { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837, upload-time = "2026-01-14T23:17:23.146Z" }, ] [[package]] name = "requests" -version = "2.32.4" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1608,100 +2317,101 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, -] - -[[package]] -name = "requests-cache" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "cattrs" }, - { name = "platformdirs" }, - { name = "requests" }, - { name = "url-normalize" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/be/7b2a95a9e7a7c3e774e43d067c51244e61dea8b120ae2deff7089a93fb2b/requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1", size = 3018209, upload-time = "2024-06-18T17:18:03.774Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/2e/8f4051119f460cfc786aa91f212165bb6e643283b533db572d7b33952bd2/requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603", size = 61425, upload-time = "2024-06-18T17:17:45Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] -name = "requirements-parser" -version = "0.13.0" +name = "rich" +version = "14.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "packaging" }, + { name = "markdown-it-py" }, + { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/96/fb6dbfebb524d5601d359a47c78fe7ba1eef90fc4096404aa60c9a906fbb/requirements_parser-0.13.0.tar.gz", hash = "sha256:0843119ca2cb2331de4eb31b10d70462e39ace698fd660a915c247d2301a4418", size = 22630, upload-time = "2025-05-21T13:42:05.464Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/60/50fbb6ffb35f733654466f1a90d162bcbea358adc3b0871339254fbc37b2/requirements_parser-0.13.0-py3-none-any.whl", hash = "sha256:2b3173faecf19ec5501971b7222d38f04cb45bb9d87d0ad629ca71e2e62ded14", size = 14782, upload-time = "2025-05-21T13:42:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, ] [[package]] -name = "rich" -version = "13.9.4" +name = "rich-rst" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, + { name = "docutils" }, + { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, ] [[package]] name = "rpds-py" -version = "0.25.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647, upload-time = "2025-05-21T12:43:28.559Z" }, - { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454, upload-time = "2025-05-21T12:43:30.615Z" }, - { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665, upload-time = "2025-05-21T12:43:32.629Z" }, - { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873, upload-time = "2025-05-21T12:43:34.576Z" }, - { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866, upload-time = "2025-05-21T12:43:36.123Z" }, - { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886, upload-time = "2025-05-21T12:43:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666, upload-time = "2025-05-21T12:43:40.065Z" }, - { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109, upload-time = "2025-05-21T12:43:42.263Z" }, - { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244, upload-time = "2025-05-21T12:43:43.846Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023, upload-time = "2025-05-21T12:43:45.932Z" }, - { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634, upload-time = "2025-05-21T12:43:48.263Z" }, - { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713, upload-time = "2025-05-21T12:43:49.897Z" }, - { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280, upload-time = "2025-05-21T12:43:51.893Z" }, - { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399, upload-time = "2025-05-21T12:43:53.351Z" }, - { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, - { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, - { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, - { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, - { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, - { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, - { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, - { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, - { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, - { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, - { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, - { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, - { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, - { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, - { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, ] [[package]] @@ -1717,383 +2427,239 @@ wheels = [ ] [[package]] -name = "ruamel-yaml" -version = "0.17.40" +name = "s3transfer" +version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ruamel-yaml-clib", marker = "python_full_version < '3.13' and platform_python_implementation == 'CPython'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/d6/eb2833ccba5ea36f8f4de4bcfa0d1a91eb618f832d430b70e3086821f251/ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d", size = 137672, upload-time = "2023-10-20T12:53:56.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/79/5e2cffa1c77432f11cd93a5351f30732c997a239d3a3090856a72d6d8ba7/ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c", size = 113666, upload-time = "2023-10-20T12:53:52.628Z" }, -] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, - { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011, upload-time = "2024-10-20T10:13:04.377Z" }, - { url = "https://files.pythonhosted.org/packages/7f/5e/212f473a93ae78c669ffa0cb051e3fee1139cb2d385d2ae1653d64281507/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475", size = 642488, upload-time = "2024-10-20T10:13:05.906Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8f/ecfbe2123ade605c49ef769788f79c38ddb1c8fa81e01f4dbf5cf1a44b16/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef", size = 745066, upload-time = "2024-10-20T10:13:07.26Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785, upload-time = "2024-10-20T10:13:08.504Z" }, - { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017, upload-time = "2024-10-21T11:26:48.866Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270, upload-time = "2024-10-21T11:26:50.213Z" }, - { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059, upload-time = "2024-12-11T19:58:18.846Z" }, - { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583, upload-time = "2024-10-20T10:13:09.658Z" }, - { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190, upload-time = "2024-10-20T10:13:10.66Z" }, + { name = "botocore" }, ] - -[[package]] -name = "safetensors" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210, upload-time = "2025-02-26T09:15:13.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917, upload-time = "2025-02-26T09:15:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419, upload-time = "2025-02-26T09:15:01.765Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493, upload-time = "2025-02-26T09:14:51.812Z" }, - { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400, upload-time = "2025-02-26T09:14:53.549Z" }, - { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891, upload-time = "2025-02-26T09:14:55.717Z" }, - { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694, upload-time = "2025-02-26T09:14:57.036Z" }, - { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642, upload-time = "2025-02-26T09:15:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241, upload-time = "2025-02-26T09:14:58.303Z" }, - { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001, upload-time = "2025-02-26T09:15:05.79Z" }, - { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013, upload-time = "2025-02-26T09:15:07.892Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687, upload-time = "2025-02-26T09:15:09.979Z" }, - { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147, upload-time = "2025-02-26T09:15:11.185Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677, upload-time = "2025-02-26T09:15:16.554Z" }, - { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878, upload-time = "2025-02-26T09:15:14.99Z" }, + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] -name = "semgrep" -version = "1.85.0" +name = "secretstorage" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, - { name = "boltons" }, - { name = "click" }, - { name = "click-option-group" }, - { name = "colorama" }, - { name = "defusedxml" }, - { name = "exceptiongroup" }, - { name = "glom" }, - { name = "jsonschema" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-otlp-proto-http" }, - { name = "opentelemetry-instrumentation-requests" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "peewee" }, - { name = "requests" }, - { name = "rich" }, - { name = "ruamel-yaml" }, - { name = "tomli" }, - { name = "typing-extensions" }, - { name = "urllib3" }, - { name = "wcmatch" }, + { name = "cryptography" }, + { name = "jeepney" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/41/3e42c952458400baf0cf3f89e79dfc1d93ea636eedeb6ebaae33280e4383/semgrep-1.85.0.tar.gz", hash = "sha256:1a321cca4c5da84eb466ca1a4ceda10223e806225e371c4fef710cfe4b4b1df7", size = 27204522, upload-time = "2024-08-15T17:56:40.809Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/04/53e7df61e90f66f45e0f7d60b52d3787bdaae550c5c5a0940c40dce28036/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-any.whl", hash = "sha256:91fab3a0aa7f987a6605e01617179a363338350cca51174905d6ad0080a8d08e", size = 27619862, upload-time = "2024-08-15T17:56:24.981Z" }, - { url = "https://files.pythonhosted.org/packages/e0/6a/0762eded629759b3b876dcd81a33a736a3ecae08a4896f21abcc4c800b3d/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_10_14_x86_64.whl", hash = "sha256:a63055b392da70c46947780f43fecf54064fb60d8de11a16902e0cc149350a3e", size = 27833827, upload-time = "2024-08-15T17:56:29.344Z" }, - { url = "https://files.pythonhosted.org/packages/97/fc/d34edce42fc2785645f64345d91b3ebd7d705c129306e0b420bd4ae0d31a/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-macosx_11_0_arm64.whl", hash = "sha256:157b7724c35a8bda972921abfaaf252bdb754bbda004b2a82aaa38ac8099e176", size = 33557689, upload-time = "2024-08-15T17:56:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/23/0e/679e6fe0b6f3e1496f01ad28a79829b832df69f19815a3891f0e9a144b35/semgrep-1.85.0-cp38.cp39.cp310.cp311.py37.py38.py39.py310.py311-none-musllinux_1_0_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6b5d509576bbe0d68245d9ee2973ccecb485ca5907e85a7a8793552bf622cb", size = 32246576, upload-time = "2024-08-15T17:56:36.849Z" }, + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, ] [[package]] -name = "setuptools" -version = "80.9.0" +name = "shellingham" +version = "1.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] -name = "shared" -version = "0.0.32" +name = "six" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "kvf" }, - { name = "paradict" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/39/f39c2560ac971efbf437f7ffa1d82a12fa77f50b0127e6e5ec5cc8d377df/shared-0.0.32.tar.gz", hash = "sha256:7308adc95c0dab14d0c99635cd8049d1f004cc7fef7396d3fe47323c34ec58c6", size = 7793, upload-time = "2024-12-10T20:49:22.469Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/03/da58e40386d8ebcdfa3617070a95ca1deb5a5e6aa3d4e15ea2045173d5ac/shared-0.0.32-py3-none-any.whl", hash = "sha256:f17962c0f0fe6a23015accc7cac029e1c24c4b14578094e1f7033a7a7ef16140", size = 29304, upload-time = "2024-12-10T20:49:19.763Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] -name = "shellingham" -version = "1.5.4" +name = "sniffio" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] -name = "sniffio" -version = "1.3.1" +name = "sortedcontainers" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] name = "sre-agent" -version = "0.0.1" +version = "0.2.0" source = { editable = "." } dependencies = [ + { name = "boto3" }, { name = "click" }, - { name = "google-genai" }, - { name = "httpx" }, - { name = "prompt-toolkit" }, + { name = "fastapi" }, + { name = "platformdirs" }, + { name = "pydantic-ai" }, { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "questionary" }, { name = "rich" }, - { name = "types-requests" }, + { name = "uvicorn" }, ] [package.dev-dependencies] -ci = [ - { name = "anthropic" }, - { name = "fastapi" }, - { name = "llamafirewall" }, - { name = "mcp" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "python-dotenv" }, - { name = "shared" }, - { name = "transformers" }, - { name = "types-requests" }, -] dev = [ - { name = "licensecheck" }, { name = "mypy" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-cov" }, ] [package.metadata] requires-dist = [ - { name = "click", specifier = ">=8.0.0" }, - { name = "google-genai", specifier = ">=1.19.0" }, - { name = "httpx", specifier = ">=0.25.0" }, - { name = "prompt-toolkit", specifier = ">=3.0.52" }, - { name = "pydantic-settings", specifier = ">=2.9.1" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, - { name = "questionary", specifier = ">=2.0.0" }, - { name = "rich", specifier = ">=13.0.0" }, - { name = "types-requests", specifier = ">=2.32.0.20250602" }, + { name = "boto3", specifier = ">=1.42.39" }, + { name = "click", specifier = ">=8.3.1" }, + { name = "fastapi", specifier = ">=0.128.0" }, + { name = "platformdirs", specifier = ">=4.5.1" }, + { name = "pydantic-ai", specifier = ">=1.51.0" }, + { name = "pydantic-settings", specifier = ">=2.12.0" }, + { name = "python-dotenv", specifier = ">=1.2.1" }, + { name = "questionary", specifier = ">=2.1.1" }, + { name = "rich", specifier = ">=14.3.2" }, + { name = "uvicorn", specifier = ">=0.40.0" }, ] [package.metadata.requires-dev] -ci = [ - { name = "anthropic", specifier = ">=0.49.0" }, - { name = "fastapi", specifier = ">=0.115.12" }, - { name = "llamafirewall", specifier = ">=1.0.2" }, - { name = "mcp", specifier = ">=1.6.0" }, - { name = "pydantic", specifier = ">=2.11.3" }, - { name = "pydantic-settings", specifier = ">=2.9.1" }, - { name = "python-dotenv", specifier = ">=1.1.0" }, - { name = "shared" }, - { name = "transformers", specifier = ">=4.51.3" }, - { name = "types-requests", specifier = ">=2.32.0.20250328" }, -] -dev = [ - { name = "licensecheck", specifier = ">=2024.1.2" }, - { name = "mypy", specifier = ">=1.15.0" }, - { name = "pre-commit", specifier = ">=4.2.0" }, - { name = "pytest", specifier = ">=7.2.0" }, - { name = "pytest-cov", specifier = ">=4.0.0" }, -] +dev = [{ name = "mypy", specifier = ">=1.19.1" }] [[package]] name = "sse-starlette" -version = "2.3.6" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, + { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" }, + { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, ] [[package]] name = "starlette" -version = "0.46.2" +version = "0.50.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846, upload-time = "2025-04-13T13:56:17.942Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" }, + { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" }, ] [[package]] -name = "sympy" -version = "1.14.0" +name = "temporalio" +version = "1.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mpmath" }, + { name = "nexus-rpc" }, + { name = "protobuf" }, + { name = "types-protobuf" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/db/7d5118d28b0918888e1ec98f56f659fdb006351e06d95f30f4274962a76f/temporalio-1.20.0.tar.gz", hash = "sha256:5a6a85b7d298b7359bffa30025f7deac83c74ac095a4c6952fbf06c249a2a67c", size = 1850498, upload-time = "2025-11-25T21:25:20.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/f4/1b/e69052aa6003eafe595529485d9c62d1382dd5e671108f1bddf544fb6032/temporalio-1.20.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fba70314b4068f8b1994bddfa0e2ad742483f0ae714d2ef52e63013ccfd7042e", size = 12061638, upload-time = "2025-11-25T21:24:57.918Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/3e8c67ed7f23bedfa231c6ac29a7a9c12b89881da7694732270f3ecd6b0c/temporalio-1.20.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffc5bb6cabc6ae67f0bfba44de6a9c121603134ae18784a2ff3a7f230ad99080", size = 11562603, upload-time = "2025-11-25T21:25:01.721Z" }, + { url = "https://files.pythonhosted.org/packages/6d/be/ed0cc11702210522a79e09703267ebeca06eb45832b873a58de3ca76b9d0/temporalio-1.20.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1e80c1e4cdf88fa8277177f563edc91466fe4dc13c0322f26e55c76b6a219e6", size = 11824016, upload-time = "2025-11-25T21:25:06.771Z" }, + { url = "https://files.pythonhosted.org/packages/9d/97/09c5cafabc80139d97338a2bdd8ec22e08817dfd2949ab3e5b73565006eb/temporalio-1.20.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba92d909188930860c9d89ca6d7a753bc5a67e4e9eac6cea351477c967355eed", size = 12189521, upload-time = "2025-11-25T21:25:12.091Z" }, + { url = "https://files.pythonhosted.org/packages/11/23/5689c014a76aff3b744b3ee0d80815f63b1362637814f5fbb105244df09b/temporalio-1.20.0-cp310-abi3-win_amd64.whl", hash = "sha256:eacfd571b653e0a0f4aa6593f4d06fc628797898f0900d400e833a1f40cad03a", size = 12745027, upload-time = "2025-11-25T21:25:16.827Z" }, ] [[package]] name = "tenacity" -version = "8.5.0" +version = "9.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78", size = 47309, upload-time = "2024-07-05T07:25:31.836Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165, upload-time = "2024-07-05T07:25:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] [[package]] -name = "tokenizers" -version = "0.21.1" +name = "tiktoken" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/92/76/5ac0c97f1117b91b7eb7323dcd61af80d72f790b4df71249a7850c195f30/tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab", size = 343256, upload-time = "2025-03-13T10:51:18.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/1f/328aee25f9115bf04262e8b4e5a2050b7b7cf44b59c74e982db7270c7f30/tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41", size = 2780767, upload-time = "2025-03-13T10:51:09.459Z" }, - { url = "https://files.pythonhosted.org/packages/ae/1a/4526797f3719b0287853f12c5ad563a9be09d446c44ac784cdd7c50f76ab/tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3", size = 2650555, upload-time = "2025-03-13T10:51:07.692Z" }, - { url = "https://files.pythonhosted.org/packages/4d/7a/a209b29f971a9fdc1da86f917fe4524564924db50d13f0724feed37b2a4d/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f", size = 2937541, upload-time = "2025-03-13T10:50:56.679Z" }, - { url = "https://files.pythonhosted.org/packages/3c/1e/b788b50ffc6191e0b1fc2b0d49df8cff16fe415302e5ceb89f619d12c5bc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf", size = 2819058, upload-time = "2025-03-13T10:50:59.525Z" }, - { url = "https://files.pythonhosted.org/packages/36/aa/3626dfa09a0ecc5b57a8c58eeaeb7dd7ca9a37ad9dd681edab5acd55764c/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8", size = 3133278, upload-time = "2025-03-13T10:51:04.678Z" }, - { url = "https://files.pythonhosted.org/packages/a4/4d/8fbc203838b3d26269f944a89459d94c858f5b3f9a9b6ee9728cdcf69161/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0", size = 3144253, upload-time = "2025-03-13T10:51:01.261Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1b/2bd062adeb7c7511b847b32e356024980c0ffcf35f28947792c2d8ad2288/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c", size = 3398225, upload-time = "2025-03-13T10:51:03.243Z" }, - { url = "https://files.pythonhosted.org/packages/8a/63/38be071b0c8e06840bc6046991636bcb30c27f6bb1e670f4f4bc87cf49cc/tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a", size = 3038874, upload-time = "2025-03-13T10:51:06.235Z" }, - { url = "https://files.pythonhosted.org/packages/ec/83/afa94193c09246417c23a3c75a8a0a96bf44ab5630a3015538d0c316dd4b/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf", size = 9014448, upload-time = "2025-03-13T10:51:10.927Z" }, - { url = "https://files.pythonhosted.org/packages/ae/b3/0e1a37d4f84c0f014d43701c11eb8072704f6efe8d8fc2dcdb79c47d76de/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6", size = 8937877, upload-time = "2025-03-13T10:51:12.688Z" }, - { url = "https://files.pythonhosted.org/packages/ac/33/ff08f50e6d615eb180a4a328c65907feb6ded0b8f990ec923969759dc379/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d", size = 9186645, upload-time = "2025-03-13T10:51:14.723Z" }, - { url = "https://files.pythonhosted.org/packages/5f/aa/8ae85f69a9f6012c6f8011c6f4aa1c96154c816e9eea2e1b758601157833/tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f", size = 9384380, upload-time = "2025-03-13T10:51:16.526Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506, upload-time = "2025-03-13T10:51:20.643Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481, upload-time = "2025-03-13T10:51:19.243Z" }, + { name = "regex" }, + { name = "requests" }, ] - -[[package]] -name = "tomli" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/b9/de2a5c0144d7d75a57ff355c0c24054f965b2dc3036456ae03a51ea6264b/tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed", size = 16096, upload-time = "2024-10-02T10:46:13.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/db/ce8eda256fa131af12e0a76d481711abe4681b6923c27efb9a255c9e4594/tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38", size = 13237, upload-time = "2024-10-02T10:46:11.806Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, + { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, + { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, + { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, + { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, + { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, + { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, + { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, + { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, + { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188, upload-time = "2025-10-06T20:22:19.563Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978, upload-time = "2025-10-06T20:22:20.702Z" }, + { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271, upload-time = "2025-10-06T20:22:22.06Z" }, + { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216, upload-time = "2025-10-06T20:22:23.085Z" }, + { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860, upload-time = "2025-10-06T20:22:24.602Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567, upload-time = "2025-10-06T20:22:25.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067, upload-time = "2025-10-06T20:22:26.753Z" }, + { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473, upload-time = "2025-10-06T20:22:27.775Z" }, + { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855, upload-time = "2025-10-06T20:22:28.799Z" }, + { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022, upload-time = "2025-10-06T20:22:29.981Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736, upload-time = "2025-10-06T20:22:30.996Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908, upload-time = "2025-10-06T20:22:32.073Z" }, + { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706, upload-time = "2025-10-06T20:22:33.385Z" }, + { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667, upload-time = "2025-10-06T20:22:34.444Z" }, ] [[package]] -name = "torch" -version = "2.7.1" +name = "tokenizers" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "setuptools" }, - { name = "sympy" }, - { name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, - { name = "typing-extensions" }, + { name = "huggingface-hub" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/93/fb505a5022a2e908d81fe9a5e0aa84c86c0d5f408173be71c6018836f34e/torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa", size = 98948276, upload-time = "2025-06-04T17:39:12.852Z" }, - { url = "https://files.pythonhosted.org/packages/56/7e/67c3fe2b8c33f40af06326a3d6ae7776b3e3a01daa8f71d125d78594d874/torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc", size = 821025792, upload-time = "2025-06-04T17:34:58.747Z" }, - { url = "https://files.pythonhosted.org/packages/a1/37/a37495502bc7a23bf34f89584fa5a78e25bae7b8da513bc1b8f97afb7009/torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b", size = 216050349, upload-time = "2025-06-04T17:38:59.709Z" }, - { url = "https://files.pythonhosted.org/packages/3a/60/04b77281c730bb13460628e518c52721257814ac6c298acd25757f6a175c/torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb", size = 68645146, upload-time = "2025-06-04T17:38:52.97Z" }, - { url = "https://files.pythonhosted.org/packages/66/81/e48c9edb655ee8eb8c2a6026abdb6f8d2146abd1f150979ede807bb75dcb/torch-2.7.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:03563603d931e70722dce0e11999d53aa80a375a3d78e6b39b9f6805ea0a8d28", size = 98946649, upload-time = "2025-06-04T17:38:43.031Z" }, - { url = "https://files.pythonhosted.org/packages/3a/24/efe2f520d75274fc06b695c616415a1e8a1021d87a13c68ff9dce733d088/torch-2.7.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d632f5417b6980f61404a125b999ca6ebd0b8b4bbdbb5fbbba44374ab619a412", size = 821033192, upload-time = "2025-06-04T17:38:09.146Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d9/9c24d230333ff4e9b6807274f6f8d52a864210b52ec794c5def7925f4495/torch-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:23660443e13995ee93e3d844786701ea4ca69f337027b05182f5ba053ce43b38", size = 216055668, upload-time = "2025-06-04T17:38:36.253Z" }, - { url = "https://files.pythonhosted.org/packages/95/bf/e086ee36ddcef9299f6e708d3b6c8487c1651787bb9ee2939eb2a7f74911/torch-2.7.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0da4f4dba9f65d0d203794e619fe7ca3247a55ffdcbd17ae8fb83c8b2dc9b585", size = 68925988, upload-time = "2025-06-04T17:38:29.273Z" }, - { url = "https://files.pythonhosted.org/packages/69/6a/67090dcfe1cf9048448b31555af6efb149f7afa0a310a366adbdada32105/torch-2.7.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e08d7e6f21a617fe38eeb46dd2213ded43f27c072e9165dc27300c9ef9570934", size = 99028857, upload-time = "2025-06-04T17:37:50.956Z" }, - { url = "https://files.pythonhosted.org/packages/90/1c/48b988870823d1cc381f15ec4e70ed3d65e043f43f919329b0045ae83529/torch-2.7.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:30207f672328a42df4f2174b8f426f354b2baa0b7cca3a0adb3d6ab5daf00dc8", size = 821098066, upload-time = "2025-06-04T17:37:33.939Z" }, - { url = "https://files.pythonhosted.org/packages/7b/eb/10050d61c9d5140c5dc04a89ed3257ef1a6b93e49dd91b95363d757071e0/torch-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:79042feca1c634aaf6603fe6feea8c6b30dfa140a6bbc0b973e2260c7e79a22e", size = 216336310, upload-time = "2025-06-04T17:36:09.862Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/beb45cdf5c4fc3ebe282bf5eafc8dfd925ead7299b3c97491900fe5ed844/torch-2.7.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:988b0cbc4333618a1056d2ebad9eb10089637b659eb645434d0809d8d937b946", size = 68645708, upload-time = "2025-06-04T17:34:39.852Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, ] [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, -] - -[[package]] -name = "transformers" -version = "4.52.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "requests" }, - { name = "safetensors" }, - { name = "tokenizers" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/a9/275037087f9d846580b02f2d7cae0e0a6955d46f84583d0151d6227bd416/transformers-4.52.4.tar.gz", hash = "sha256:aff3764441c1adc192a08dba49740d3cbbcb72d850586075aed6bd89b98203e6", size = 8945376, upload-time = "2025-05-30T09:17:17.947Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/f2/25b27b396af03d5b64e61976b14f7209e2939e9e806c10749b6d277c273e/transformers-4.52.4-py3-none-any.whl", hash = "sha256:203f5c19416d5877e36e88633943761719538a25d9775977a24fe77a1e5adfc7", size = 10460375, upload-time = "2025-05-30T09:17:14.477Z" }, -] - -[[package]] -name = "triton" -version = "3.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] +sdist = { url = "https://files.pythonhosted.org/packages/27/89/4b0001b2dab8df0a5ee2787dcbe771de75ded01f18f1f8d53dedeea2882b/tqdm-4.67.2.tar.gz", hash = "sha256:649aac53964b2cb8dec76a14b405a4c0d13612cb8933aae547dd144eacc99653", size = 169514, upload-time = "2026-01-30T23:12:06.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/5f/950fb373bf9c01ad4eb5a8cd5eaf32cdf9e238c02f9293557a2129b9c4ac/triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43", size = 155669138, upload-time = "2025-05-29T23:39:51.771Z" }, - { url = "https://files.pythonhosted.org/packages/74/1f/dfb531f90a2d367d914adfee771babbd3f1a5b26c3f5fbc458dee21daa78/triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240", size = 155673035, upload-time = "2025-05-29T23:40:02.468Z" }, - { url = "https://files.pythonhosted.org/packages/28/71/bd20ffcb7a64c753dc2463489a61bf69d531f308e390ad06390268c4ea04/triton-3.3.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3198adb9d78b77818a5388bff89fa72ff36f9da0bc689db2f0a651a67ce6a42", size = 155735832, upload-time = "2025-05-29T23:40:10.522Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/31eac96de2915cf20ccaed0225035db149dfb9165a9ed28d4b252ef3f7f7/tqdm-4.67.2-py3-none-any.whl", hash = "sha256:9a12abcbbff58b6036b2167d9d3853042b9d436fe7330f06ae047867f2f8e0a7", size = 78354, upload-time = "2026-01-30T23:12:04.368Z" }, ] [[package]] name = "typer" -version = "0.16.0" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2101,136 +2667,82 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.32.1.20251210" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" }, ] [[package]] name = "types-requests" -version = "2.32.0.20250602" +version = "2.32.4.20260107" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/b0/5321e6eeba5d59e4347fcf9bf06a5052f085c3aa0f4876230566d6a4dc97/types_requests-2.32.0.20250602.tar.gz", hash = "sha256:ee603aeefec42051195ae62ca7667cd909a2f8128fdf8aad9e8a5219ecfab3bf", size = 23042, upload-time = "2025-06-02T03:15:02.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/18/9b782980e575c6581d5c0c1c99f4c6f89a1d7173dad072ee96b2756c02e6/types_requests-2.32.0.20250602-py3-none-any.whl", hash = "sha256:f4f335f87779b47ce10b8b8597b409130299f6971ead27fead4fe7ba6ea3e726", size = 20638, upload-time = "2025-06-02T03:15:01.959Z" }, + { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" }, ] [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, -] - -[[package]] -name = "url-normalize" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/80/31/febb777441e5fcdaacb4522316bf2a527c44551430a4873b052d545e3279/url_normalize-2.2.1.tar.gz", hash = "sha256:74a540a3b6eba1d95bdc610c24f2c0141639f3ba903501e61a52a8730247ff37", size = 18846, upload-time = "2025-04-26T20:37:58.553Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/d9/5ec15501b675f7bc07c5d16aa70d8d778b12375686b6efd47656efdc67cd/url_normalize-2.2.1-py3-none-any.whl", hash = "sha256:3deb687587dc91f7b25c9ae5162ffc0f057ae85d22b1e15cf5698311247f567b", size = 14728, upload-time = "2025-04-26T20:37:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] name = "urllib3" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, -] - -[[package]] -name = "uv" -version = "0.7.12" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/35/360a4aa325254b7f11d0898d30588861428659011b34f1e19c40fdd15db6/uv-0.7.12.tar.gz", hash = "sha256:4aa152e6a70d5662ca66a918f697bf8fb710f391068aa7d04e032af2edebb095", size = 3298683, upload-time = "2025-06-06T20:39:04.308Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/64/ee9f1b27f006c49a6765e9655ab93e7c8cbd6f0bf8b731f30f608b0be9fd/uv-0.7.12-py3-none-linux_armv6l.whl", hash = "sha256:81824caf5756ffee54b4c937d92d7c8c224c416270c90a83b9b4a973f6e4e559", size = 17024991, upload-time = "2025-06-06T20:38:17.053Z" }, - { url = "https://files.pythonhosted.org/packages/43/aa/f42707faa13a9c1b4f662456b2dca4bde169eb921f135319d8856c6e5e8e/uv-0.7.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:02e67c5f9d141fb25976cddb28abceaf715412ed83070cb9b87c5c488c8451af", size = 17097383, upload-time = "2025-06-06T20:38:21.174Z" }, - { url = "https://files.pythonhosted.org/packages/b9/a9/0f27e16e161f98240a328b5201b8abf178b751fde4fc56c54c1321812cd5/uv-0.7.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e70a4393fd6a09b056e1ac500fe2b796d26c30783194868c6801ea08c3bbf863", size = 15812649, upload-time = "2025-06-06T20:38:23.51Z" }, - { url = "https://files.pythonhosted.org/packages/0b/eb/605d8f1d08606024209d0e31c3799c696199a887260ee1db52663e4da2e8/uv-0.7.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bb47326b9c4802db28e11f1aab174d5c9c0a8b26ed0a83094d3882dd8f5049ad", size = 16344497, upload-time = "2025-06-06T20:38:25.899Z" }, - { url = "https://files.pythonhosted.org/packages/b7/86/3503eb869fa17d607cc296a6514db52ec73c2ec85ad608952a207fd2e8ff/uv-0.7.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:14214a51e0ae0f0e8dbcac35a29722c45dbf40d0fd37309897642f7989af6caf", size = 16773525, upload-time = "2025-06-06T20:38:28.619Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d6/868fb3f0b9f2a0d2f14cb8079171b862adbd782e47e0469dad3d3d71c938/uv-0.7.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fa630d865111c26f26c5e6f4547a73b13284f098471a4ca982d7b0caf0e658b", size = 17551173, upload-time = "2025-06-06T20:38:31.166Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a8/b5be1c67c7894caf178e850903ac25f465e3508a6eada2ae735b187dc39d/uv-0.7.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1557a154d2c36030ff0b707f3c2bfafd977e54fcd4d628dd0fa8a265449e9f13", size = 18359491, upload-time = "2025-06-06T20:38:33.569Z" }, - { url = "https://files.pythonhosted.org/packages/95/23/f62bab13f67ed785f7ad01546c499809d1db71b03f94950380f0bc407625/uv-0.7.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e0ba7767b21d58d65703c3cd43814ccfe06d7664ac42b3589d5f2b72486b903", size = 18098855, upload-time = "2025-06-06T20:38:36.029Z" }, - { url = "https://files.pythonhosted.org/packages/a6/4a/db21a5d3839771799af2df366cc5ed0933ebe9fc9e920f212e33dc00136e/uv-0.7.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0672dc5dc1b0ae7191d11ecae8bb794c7e860936b66c2bc3855bd0dee17fca1", size = 18206282, upload-time = "2025-06-06T20:38:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ae/fcfd916cbc109c5626dc25b208395b47ba12b27af82f3bb8e247b4e95692/uv-0.7.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34b4ad4288828210c2e075934009903514ca97bd603aced7d0755040b4d0489", size = 17777690, upload-time = "2025-06-06T20:38:41.021Z" }, - { url = "https://files.pythonhosted.org/packages/92/78/608163b35ffaf1054cd10197646b6336e7be7b6a51dfef6d98a91600c6be/uv-0.7.12-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:8a7ed9e94ec409bfc7181ee274d1b0ed6292698a20df0ae035ce422224863af5", size = 16599406, upload-time = "2025-06-06T20:38:43.72Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d6/6fe3b16390472a9d31dd1e0e7e3759b884d71e8a0dff1baf4a753b4adaaa/uv-0.7.12-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:85e8d3dea95016a45ed8c48343f98734d1b5c4be7bba26257d4c8873059646fa", size = 16714823, upload-time = "2025-06-06T20:38:45.949Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a5/b0432a25eaa23e9f909649321784b8e4be4579e9957eb5d369aa30c79164/uv-0.7.12-py3-none-musllinux_1_1_i686.whl", hash = "sha256:01310c45d55f6e7580124c9b1f7e3586b9609c4f8e5a78558a75951b03541bb2", size = 17086446, upload-time = "2025-06-06T20:38:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/da/d8/673591f34f897aa4216144a513e60c2004399155c47e7b550612960359c6/uv-0.7.12-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:4c697ef9d9f6b6f42df5a661efa8a745c0e4c330039d45b549b2ca7e7b66f8a5", size = 17903789, upload-time = "2025-06-06T20:38:51.864Z" }, - { url = "https://files.pythonhosted.org/packages/15/09/e476187c0a1da78b9c2021f3c3ab31ed2469a70d222bde5dc892236b3c4f/uv-0.7.12-py3-none-win32.whl", hash = "sha256:6008abf92c8d37060944377d89bf9f514aa18370391d9d63dc7d449dac94aca1", size = 17344011, upload-time = "2025-06-06T20:38:54.276Z" }, - { url = "https://files.pythonhosted.org/packages/08/9e/c52c7f50280e57110ca79b6805877f50514d9a777d31a683a4eb1de52312/uv-0.7.12-py3-none-win_amd64.whl", hash = "sha256:bb57bd26becd86194788f832af373b6ba431314fa0f6f7e904c90cac1818a7dc", size = 18803328, upload-time = "2025-06-06T20:38:59.368Z" }, - { url = "https://files.pythonhosted.org/packages/8e/35/4800ff7bc1663d9f967eabc8440074f906c8a98ea28d1aae66d2d19b7ae9/uv-0.7.12-py3-none-win_arm64.whl", hash = "sha256:8aba24e12ded2f2974a2f213e55daabf78002613d3772c1396fc924c6682cd27", size = 17450522, upload-time = "2025-06-06T20:39:01.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] name = "uvicorn" -version = "0.34.3" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" }, -] - -[[package]] -name = "virtualenv" -version = "20.31.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, -] - -[[package]] -name = "wcmatch" -version = "8.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "bracex" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/c4/55e0d36da61d7b8b2a49fd273e6b296fd5e8471c72ebbe438635d1af3968/wcmatch-8.5.2.tar.gz", hash = "sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2", size = 114983, upload-time = "2024-05-15T12:51:08.054Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/78/533ef890536e5ba0fd4f7df37482b5800ecaaceae9afc30978a1a7f88ff1/wcmatch-8.5.2-py3-none-any.whl", hash = "sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478", size = 39397, upload-time = "2024-05-15T12:51:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091", size = 157587, upload-time = "2026-01-31T03:52:10.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e", size = 92981, upload-time = "2026-01-31T03:52:09.14Z" }, ] [[package]] @@ -2239,17 +2751,6 @@ version = "15.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, @@ -2265,54 +2766,139 @@ wheels = [ ] [[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +name = "wrapt" +version = "1.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, +] + +[[package]] +name = "xai-sdk" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/66/1e0163eac090733d0ed0836a0cd3c14f5b59abeaa6fdba71c7b56b1916e4/xai_sdk-1.6.1.tar.gz", hash = "sha256:b55528df188f8c8448484021d735f75b0e7d71719ddeb432c5f187ac67e3c983", size = 388223, upload-time = "2026-01-29T03:13:07.373Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, + { url = "https://files.pythonhosted.org/packages/94/98/8b4019b35f2200295c5eec8176da4b779ec3a0fd60eba7196b618f437e1f/xai_sdk-1.6.1-py3-none-any.whl", hash = "sha256:f478dee9bd8839b8d341bd075277d0432aff5cd7120a4284547d25c6c9e7ab3b", size = 240917, upload-time = "2026-01-29T03:13:05.626Z" }, ] [[package]] -name = "wrapt" -version = "1.17.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" }, - { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" }, - { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" }, - { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" }, - { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" }, - { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" }, - { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" }, - { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" }, - { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" }, - { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" }, - { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" }, - { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" }, - { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" }, - { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" }, - { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" }, - { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" }, - { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" }, - { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" }, - { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" }, - { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" }, +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] [[package]] From 80a95501aff67a56e0c4f81f5d69e37c5e381685 Mon Sep 17 00:00:00 2001 From: Oscar <25309418+osw282@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:01:56 +0000 Subject: [PATCH 2/8] Refactor and Fix ECS Agent Deployment (#154) * Update agents.md * ECS deployment * Simplify by removing server for now * Add logo * Update text colour * Update * Refactoring * Refactoring --- .env.example | 17 - AGENTS.md | 25 +- DEVELOPMENT.md | 13 +- Dockerfile | 11 +- README.md | 2 +- pyproject.toml | 2 - run.py | 60 -- src/sre_agent/__init__.py | 6 +- src/sre_agent/cli/config.py | 136 ---- src/sre_agent/cli/configuration.py | 262 ------- src/sre_agent/cli/configuration/__init__.py | 13 + src/sre_agent/cli/configuration/models.py | 70 ++ src/sre_agent/cli/configuration/options.py | 32 + .../cli/configuration/providers/__init__.py | 1 + .../cli/configuration/providers/aws.py | 133 ++++ src/sre_agent/cli/configuration/store.py | 52 ++ src/sre_agent/cli/configuration/wizard.py | 589 +++++++++++++++ src/sre_agent/cli/env.py | 79 ++ src/sre_agent/cli/interactive_shell.py | 12 +- src/sre_agent/cli/main.py | 2 + src/sre_agent/cli/mode/local.py | 16 +- src/sre_agent/cli/mode/remote.py | 684 ----------------- src/sre_agent/cli/mode/remote/__init__.py | 1 + src/sre_agent/cli/mode/remote/aws/__init__.py | 1 + .../cli/mode/remote/aws/ecs/__init__.py | 1 + .../cli/mode/remote/aws/ecs/errors.py | 98 +++ src/sre_agent/cli/mode/remote/aws/ecs/menu.py | 466 ++++++++++++ .../cli/mode/remote/aws/ecs/metadata.py | 88 +++ .../cli/mode/remote/aws/ecs/status.py | 155 ++++ .../cli/mode/remote/aws/ecs/steps.py | 689 ++++++++++++++++++ src/sre_agent/cli/mode/remote/menu.py | 21 + src/sre_agent/cli/presentation/__init__.py | 1 + .../cli/{ => presentation}/ascii_art.py | 0 .../cli/{ => presentation}/banner.py | 14 +- .../cli/{ui.py => presentation/console.py} | 0 src/sre_agent/cli/presentation/styles.py | 28 + src/sre_agent/config/__init__.py | 1 + src/sre_agent/config/paths.py | 36 + src/sre_agent/core/__init__.py | 6 +- src/sre_agent/core/agent.py | 12 +- src/sre_agent/core/api.py | 87 --- src/sre_agent/core/deployments/__init__.py | 14 - .../core/deployments/aws_ecs/__init__.py | 31 +- .../core/deployments/aws_ecs/cleanup.py | 24 +- .../core/deployments/aws_ecs/deploy.py | 16 - src/sre_agent/core/deployments/aws_ecs/ecr.py | 5 +- .../core/deployments/aws_ecs/ecs_tasks.py | 158 +++- src/sre_agent/core/deployments/aws_ecs/iam.py | 7 +- .../core/deployments/aws_ecs/images.py | 33 +- .../core/deployments/aws_ecs/models.py | 21 +- .../core/deployments/aws_ecs/network.py | 49 +- .../core/deployments/aws_ecs/secrets.py | 35 +- .../deployments/aws_ecs/security_groups.py | 21 +- .../core/deployments/aws_ecs/status.py | 66 +- src/sre_agent/core/prompts.py | 4 +- src/sre_agent/core/{config.py => settings.py} | 50 +- src/sre_agent/core/tools/cloudwatch.py | 4 +- src/sre_agent/core/tools/github.py | 4 +- src/sre_agent/core/tools/slack.py | 4 +- src/sre_agent/run.py | 83 +++ uv.lock | 28 - 61 files changed, 3006 insertions(+), 1573 deletions(-) delete mode 100644 .env.example delete mode 100644 run.py delete mode 100644 src/sre_agent/cli/config.py delete mode 100644 src/sre_agent/cli/configuration.py create mode 100644 src/sre_agent/cli/configuration/__init__.py create mode 100644 src/sre_agent/cli/configuration/models.py create mode 100644 src/sre_agent/cli/configuration/options.py create mode 100644 src/sre_agent/cli/configuration/providers/__init__.py create mode 100644 src/sre_agent/cli/configuration/providers/aws.py create mode 100644 src/sre_agent/cli/configuration/store.py create mode 100644 src/sre_agent/cli/configuration/wizard.py create mode 100644 src/sre_agent/cli/env.py delete mode 100644 src/sre_agent/cli/mode/remote.py create mode 100644 src/sre_agent/cli/mode/remote/__init__.py create mode 100644 src/sre_agent/cli/mode/remote/aws/__init__.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/__init__.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/errors.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/menu.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/metadata.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/status.py create mode 100644 src/sre_agent/cli/mode/remote/aws/ecs/steps.py create mode 100644 src/sre_agent/cli/mode/remote/menu.py create mode 100644 src/sre_agent/cli/presentation/__init__.py rename src/sre_agent/cli/{ => presentation}/ascii_art.py (100%) rename src/sre_agent/cli/{ => presentation}/banner.py (73%) rename src/sre_agent/cli/{ui.py => presentation/console.py} (100%) create mode 100644 src/sre_agent/cli/presentation/styles.py create mode 100644 src/sre_agent/config/__init__.py create mode 100644 src/sre_agent/config/paths.py delete mode 100644 src/sre_agent/core/api.py delete mode 100644 src/sre_agent/core/deployments/aws_ecs/deploy.py rename src/sre_agent/core/{config.py => settings.py} (62%) create mode 100644 src/sre_agent/run.py diff --git a/.env.example b/.env.example deleted file mode 100644 index 9f1cfed5..00000000 --- a/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# LLM Provider -ANTHROPIC_API_KEY=sk-ant-xxxx - -# AWS (or use AWS CLI credentials) -AWS_REGION=eu-west-2 -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_SESSION_TOKEN= - -# GitHub MCP Server (Remote) -GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxx -GITHUB_MCP_URL=https://api.githubcopilot.com/mcp/ - -# Slack MCP Server (runs as sidecar) -SLACK_BOT_TOKEN=xoxb-xxxx -SLACK_CHANNEL_ID=Cxxxxxxxxxx -SLACK_MCP_URL=http://localhost:13080/sse diff --git a/AGENTS.md b/AGENTS.md index 3a392b50..9fe4e410 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,35 @@ # AGENTS.md ## Do -- Always use the context7 MCP server to look up up-to-date library syntax and usage. +- Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask. - Keep code simple, with a strong focus on readability and maintainability. - Use UK English. +- No em dashes in comments and documentations. + +## Functions +- Keep each function concise, easy to read, and clearly named. +- Avoid functions that handle multiple responsibilities. Break them into smaller units unless there is a sensible trade off. +- Prioritise on readability, maintainability, reusability, and testability. ## Docstrings - Keep module-level and script top-level docstrings to a single line. - Use Google-style docstrings. - Do not include types for arguments. - Keep docstrings concise and only include what is necessary to help readers understand the function or class. + +### Docstrings Example +``` +def function_with_pep484_type_annotations(param1: int, param2: str) -> bool: + """Example function with PEP 484 type annotations. + + Important note. + + Args: + param1: The first parameter. + param2: The second parameter. + + Returns: + The return value. True for success, False otherwise. + + """ +``` diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b3cfa9b2..bde05bbe 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -4,12 +4,7 @@ This document is for developers of sre-agent, specifically for v0.2.0. ## To start the agent -Create a `.env` file: -```bash -cp .env.example .env -``` - -Add your AWS credentials and Anthropic API key. +Run the CLI once and complete the configuration wizard to create the user `.env` file in the platform config directory. Start the agent server and the Slack MCP server: ```bash @@ -39,9 +34,9 @@ If an MCP server exists for the service, you can use that. No interface implemen ```python # tools/example.py from pydantic_ai.mcp import MCPServerStdio -from sre_agent.config import AgentConfig +from sre_agent.core.settings import AgentSettings -def create_example_mcp_toolset(config: AgentConfig) -> MCPServerStdio: +def create_example_mcp_toolset(config: AgentSettings) -> MCPServerStdio: return MCPServerStdio( "docker", args=["run", "-i", "--rm", "-e", f"TOKEN={config.example.token}", "mcp/example"], @@ -70,7 +65,7 @@ class ExampleLogging(LoggingInterface): # Implementation using direct API calls ... -def create_example_toolset(config: AgentConfig) -> FunctionToolset: +def create_example_toolset(config: AgentSettings) -> FunctionToolset: toolset = FunctionToolset() impl = ExampleLogging(config.example.api_key) diff --git a/Dockerfile b/Dockerfile index 10a0da35..1b7f1567 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,12 +33,5 @@ ENV PATH="/app/.venv/bin:$PATH" ENV PYTHONUNBUFFERED=1 ENV PYTHONDONTWRITEBYTECODE=1 -# Expose the API port -EXPOSE 8000 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 - -# Run the FastAPI app -CMD ["uvicorn", "sre_agent.api:app", "--host", "0.0.0.0", "--port", "8000"] +# Run one diagnosis job and then exit +CMD ["python", "-m", "sre_agent.run"] diff --git a/README.md b/README.md index 9f3fa722..c309b133 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ WIP. To run the agent locally, use the CLI and follow the prompts: `uv run sre-agent`. -For direct local diagnostics without the CLI, start the Slack MCP container with `docker compose up -d slack`, then invoke `run.py` with a log group, service name, and time window in minutes, for example: `uv run python run.py /aws/containerinsights/no-loafers-for-you/application currencyservice 10`. +For direct local diagnostics without the CLI, start the Slack MCP container with `docker compose up -d slack`, then run the module with a log group, service name, and time window in minutes, for example: `uv run python -m sre_agent.run /aws/containerinsights/no-loafers-for-you/application currencyservice 10`. diff --git a/pyproject.toml b/pyproject.toml index 9ff27c18..347ed6dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,14 +9,12 @@ license = { text = "MIT" } dependencies = [ "boto3>=1.42.39", "click>=8.3.1", - "fastapi>=0.128.0", "platformdirs>=4.5.1", "pydantic-ai>=1.51.0", "pydantic-settings>=2.12.0", "python-dotenv>=1.2.1", "questionary>=2.1.1", "rich>=14.3.2", - "uvicorn>=0.40.0", ] [project.scripts] diff --git a/run.py b/run.py deleted file mode 100644 index bc0b15fd..00000000 --- a/run.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -"""Run the SRE Agent to diagnose errors.""" - -import asyncio -import logging -import sys -from pathlib import Path - -from dotenv import load_dotenv - -load_dotenv(Path(__file__).parent / ".env") - -# Configure logging to see tool calls and agent thoughts -logging.basicConfig(level=logging.INFO) -# Set pydantic_ai to INFO to see agent activity -logging.getLogger("pydantic_ai").setLevel(logging.INFO) - -from sre_agent import diagnose_error - - -async def main() -> None: - """Run the SRE Agent.""" - if len(sys.argv) < 3: - print("Usage: python run.py [time_range_minutes]") - print("Example: python run.py /aws/fluentbit/logs cartservice 10") - sys.exit(1) - - log_group = sys.argv[1] - service_name = sys.argv[2] - time_range_minutes = int(sys.argv[3]) if len(sys.argv) > 3 else 10 - - print(f"🔍 Diagnosing errors in {log_group}") - print(f" Service: {service_name}") - print(f" Time range: last {time_range_minutes} minutes") - print("-" * 60) - - try: - result = await diagnose_error( - log_group=log_group, - service_name=service_name, - time_range_minutes=time_range_minutes, - ) - - print("-" * 60) - print("📋 DIAGNOSIS RESULT") - print("-" * 60) - print(f"\nSummary: {result.summary}") - print(f"\nRoot Cause: {result.root_cause}") - - if result.suggested_fixes: - print("\nSuggested Fixes:") - for fix in result.suggested_fixes: - print(f" • {fix.description}") - except Exception as e: - print(f"\n❌ FATAL ERROR: {e}") - sys.exit(1) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/src/sre_agent/__init__.py b/src/sre_agent/__init__.py index cce880c4..fcedffdc 100644 --- a/src/sre_agent/__init__.py +++ b/src/sre_agent/__init__.py @@ -1,14 +1,14 @@ """Public API for the SRE Agent.""" from sre_agent.core.agent import create_sre_agent, diagnose_error -from sre_agent.core.config import AgentConfig, get_config from sre_agent.core.models import ErrorDiagnosis, LogEntry, LogQueryResult +from sre_agent.core.settings import AgentSettings, get_settings __all__ = [ "create_sre_agent", "diagnose_error", - "AgentConfig", - "get_config", + "AgentSettings", + "get_settings", "ErrorDiagnosis", "LogEntry", "LogQueryResult", diff --git a/src/sre_agent/cli/config.py b/src/sre_agent/cli/config.py deleted file mode 100644 index 73fcb9af..00000000 --- a/src/sre_agent/cli/config.py +++ /dev/null @@ -1,136 +0,0 @@ -"""CLI configuration helpers.""" - -import json -from dataclasses import asdict, dataclass, field, fields -from pathlib import Path -from typing import Any - -from platformdirs import user_config_dir - -APP_NAME = "sre-agent" -CONFIG_FILENAME = "config.json" - - -class ConfigError(RuntimeError): - """Configuration related errors.""" - - -@dataclass -class CliConfig: - """CLI configuration for ECS deployment.""" - - aws_region: str = "eu-west-2" - aws_profile: str | None = None - - project_name: str = "sre-agent" - cluster_name: str = "sre-agent" - task_family: str = "sre-agent" - task_cpu: int = 512 - task_memory: int = 1024 - image_tag: str = "latest" - - vpc_id: str | None = None - private_subnet_ids: list[str] = field(default_factory=list) - security_group_id: str | None = None - - ecr_repo_sre_agent: str = "sre-agent" - ecr_repo_slack_mcp: str = "sre-agent-slack-mcp" - - secret_anthropic_name: str = "sre-agent/anthropic_api_key" - secret_slack_bot_name: str = "sre-agent/slack_bot_token" - secret_github_token_name: str = "sre-agent/github_token" - secret_anthropic_arn: str | None = None - secret_slack_bot_arn: str | None = None - secret_github_token_arn: str | None = None - - exec_role_arn: str | None = None - task_role_arn: str | None = None - - ecr_sre_agent_uri: str | None = None - ecr_slack_mcp_uri: str | None = None - - task_definition_arn: str | None = None - cluster_arn: str | None = None - - model: str = "claude-sonnet-4-5-20250929" - slack_channel_id: str | None = None - github_mcp_url: str = "https://api.githubcopilot.com/mcp/" - log_group_name: str = "/ecs/sre-agent" - - slack_mcp_host: str = "127.0.0.1" - slack_mcp_port: int = 13080 - - -def config_dir() -> Path: - """Return the user configuration directory. - - Returns: - The user configuration directory path. - """ - return Path(user_config_dir(APP_NAME)) - - -def config_path() -> Path: - """Return the configuration file path. - - Returns: - The configuration file path. - """ - return config_dir() / CONFIG_FILENAME - - -def load_config() -> CliConfig: - """Load CLI configuration from disk. - - Returns: - The loaded configuration object. - """ - path = config_path() - if not path.exists(): - return CliConfig() - - try: - data = json.loads(path.read_text(encoding="utf-8")) - except json.JSONDecodeError as exc: - raise ConfigError(f"Invalid configuration file: {exc}") from exc - - if not isinstance(data, dict): - raise ConfigError("Configuration file must contain a JSON object.") - - return _config_from_dict(data) - - -def save_config(config: CliConfig) -> Path: - """Save CLI configuration to disk. - - Args: - config: Configuration object to save. - - Returns: - The saved configuration file path. - """ - path = config_path() - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(asdict(config), indent=2), encoding="utf-8") - return path - - -def _config_from_dict(data: dict[str, Any]) -> CliConfig: - """Build a CLI configuration from a dictionary. - - Args: - data: Dictionary containing configuration data. - - Returns: - The configuration object. - """ - allowed_fields = {field.name for field in fields(CliConfig)} - filtered: dict[str, Any] = {key: value for key, value in data.items() if key in allowed_fields} - - subnet_ids = filtered.get("private_subnet_ids") - if isinstance(subnet_ids, str): - filtered["private_subnet_ids"] = [ - item.strip() for item in subnet_ids.split(",") if item.strip() - ] - - return CliConfig(**filtered) diff --git a/src/sre_agent/cli/configuration.py b/src/sre_agent/cli/configuration.py deleted file mode 100644 index 634749cf..00000000 --- a/src/sre_agent/cli/configuration.py +++ /dev/null @@ -1,262 +0,0 @@ -"""Configuration setup for CLI runs.""" - -import os -import re -from pathlib import Path - -import questionary - -from sre_agent.cli.config import CliConfig, load_config, save_config -from sre_agent.cli.mode.paths import project_root -from sre_agent.cli.ui import console - - -def ensure_required_config() -> CliConfig: - """Ensure the required configuration is present. - - Returns: - The configuration object. - """ - config = load_config() - env_values = _load_env_values() - missing = _find_missing_config(env_values, config) - - if not missing: - console.print("[green]Configuration detected.[/green]") - reuse = questionary.confirm("Reuse existing configuration?", default=True).ask() - if reuse: - return config - console.print("[dim]Reconfiguring all settings.[/dim]") - return _run_config_wizard(config, env_values, force_reconfigure=True) - - console.print("[yellow]Configuration missing for:[/yellow]") - for item in missing: - console.print(f"- {item}") - - configure = questionary.confirm("Configure now?", default=True).ask() - if not configure: - console.print("[dim]Skipping configuration. Some features may fail.[/dim]") - return config - - return _run_config_wizard(config, env_values, force_reconfigure=False) - - -def _run_config_wizard( - config: CliConfig, - env_values: dict[str, str], - force_reconfigure: bool, -) -> CliConfig: - """Prompt for required configuration and save it to .env. - - Args: - config: Existing configuration values. - env_values: Current environment values. - force_reconfigure: Whether to ignore existing values. - - Returns: - The updated configuration object. - """ - env_path = project_root() / ".env" - updates: dict[str, str] = {} - - updates["ANTHROPIC_API_KEY"] = _prompt_secret( - "Anthropic API key:", - env_values.get("ANTHROPIC_API_KEY"), - force_reconfigure, - ) - updates["SLACK_BOT_TOKEN"] = _prompt_secret( - "Slack bot token:", - env_values.get("SLACK_BOT_TOKEN"), - force_reconfigure, - ) - slack_channel_id = _prompt_text( - "Slack channel ID:", - env_values.get("SLACK_CHANNEL_ID"), - force_reconfigure, - ) - updates["SLACK_CHANNEL_ID"] = slack_channel_id - updates["GITHUB_PERSONAL_ACCESS_TOKEN"] = _prompt_secret( - "GitHub token:", - env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"), - force_reconfigure, - ) - - use_profile = questionary.confirm( - "Use AWS_PROFILE instead of access keys?", - default=bool(env_values.get("AWS_PROFILE")), - ).ask() - if use_profile: - updates["AWS_PROFILE"] = _prompt_text( - "AWS_PROFILE:", - env_values.get("AWS_PROFILE"), - force_reconfigure, - ) - else: - updates["AWS_ACCESS_KEY_ID"] = _prompt_text( - "AWS access key ID:", - env_values.get("AWS_ACCESS_KEY_ID"), - force_reconfigure, - ) - updates["AWS_SECRET_ACCESS_KEY"] = _prompt_secret( - "AWS secret access key:", - env_values.get("AWS_SECRET_ACCESS_KEY"), - force_reconfigure, - ) - session_token = questionary.password("AWS session token (optional):").ask() - if session_token: - updates["AWS_SESSION_TOKEN"] = session_token - - updates["AWS_REGION"] = _prompt_text( - "AWS region:", - env_values.get("AWS_REGION", config.aws_region), - force_reconfigure, - ) - - _write_env_file(env_path, updates) - - config.slack_channel_id = slack_channel_id - config.aws_region = updates["AWS_REGION"] - save_config(config) - - console.print(f"[green]Saved configuration to {env_path}[/green]") - return config - - -def _find_missing_config(env_values: dict[str, str], config: CliConfig) -> list[str]: - """Return a list of missing configuration items. - - Args: - env_values: Current environment values. - config: Existing configuration values. - - Returns: - A list of missing configuration labels. - """ - missing = [] - if not env_values.get("ANTHROPIC_API_KEY"): - missing.append("Anthropic API key") - if not env_values.get("SLACK_BOT_TOKEN"): - missing.append("Slack bot token") - if not env_values.get("SLACK_CHANNEL_ID") and not config.slack_channel_id: - missing.append("Slack channel ID") - if not env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"): - missing.append("GitHub token") - - has_profile = bool(env_values.get("AWS_PROFILE") or config.aws_profile) - has_keys = bool(env_values.get("AWS_ACCESS_KEY_ID") and env_values.get("AWS_SECRET_ACCESS_KEY")) - if not (has_profile or has_keys): - missing.append("AWS credentials (AWS_PROFILE or access keys)") - - if not env_values.get("AWS_REGION") and not config.aws_region: - missing.append("AWS region") - - return missing - - -def _prompt_secret(label: str, current: str | None, force_reconfigure: bool) -> str: - """Prompt for a secret value. - - Args: - label: Prompt label for the value. - current: Current value if already set. - force_reconfigure: Whether to ignore existing values. - - Returns: - The selected secret value. - """ - if current and not force_reconfigure: - use_existing = questionary.confirm(f"{label} already set. Keep it?", default=True).ask() - if use_existing: - return current - value: str | None = questionary.password(label).ask() - if not value: - console.print("[yellow]Value required.[/yellow]") - return _prompt_secret(label, current, force_reconfigure) - return value - - -def _prompt_text(label: str, current: str | None, force_reconfigure: bool) -> str: - """Prompt for a text value. - - Args: - label: Prompt label for the value. - current: Current value if already set. - force_reconfigure: Whether to ignore existing values. - - Returns: - The selected text value. - """ - default = "" if force_reconfigure else (current or "") - value: str | None = questionary.text(label, default=default).ask() - if not value: - console.print("[yellow]Value required.[/yellow]") - return _prompt_text(label, current, force_reconfigure) - return value - - -def _load_env_values() -> dict[str, str]: - """Load .env values and overlay environment variables. - - Returns: - Combined .env and environment variable values. - """ - env_path = project_root() / ".env" - values = _read_env_file(env_path) - for key, value in os.environ.items(): - if value: - values[key] = value - return values - - -def _read_env_file(path: Path) -> dict[str, str]: - """Read simple key/value pairs from a .env file. - - Args: - path: Path to the .env file. - - Returns: - Parsed key/value pairs. - """ - if not path.exists(): - return {} - - values: dict[str, str] = {} - for raw_line in path.read_text(encoding="utf-8").splitlines(): - line = raw_line.strip() - if not line or line.startswith("#") or "=" not in line: - continue - key, value = line.split("=", 1) - values[key.strip()] = value.strip().strip("\"'") - return values - - -def _write_env_file(path: Path, updates: dict[str, str]) -> None: - """Write updates to the .env file. - - Args: - path: Path to the .env file. - updates: Values to write into the file. - """ - current = _read_env_file(path) - current.update({key: value for key, value in updates.items() if value}) - - lines = [] - for key, value in current.items(): - safe_value = _escape_env_value(value) - lines.append(f"{key}={safe_value}") - - path.write_text("\n".join(lines) + "\n", encoding="utf-8") - - -def _escape_env_value(value: str) -> str: - """Escape a value for .env output. - - Args: - value: Value to escape. - - Returns: - The escaped value. - """ - if re.search(r"\s", value): - return f'"{value}"' - return value diff --git a/src/sre_agent/cli/configuration/__init__.py b/src/sre_agent/cli/configuration/__init__.py new file mode 100644 index 00000000..401bcb81 --- /dev/null +++ b/src/sre_agent/cli/configuration/__init__.py @@ -0,0 +1,13 @@ +"""CLI configuration wizard package.""" + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.cli.configuration.store import ConfigError, load_config, save_config +from sre_agent.cli.configuration.wizard import ensure_required_config + +__all__ = [ + "CliConfig", + "ConfigError", + "ensure_required_config", + "load_config", + "save_config", +] diff --git a/src/sre_agent/cli/configuration/models.py b/src/sre_agent/cli/configuration/models.py new file mode 100644 index 00000000..7c994648 --- /dev/null +++ b/src/sre_agent/cli/configuration/models.py @@ -0,0 +1,70 @@ +"""CLI configuration models.""" + +from pydantic import BaseModel, ConfigDict, Field + + +class AwsConfig(BaseModel): + """AWS configuration values for CLI deployment.""" + + region: str = "eu-west-2" + profile: str | None = None + + +class EcsConfig(BaseModel): + """ECS configuration values for CLI deployment.""" + + project_name: str = "sre-agent" + cluster_name: str = "sre-agent" + task_family: str = "sre-agent" + task_cpu: int = 512 + task_memory: int = 1024 + task_cpu_architecture: str = "X86_64" + image_tag: str = "latest" + ecr_repo_sre_agent: str = "sre-agent" + ecr_repo_slack_mcp: str = "sre-agent-slack-mcp" + secret_anthropic_name: str = "sre-agent/anthropic_api_key" + secret_slack_bot_name: str = "sre-agent/slack_bot_token" + secret_github_token_name: str = "sre-agent/github_token" + log_group_name: str = "/ecs/sre-agent" + slack_mcp_host: str = "127.0.0.1" + slack_mcp_port: int = 13080 + + +class IntegrationConfig(BaseModel): + """Integration and provider configuration values.""" + + model: str = "claude-sonnet-4-5-20250929" + model_provider: str = "anthropic" + notification_platform: str = "slack" + code_repository_provider: str = "github" + deployment_platform: str = "aws" + logging_platform: str = "cloudwatch" + slack_channel_id: str | None = None + github_mcp_url: str = "https://api.githubcopilot.com/mcp/" + + +class DeploymentState(BaseModel): + """Runtime deployment state discovered or created by the CLI.""" + + vpc_id: str | None = None + private_subnet_ids: list[str] = Field(default_factory=list) + security_group_id: str | None = None + secret_anthropic_arn: str | None = None + secret_slack_bot_arn: str | None = None + secret_github_token_arn: str | None = None + exec_role_arn: str | None = None + task_role_arn: str | None = None + ecr_sre_agent_uri: str | None = None + task_definition_arn: str | None = None + cluster_arn: str | None = None + + +class CliConfig(BaseModel): + """CLI configuration and deployment state.""" + + model_config = ConfigDict(extra="ignore") + + aws: AwsConfig = Field(default_factory=AwsConfig) + ecs: EcsConfig = Field(default_factory=EcsConfig) + integrations: IntegrationConfig = Field(default_factory=IntegrationConfig) + deployment: DeploymentState = Field(default_factory=DeploymentState) diff --git a/src/sre_agent/cli/configuration/options.py b/src/sre_agent/cli/configuration/options.py new file mode 100644 index 00000000..16046e7f --- /dev/null +++ b/src/sre_agent/cli/configuration/options.py @@ -0,0 +1,32 @@ +"""Configuration wizard option constants for the CLI.""" + +MODEL_PROVIDER_ENV = "MODEL_PROVIDER" +NOTIFICATION_PLATFORM_ENV = "NOTIFICATION_PLATFORM" +CODE_REPOSITORY_PROVIDER_ENV = "CODE_REPOSITORY_PROVIDER" +DEPLOYMENT_PLATFORM_ENV = "DEPLOYMENT_PLATFORM" +LOGGING_PLATFORM_ENV = "LOGGING_PLATFORM" +LEGACY_SELECTION_ENV_KEYS: tuple[str, ...] = ( + MODEL_PROVIDER_ENV, + NOTIFICATION_PLATFORM_ENV, + CODE_REPOSITORY_PROVIDER_ENV, + DEPLOYMENT_PLATFORM_ENV, + LOGGING_PLATFORM_ENV, +) + +MODEL_PROVIDER_ANTHROPIC = "anthropic" +NOTIFICATION_PLATFORM_SLACK = "slack" +CODE_REPOSITORY_PROVIDER_GITHUB = "github" +DEPLOYMENT_PLATFORM_AWS = "aws" +LOGGING_PLATFORM_CLOUDWATCH = "cloudwatch" + +MODEL_PROVIDER_CHOICES: tuple[tuple[str, str], ...] = (("Anthropic", MODEL_PROVIDER_ANTHROPIC),) +NOTIFICATION_PLATFORM_CHOICES: tuple[tuple[str, str], ...] = ( + ("Slack", NOTIFICATION_PLATFORM_SLACK), +) +CODE_REPOSITORY_PROVIDER_CHOICES: tuple[tuple[str, str], ...] = ( + ("GitHub", CODE_REPOSITORY_PROVIDER_GITHUB), +) +DEPLOYMENT_PLATFORM_CHOICES: tuple[tuple[str, str], ...] = (("AWS", DEPLOYMENT_PLATFORM_AWS),) +AWS_LOGGING_PLATFORM_CHOICES: tuple[tuple[str, str], ...] = ( + ("CloudWatch", LOGGING_PLATFORM_CLOUDWATCH), +) diff --git a/src/sre_agent/cli/configuration/providers/__init__.py b/src/sre_agent/cli/configuration/providers/__init__.py new file mode 100644 index 00000000..c75a2bf7 --- /dev/null +++ b/src/sre_agent/cli/configuration/providers/__init__.py @@ -0,0 +1 @@ +"""Provider-specific helpers for CLI configuration.""" diff --git a/src/sre_agent/cli/configuration/providers/aws.py b/src/sre_agent/cli/configuration/providers/aws.py new file mode 100644 index 00000000..3e9183c2 --- /dev/null +++ b/src/sre_agent/cli/configuration/providers/aws.py @@ -0,0 +1,133 @@ +"""AWS configuration helpers for CLI setup.""" + +from collections.abc import Mapping +from dataclasses import dataclass + +import boto3 +from botocore.exceptions import ClientError, NoCredentialsError, ProfileNotFound + +from sre_agent.cli.configuration.models import CliConfig + + +@dataclass(frozen=True) +class AwsConnectionInputs: + """AWS values used to validate account access.""" + + region: str | None + profile: str | None + access_key_id: str | None + secret_access_key: str | None + session_token: str | None + + +@dataclass(frozen=True) +class AwsConnectionCheckResult: + """Result of an AWS connection check.""" + + success: bool + message: str + + +def build_aws_connection_inputs( + updates: Mapping[str, str], + env_values: Mapping[str, str], + config: CliConfig, +) -> AwsConnectionInputs: + """Resolve AWS connection inputs from wizard and existing values. + + Args: + updates: Values captured in the current setup wizard run. + env_values: Existing values from env file and process environment. + config: Cached CLI configuration values. + + Returns: + The resolved AWS connection inputs. + """ + region = _resolve_env_value("AWS_REGION", updates, env_values, config.aws.region) + profile = _resolve_env_value("AWS_PROFILE", updates, env_values, config.aws.profile) + access_key_id = _resolve_env_value("AWS_ACCESS_KEY_ID", updates, env_values) + secret_access_key = _resolve_env_value("AWS_SECRET_ACCESS_KEY", updates, env_values) + session_token = _resolve_env_value("AWS_SESSION_TOKEN", updates, env_values) + + return AwsConnectionInputs( + region=region, + profile=profile, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + ) + + +def validate_aws_connection(inputs: AwsConnectionInputs) -> AwsConnectionCheckResult: + """Validate AWS access by calling STS get_caller_identity. + + Args: + inputs: AWS connection inputs to validate. + + Returns: + The connection check result. + """ + try: + session = _create_aws_session(inputs) + identity = session.client("sts").get_caller_identity() + except ProfileNotFound as exc: + return AwsConnectionCheckResult(success=False, message=f"Profile not found: {exc}") + except NoCredentialsError as exc: + return AwsConnectionCheckResult(success=False, message=f"No AWS credentials found: {exc}") + except ClientError as exc: + return AwsConnectionCheckResult(success=False, message=str(exc)) + except Exception as exc: # noqa: BLE001 + return AwsConnectionCheckResult(success=False, message=str(exc)) + + account = str(identity.get("Account", "unknown-account")) + arn = str(identity.get("Arn", "unknown-arn")) + return AwsConnectionCheckResult( + success=True, + message=f"AWS connection successful. Account: {account}, Identity: {arn}", + ) + + +def _resolve_env_value( + key: str, + updates: Mapping[str, str], + env_values: Mapping[str, str], + fallback: str | None = None, +) -> str | None: + """Read a value from updates first, then env values. + + Args: + key: Name of the environment key. + updates: Values captured in the current setup wizard run. + env_values: Existing values from env file and process environment. + fallback: Value to use when key is absent in updates and env values. + + Returns: + The resolved value, if any. + """ + if key in updates: + return updates[key] or None + return env_values.get(key) or fallback + + +def _create_aws_session(inputs: AwsConnectionInputs) -> boto3.session.Session: + """Create an AWS session from resolved connection inputs. + + Args: + inputs: AWS connection inputs. + + Returns: + A boto3 session configured for the provided inputs. + """ + if inputs.profile: + return boto3.session.Session( + profile_name=inputs.profile, + region_name=inputs.region, + ) + if inputs.access_key_id and inputs.secret_access_key: + return boto3.session.Session( + aws_access_key_id=inputs.access_key_id, + aws_secret_access_key=inputs.secret_access_key, + aws_session_token=inputs.session_token, + region_name=inputs.region, + ) + return boto3.session.Session(region_name=inputs.region) diff --git a/src/sre_agent/cli/configuration/store.py b/src/sre_agent/cli/configuration/store.py new file mode 100644 index 00000000..7caad966 --- /dev/null +++ b/src/sre_agent/cli/configuration/store.py @@ -0,0 +1,52 @@ +"""CLI configuration persistence helpers.""" + +import json +from pathlib import Path + +from pydantic import ValidationError + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.config.paths import cli_config_path + + +class ConfigError(RuntimeError): + """Configuration related errors.""" + + +def load_config() -> CliConfig: + """Load CLI configuration from disk. + + Returns: + The loaded configuration object. + """ + path = cli_config_path() + if not path.exists(): + return CliConfig() + + try: + data = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + raise ConfigError(f"Invalid configuration file: {exc}") from exc + + if not isinstance(data, dict): + raise ConfigError("Configuration file must contain a JSON object.") + + try: + return CliConfig.model_validate(data) + except ValidationError as exc: + raise ConfigError(f"Invalid configuration values: {exc}") from exc + + +def save_config(config: CliConfig) -> Path: + """Save CLI configuration to disk. + + Args: + config: Configuration object to save. + + Returns: + The saved configuration file path. + """ + path = cli_config_path() + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(config.model_dump(mode="json"), indent=2), encoding="utf-8") + return path diff --git a/src/sre_agent/cli/configuration/wizard.py b/src/sre_agent/cli/configuration/wizard.py new file mode 100644 index 00000000..7c48f8a4 --- /dev/null +++ b/src/sre_agent/cli/configuration/wizard.py @@ -0,0 +1,589 @@ +"""Configuration setup for CLI runs.""" + +from dataclasses import dataclass + +import questionary + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.cli.configuration.options import ( + AWS_LOGGING_PLATFORM_CHOICES, + CODE_REPOSITORY_PROVIDER_CHOICES, + CODE_REPOSITORY_PROVIDER_GITHUB, + DEPLOYMENT_PLATFORM_AWS, + DEPLOYMENT_PLATFORM_CHOICES, + LEGACY_SELECTION_ENV_KEYS, + LOGGING_PLATFORM_CLOUDWATCH, + MODEL_PROVIDER_ANTHROPIC, + MODEL_PROVIDER_CHOICES, + NOTIFICATION_PLATFORM_CHOICES, + NOTIFICATION_PLATFORM_SLACK, +) +from sre_agent.cli.configuration.providers.aws import ( + build_aws_connection_inputs, + validate_aws_connection, +) +from sre_agent.cli.configuration.store import load_config, save_config +from sre_agent.cli.env import load_env_values, write_env_file +from sre_agent.cli.presentation.console import console +from sre_agent.config.paths import env_path + + +@dataclass(frozen=True) +class _MissingConfigItem: + """A missing configuration item and whether to show it in summary.""" + + label: str + visible: bool = True + + +@dataclass(frozen=True) +class _WizardSelections: + """Selected providers and platforms from the configuration wizard.""" + + model_provider: str + notification_platform: str + code_repository_provider: str + deployment_platform: str + logging_platform: str + slack_channel_id: str | None + + +def ensure_required_config() -> CliConfig: + """Ensure the required configuration is present. + + Returns: + The configuration object. + """ + config = load_config() + env_values = load_env_values() + missing_items = _find_missing_config_items(env_values, config) + + if not missing_items: + console.print("[orange1]Configuration detected.[/orange1]") + reuse = questionary.confirm("Reuse existing configuration?", default=True).ask() + if reuse: + return config + console.print("[dim]Reconfiguring all settings.[/dim]") + return _run_config_wizard(config, env_values, force_reconfigure=True) + + visible_missing_items = [item for item in missing_items if item.visible] + + console.print("[yellow]No configurations found[/yellow]") + for item in visible_missing_items: + console.print(f"[#e0e0e0]- {item.label}[/#e0e0e0]") + + configure = questionary.confirm("Configure now?", default=True).ask() + if not configure: + console.print("[dim]Skipping configuration. Some features may fail.[/dim]") + return config + + return _run_config_wizard(config, env_values, force_reconfigure=False) + + +def _run_config_wizard( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, +) -> CliConfig: + """Prompt for required configuration and save it to the user env file. + + Args: + config: Existing configuration values. + env_values: Current environment values. + force_reconfigure: Whether to ignore existing values. + + Returns: + The updated configuration object. + """ + env_file_path = env_path() + updates: dict[str, str] = {} + + model_provider = _configure_model_provider( + config, + env_values, + force_reconfigure, + updates, + ) + notification_platform, slack_channel_id = _configure_notification_platform( + config, + env_values, + force_reconfigure, + updates, + ) + code_repository_provider = _configure_code_repository_provider( + config, + env_values, + force_reconfigure, + updates, + ) + deployment_platform, logging_platform = _configure_deployment_platform( + config, + env_values, + force_reconfigure, + updates, + ) + + _clear_legacy_selection_env_keys(updates) + + write_env_file(env_file_path, updates) + + selections = _WizardSelections( + model_provider=model_provider, + notification_platform=notification_platform, + code_repository_provider=code_repository_provider, + deployment_platform=deployment_platform, + logging_platform=logging_platform, + slack_channel_id=slack_channel_id, + ) + _persist_wizard_choices( + config, + selections, + updates, + ) + + console.print(f"[green]Saved configuration to {env_file_path}[/green]") + return config + + +def _configure_model_provider( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> str: + """Prompt for model provider and required credentials.""" + model_provider = _prompt_choice( + "Model provider:", + config.integrations.model_provider, + force_reconfigure, + MODEL_PROVIDER_CHOICES, + MODEL_PROVIDER_ANTHROPIC, + ) + if model_provider == MODEL_PROVIDER_ANTHROPIC: + updates["ANTHROPIC_API_KEY"] = _prompt_secret( + "Anthropic API key:", + env_values.get("ANTHROPIC_API_KEY"), + force_reconfigure, + ) + else: + _clear_env_keys(updates, "ANTHROPIC_API_KEY") + return model_provider + + +def _configure_notification_platform( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> tuple[str, str | None]: + """Prompt for notification platform and required credentials.""" + notification_platform = _prompt_choice( + "Messaging/notification platform:", + config.integrations.notification_platform, + force_reconfigure, + NOTIFICATION_PLATFORM_CHOICES, + NOTIFICATION_PLATFORM_SLACK, + ) + if notification_platform != NOTIFICATION_PLATFORM_SLACK: + _clear_env_keys(updates, "SLACK_BOT_TOKEN", "SLACK_CHANNEL_ID") + return notification_platform, None + + updates["SLACK_BOT_TOKEN"] = _prompt_secret( + "Slack bot token:", + env_values.get("SLACK_BOT_TOKEN"), + force_reconfigure, + ) + slack_channel_id = _prompt_text( + "Slack channel ID:", + env_values.get("SLACK_CHANNEL_ID") or config.integrations.slack_channel_id, + force_reconfigure, + ) + updates["SLACK_CHANNEL_ID"] = slack_channel_id + return notification_platform, slack_channel_id + + +def _configure_code_repository_provider( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> str: + """Prompt for code repository provider and required credentials.""" + code_repository_provider = _prompt_choice( + "Remote code repository:", + config.integrations.code_repository_provider, + force_reconfigure, + CODE_REPOSITORY_PROVIDER_CHOICES, + CODE_REPOSITORY_PROVIDER_GITHUB, + ) + if code_repository_provider == CODE_REPOSITORY_PROVIDER_GITHUB: + updates["GITHUB_PERSONAL_ACCESS_TOKEN"] = _prompt_secret( + "GitHub token:", + env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"), + force_reconfigure, + ) + else: + _clear_env_keys(updates, "GITHUB_PERSONAL_ACCESS_TOKEN") + return code_repository_provider + + +def _configure_deployment_platform( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> tuple[str, str]: + """Prompt for deployment platform, logging platform, and AWS credentials.""" + deployment_platform = _prompt_choice( + "Which platform is your application deployed on?", + config.integrations.deployment_platform, + force_reconfigure, + DEPLOYMENT_PLATFORM_CHOICES, + DEPLOYMENT_PLATFORM_AWS, + ) + if deployment_platform != DEPLOYMENT_PLATFORM_AWS: + return deployment_platform, config.integrations.logging_platform + + logging_platform = _prompt_choice( + "Logging platform:", + config.integrations.logging_platform, + force_reconfigure, + AWS_LOGGING_PLATFORM_CHOICES, + LOGGING_PLATFORM_CLOUDWATCH, + ) + _configure_aws_credentials(config, env_values, force_reconfigure, updates) + _report_aws_connection_check(updates, env_values, config) + return deployment_platform, logging_platform + + +def _configure_aws_credentials( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> None: + """Prompt for AWS credentials and region.""" + use_profile = questionary.confirm( + "Use AWS_PROFILE instead of access keys?", + default=bool(env_values.get("AWS_PROFILE") or config.aws.profile), + ).ask() + if use_profile: + _configure_aws_profile_credentials(config, env_values, force_reconfigure, updates) + else: + _configure_aws_access_key_credentials(env_values, force_reconfigure, updates) + + updates["AWS_REGION"] = _prompt_text( + "AWS region:", + env_values.get("AWS_REGION", config.aws.region), + force_reconfigure, + ) + + +def _configure_aws_profile_credentials( + config: CliConfig, + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> None: + """Prompt for AWS profile credentials.""" + updates["AWS_PROFILE"] = _prompt_text( + "AWS_PROFILE:", + env_values.get("AWS_PROFILE") or config.aws.profile, + force_reconfigure, + ) + _clear_env_keys( + updates, + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + ) + + +def _configure_aws_access_key_credentials( + env_values: dict[str, str], + force_reconfigure: bool, + updates: dict[str, str], +) -> None: + """Prompt for AWS access key credentials.""" + updates["AWS_PROFILE"] = _empty_env_value() + updates["AWS_ACCESS_KEY_ID"] = _prompt_text( + "AWS access key ID:", + env_values.get("AWS_ACCESS_KEY_ID"), + force_reconfigure, + ) + updates["AWS_SECRET_ACCESS_KEY"] = _prompt_secret( + "AWS secret access key:", + env_values.get("AWS_SECRET_ACCESS_KEY"), + force_reconfigure, + ) + session_token = questionary.password("AWS session token (optional):").ask() + updates["AWS_SESSION_TOKEN"] = session_token or _empty_env_value() + + +def _clear_legacy_selection_env_keys(updates: dict[str, str]) -> None: + """Remove deprecated provider selection keys from the env file.""" + _clear_env_keys(updates, *LEGACY_SELECTION_ENV_KEYS) + + +def _clear_env_keys(updates: dict[str, str], *keys: str) -> None: + """Set keys to empty values so the env writer removes them.""" + for key in keys: + updates[key] = _empty_env_value() + + +def _empty_env_value() -> str: + """Return a canonical empty env value.""" + return "" + + +def _persist_wizard_choices( + config: CliConfig, + selections: _WizardSelections, + updates: dict[str, str], +) -> None: + """Persist wizard choices to cached CLI config.""" + config.integrations.model_provider = selections.model_provider + config.integrations.notification_platform = selections.notification_platform + config.integrations.code_repository_provider = selections.code_repository_provider + config.integrations.deployment_platform = selections.deployment_platform + config.integrations.logging_platform = selections.logging_platform + config.integrations.slack_channel_id = selections.slack_channel_id + if selections.deployment_platform == DEPLOYMENT_PLATFORM_AWS: + config.aws.region = updates["AWS_REGION"] + config.aws.profile = updates["AWS_PROFILE"] or None + save_config(config) + + +def _find_missing_config_items( + env_values: dict[str, str], + config: CliConfig, +) -> list[_MissingConfigItem]: + """Return missing configuration items. + + Args: + env_values: Current environment values. + config: Existing configuration values. + + Returns: + A list of missing configuration items. + """ + missing: list[_MissingConfigItem] = [] + _append_model_missing_items(missing, env_values, config) + _append_notification_missing_items(missing, env_values, config) + _append_repository_missing_items(missing, env_values, config) + _append_deployment_missing_items(missing, env_values, config) + return missing + + +def _append_model_missing_items( + missing: list[_MissingConfigItem], + env_values: dict[str, str], + config: CliConfig, +) -> None: + """Append missing model configuration items.""" + model_provider_value = config.integrations.model_provider + if not _is_supported_choice(model_provider_value, MODEL_PROVIDER_CHOICES): + missing.append(_MissingConfigItem("Model provider")) + model_provider = _normalise_choice( + model_provider_value, + MODEL_PROVIDER_CHOICES, + MODEL_PROVIDER_ANTHROPIC, + ) + if model_provider == MODEL_PROVIDER_ANTHROPIC and not env_values.get("ANTHROPIC_API_KEY"): + missing.append(_MissingConfigItem("Anthropic API key", visible=False)) + + +def _append_notification_missing_items( + missing: list[_MissingConfigItem], + env_values: dict[str, str], + config: CliConfig, +) -> None: + """Append missing notification configuration items.""" + notification_platform_value = config.integrations.notification_platform + if not _is_supported_choice(notification_platform_value, NOTIFICATION_PLATFORM_CHOICES): + missing.append(_MissingConfigItem("Messaging/notification platform")) + notification_platform = _normalise_choice( + notification_platform_value, + NOTIFICATION_PLATFORM_CHOICES, + NOTIFICATION_PLATFORM_SLACK, + ) + if notification_platform != NOTIFICATION_PLATFORM_SLACK: + return + if not env_values.get("SLACK_BOT_TOKEN"): + missing.append(_MissingConfigItem("Slack bot token", visible=False)) + if not env_values.get("SLACK_CHANNEL_ID") and not config.integrations.slack_channel_id: + missing.append(_MissingConfigItem("Slack channel ID")) + + +def _append_repository_missing_items( + missing: list[_MissingConfigItem], + env_values: dict[str, str], + config: CliConfig, +) -> None: + """Append missing repository configuration items.""" + code_repository_provider_value = config.integrations.code_repository_provider + if not _is_supported_choice(code_repository_provider_value, CODE_REPOSITORY_PROVIDER_CHOICES): + missing.append(_MissingConfigItem("Remote code repository")) + code_repository_provider = _normalise_choice( + code_repository_provider_value, + CODE_REPOSITORY_PROVIDER_CHOICES, + CODE_REPOSITORY_PROVIDER_GITHUB, + ) + if code_repository_provider == CODE_REPOSITORY_PROVIDER_GITHUB and not env_values.get( + "GITHUB_PERSONAL_ACCESS_TOKEN" + ): + missing.append(_MissingConfigItem("GitHub token", visible=False)) + + +def _append_deployment_missing_items( + missing: list[_MissingConfigItem], + env_values: dict[str, str], + config: CliConfig, +) -> None: + """Append missing deployment configuration items.""" + deployment_platform_value = config.integrations.deployment_platform + if not _is_supported_choice(deployment_platform_value, DEPLOYMENT_PLATFORM_CHOICES): + missing.append(_MissingConfigItem("Deployment platform")) + deployment_platform = _normalise_choice( + deployment_platform_value, + DEPLOYMENT_PLATFORM_CHOICES, + DEPLOYMENT_PLATFORM_AWS, + ) + if deployment_platform != DEPLOYMENT_PLATFORM_AWS: + return + + if not _is_supported_choice(config.integrations.logging_platform, AWS_LOGGING_PLATFORM_CHOICES): + missing.append(_MissingConfigItem("Logging platform")) + has_profile = bool(env_values.get("AWS_PROFILE") or config.aws.profile) + has_keys = bool(env_values.get("AWS_ACCESS_KEY_ID") and env_values.get("AWS_SECRET_ACCESS_KEY")) + if not (has_profile or has_keys): + missing.append(_MissingConfigItem("AWS credentials (AWS_PROFILE or access keys)")) + if not env_values.get("AWS_REGION") and not config.aws.region: + missing.append(_MissingConfigItem("AWS region")) + + +def _prompt_choice( + label: str, + current: str | None, + force_reconfigure: bool, + choices: tuple[tuple[str, str], ...], + fallback: str, +) -> str: + """Prompt for a single choice value. + + Args: + label: Prompt label for the choice. + current: Current value if already set. + force_reconfigure: Whether to ignore existing values. + choices: Available display/value pairs. + fallback: Default choice value. + + Returns: + The selected value. + """ + default = fallback if force_reconfigure else _normalise_choice(current, choices, fallback) + selection = questionary.select( + label, + choices=[questionary.Choice(title=title, value=value) for title, value in choices], + default=default, + ).ask() + if not selection: + console.print("[yellow]Selection required.[/yellow]") + return _prompt_choice(label, current, force_reconfigure, choices, fallback) + return str(selection) + + +def _normalise_choice( + value: str | None, + choices: tuple[tuple[str, str], ...], + fallback: str, +) -> str: + """Return a supported value or a fallback. + + Args: + value: Current or selected value. + choices: Available display/value pairs. + fallback: Value to use when the current value is unsupported. + + Returns: + A supported choice value. + """ + if _is_supported_choice(value, choices): + return str(value) + return fallback + + +def _is_supported_choice(value: str | None, choices: tuple[tuple[str, str], ...]) -> bool: + """Return true when a value exists in a choice list. + + Args: + value: Current or selected value. + choices: Available display/value pairs. + + Returns: + True when the value is one of the choice values. + """ + if not value: + return False + return any(value == choice_value for _, choice_value in choices) + + +def _prompt_secret(label: str, current: str | None, force_reconfigure: bool) -> str: + """Prompt for a secret value. + + Args: + label: Prompt label for the value. + current: Current value if already set. + force_reconfigure: Whether to ignore existing values. + + Returns: + The selected secret value. + """ + if current and not force_reconfigure: + use_existing = questionary.confirm(f"{label} already set. Keep it?", default=True).ask() + if use_existing: + return current + value: str | None = questionary.password(label).ask() + if not value: + console.print("[yellow]Value required.[/yellow]") + return _prompt_secret(label, current, force_reconfigure) + return value + + +def _prompt_text(label: str, current: str | None, force_reconfigure: bool) -> str: + """Prompt for a text value. + + Args: + label: Prompt label for the value. + current: Current value if already set. + force_reconfigure: Whether to ignore existing values. + + Returns: + The selected text value. + """ + default = "" if force_reconfigure else (current or "") + value: str | None = questionary.text(label, default=default).ask() + if not value: + console.print("[yellow]Value required.[/yellow]") + return _prompt_text(label, current, force_reconfigure) + return value + + +def _report_aws_connection_check( + updates: dict[str, str], + env_values: dict[str, str], + config: CliConfig, +) -> None: + """Check AWS credentials and display the current identity.""" + console.print("[cyan]Checking AWS connection...[/cyan]") + connection_inputs = build_aws_connection_inputs(updates, env_values, config) + result = validate_aws_connection(connection_inputs) + if result.success: + console.print(f"[green]{result.message}[/green]") + return + + console.print(f"[yellow]AWS connection check failed: {result.message}[/yellow]") + console.print( + "[dim]You can continue, but deployment and diagnostics will fail " + "until credentials are fixed.[/dim]" + ) diff --git a/src/sre_agent/cli/env.py b/src/sre_agent/cli/env.py new file mode 100644 index 00000000..73fe115d --- /dev/null +++ b/src/sre_agent/cli/env.py @@ -0,0 +1,79 @@ +"""User env file helpers for the CLI.""" + +import os +import re +from pathlib import Path + +from sre_agent.config.paths import env_path + + +def load_env_values() -> dict[str, str]: + """Load env file values and overlay environment variables. + + Returns: + Combined env file and environment variable values. + """ + values = read_env_file(env_path()) + for key, value in os.environ.items(): + if value: + values[key] = value + return values + + +def read_env_file(path: Path) -> dict[str, str]: + """Read simple key/value pairs from an env file. + + Args: + path: Path to the env file. + + Returns: + Parsed key/value pairs. + """ + if not path.exists(): + return {} + + values: dict[str, str] = {} + for raw_line in path.read_text(encoding="utf-8").splitlines(): + line = raw_line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, value = line.split("=", 1) + values[key.strip()] = value.strip().strip("\"'") + return values + + +def write_env_file(path: Path, updates: dict[str, str]) -> None: + """Write updates to the env file. + + Args: + path: Path to the env file. + updates: Values to write into the file. + """ + path.parent.mkdir(parents=True, exist_ok=True) + current = read_env_file(path) + for key, value in updates.items(): + if value: + current[key] = value + elif key in current: + current.pop(key, None) + + lines = [] + for key, value in current.items(): + safe_value = _escape_env_value(value) + lines.append(f"{key}={safe_value}") + + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def _escape_env_value(value: str) -> str: + """Escape a value for env output. + + Args: + value: Value to escape. + + Returns: + The escaped value. + """ + if re.search(r"\s", value): + return f'"{value}"' + return value diff --git a/src/sre_agent/cli/interactive_shell.py b/src/sre_agent/cli/interactive_shell.py index 05669641..49f03099 100644 --- a/src/sre_agent/cli/interactive_shell.py +++ b/src/sre_agent/cli/interactive_shell.py @@ -2,13 +2,11 @@ import questionary -from sre_agent.cli.banner import print_global_banner from sre_agent.cli.configuration import ensure_required_config from sre_agent.cli.mode.local import run_local_mode -from sre_agent.cli.mode.remote import run_remote_mode -from sre_agent.cli.ui import console - -REMOTE_DEPLOYMENT_LABEL = "Remote Deployment (WIP)" +from sre_agent.cli.mode.remote.menu import run_remote_mode +from sre_agent.cli.presentation.banner import print_global_banner +from sre_agent.cli.presentation.console import console def start_interactive_shell() -> None: @@ -21,7 +19,7 @@ def start_interactive_shell() -> None: "Running Mode:", choices=[ "Local", - REMOTE_DEPLOYMENT_LABEL, + "Remote Deployment", "Exit", ], ).ask() @@ -32,5 +30,5 @@ def start_interactive_shell() -> None: if choice == "Local": run_local_mode() - elif choice == REMOTE_DEPLOYMENT_LABEL: + elif choice == "Remote Deployment": run_remote_mode() diff --git a/src/sre_agent/cli/main.py b/src/sre_agent/cli/main.py index aa5ba976..fb95ddee 100644 --- a/src/sre_agent/cli/main.py +++ b/src/sre_agent/cli/main.py @@ -3,6 +3,7 @@ import click from sre_agent.cli.interactive_shell import start_interactive_shell +from sre_agent.cli.presentation.styles import apply_questionary_style @click.group(invoke_without_command=True) @@ -13,6 +14,7 @@ def cli(ctx: click.Context) -> None: Args: ctx: Click context for the command invocation. """ + apply_questionary_style() if ctx.invoked_subcommand is None: start_interactive_shell() diff --git a/src/sre_agent/cli/mode/local.py b/src/sre_agent/cli/mode/local.py index fc6a275c..8bf32cda 100644 --- a/src/sre_agent/cli/mode/local.py +++ b/src/sre_agent/cli/mode/local.py @@ -4,13 +4,12 @@ import shutil import subprocess # nosec B404 import sys -from pathlib import Path import questionary from rich.panel import Panel from sre_agent.cli.mode.paths import project_root -from sre_agent.cli.ui import console +from sre_agent.cli.presentation.console import console def run_local_mode() -> None: @@ -78,11 +77,6 @@ def _start_local_shell(log_group: str) -> None: log_group: CloudWatch log group name. """ _print_local_banner(log_group) - run_path = project_root() / "run.py" - if not run_path.exists(): - console.print("[red]run.py not found in project root.[/red]") - return - while True: try: command = input("sre-agent (local)> ") @@ -103,7 +97,7 @@ def _start_local_shell(log_group: str) -> None: continue if command.startswith("diagnose "): - _handle_diagnose_command(run_path, log_group, command) + _handle_diagnose_command(log_group, command) continue console.print("[yellow]Unknown command. Type 'help' for commands.[/yellow]") @@ -137,11 +131,10 @@ def _print_local_help() -> None: console.print("- exit") -def _handle_diagnose_command(run_path: Path, log_group: str, command: str) -> None: +def _handle_diagnose_command(log_group: str, command: str) -> None: """Parse and run a diagnose command. Args: - run_path: Path to the local run script. log_group: CloudWatch log group name. command: Raw command string. """ @@ -165,7 +158,8 @@ def _handle_diagnose_command(run_path: Path, log_group: str, command: str) -> No subprocess.run( [ sys.executable, - str(run_path), + "-m", + "sre_agent.run", log_group, service_name, str(minutes), diff --git a/src/sre_agent/cli/mode/remote.py b/src/sre_agent/cli/mode/remote.py deleted file mode 100644 index 1fd5a1ec..00000000 --- a/src/sre_agent/cli/mode/remote.py +++ /dev/null @@ -1,684 +0,0 @@ -"""Remote deployment mode for the CLI.""" - -from datetime import UTC, datetime -from typing import Any - -import questionary - -from sre_agent.cli.config import CliConfig, load_config, save_config -from sre_agent.cli.mode.paths import project_root -from sre_agent.cli.ui import console -from sre_agent.core.deployments.aws_ecs import ( - EcsDeploymentConfig, - ImageBuildConfig, - NetworkSelection, - build_and_push_images, - check_deployment, - cleanup_resources, - create_basic_vpc, - create_secret, - create_security_group, - create_session, - ensure_cluster, - ensure_repository, - ensure_roles, - ensure_service_linked_role, - get_secret_arn, - register_task_definition, - run_task, -) - - -def run_remote_mode() -> None: - """Run the remote deployment actions.""" - target = questionary.select( - "Remote Deployment:", - choices=[ - "Deploy to AWS ECS", - "Check deployment", - "Clean up deployment", - "Back", - ], - ).ask() - - if target in (None, "Back"): - return - - if target == "Deploy to AWS ECS": - _deploy_to_ecs() - elif target == "Check deployment": - _check_deployment() - elif target == "Clean up deployment": - _cleanup_menu() - - -def _deploy_to_ecs() -> None: - """Run the full ECS deployment flow.""" - config = load_config() - _print_deployment_summary(config) - confirm = questionary.confirm( - "Proceed with ECS deployment?", - default=True, - ).ask() - if not confirm: - console.print("[dim]Deployment cancelled.[/dim]") - return - - steps = [ - _run_network_step, - _run_security_group_step, - _run_secrets_step, - _run_iam_step, - _run_ecr_step, - _run_build_push_step, - _run_task_definition_step, - _run_cluster_step, - ] - updated = config - for step in steps: - ecs_config = _ecs_config_from_cli(updated) - next_config = step(updated, ecs_config) - if next_config is None: - return - updated = next_config - - ecs_config = _ecs_config_from_cli(updated) - _run_task_step(updated, ecs_config) - - -def _check_deployment() -> None: - """Check current deployment resources.""" - config = load_config() - ecs_config = _ecs_config_from_cli(config) - session = create_session(ecs_config) - console.print("[cyan]Checking current deployment...[/cyan]") - results = check_deployment(session, ecs_config) - for name, status in results.items(): - console.print(f"- {name}: {status}") - - -def _cleanup_menu() -> None: - """Clean up deployment resources.""" - console.print("[cyan]Clean up deployment resources[/cyan]") - console.print("[dim]This removes ECS resources created by the deployment flow.[/dim]") - - config = load_config() - ecs_config = _ecs_config_from_cli(config) - _print_cleanup_summary(config) - - confirm = questionary.confirm( - "This will delete the resources listed above. Continue?", - default=False, - ).ask() - if not confirm: - console.print("[dim]Clean up cancelled.[/dim]") - return - - force_delete = questionary.confirm( - "Delete secrets immediately (no recovery window)?", - default=False, - ).ask() - - cleanup_resources(ecs_config, _report_step, force_delete) - _reset_cleanup_state(config) - - -def _ecs_config_from_cli(config: CliConfig) -> EcsDeploymentConfig: - """Build an ECS deployment config from CLI config. - - Args: - config: CLI configuration values. - - Returns: - The ECS deployment configuration. - """ - return EcsDeploymentConfig( - aws_region=config.aws_region, - aws_profile=config.aws_profile, - project_name=config.project_name, - cluster_name=config.cluster_name, - task_family=config.task_family, - task_cpu=config.task_cpu, - task_memory=config.task_memory, - image_tag=config.image_tag, - vpc_id=config.vpc_id, - private_subnet_ids=config.private_subnet_ids, - security_group_id=config.security_group_id, - ecr_repo_sre_agent=config.ecr_repo_sre_agent, - ecr_repo_slack_mcp=config.ecr_repo_slack_mcp, - secret_anthropic_name=config.secret_anthropic_name, - secret_slack_bot_name=config.secret_slack_bot_name, - secret_github_token_name=config.secret_github_token_name, - secret_anthropic_arn=config.secret_anthropic_arn, - secret_slack_bot_arn=config.secret_slack_bot_arn, - secret_github_token_arn=config.secret_github_token_arn, - exec_role_arn=config.exec_role_arn, - task_role_arn=config.task_role_arn, - ecr_sre_agent_uri=config.ecr_sre_agent_uri, - ecr_slack_mcp_uri=config.ecr_slack_mcp_uri, - task_definition_arn=config.task_definition_arn, - cluster_arn=config.cluster_arn, - model=config.model, - slack_channel_id=config.slack_channel_id, - github_mcp_url=config.github_mcp_url, - log_group_name=config.log_group_name, - slack_mcp_host=config.slack_mcp_host, - slack_mcp_port=config.slack_mcp_port, - ) - - -def _run_network_step( - config: CliConfig, - ecs_config: EcsDeploymentConfig, -) -> CliConfig | None: - """Run the VPC selection step. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Starting ECS network setup...[/cyan]") - console.print("[dim]This will create a new VPC, private subnet, and NAT gateway.[/dim]") - session = create_session(ecs_config) - _report_step("Creating a new VPC with a private subnet and NAT gateway") - network = create_basic_vpc(session, ecs_config.project_name, _report_step) - return _update_config_with_network(config, network) - - -def _run_security_group_step( - config: CliConfig, - ecs_config: EcsDeploymentConfig, -) -> CliConfig | None: - """Create a security group. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - if not config.vpc_id: - console.print("[yellow]No VPC selected yet. Run network setup first.[/yellow]") - return None - - console.print("[cyan]Setting up security group...[/cyan]") - console.print("[dim]This will create a dedicated security group for ECS tasks.[/dim]") - session = create_session(ecs_config) - _report_step("Creating a new security group for ECS tasks") - suffix = datetime.now(UTC).strftime("%Y%m%d-%H%M%S") - name = f"{ecs_config.project_name}-tasks-{suffix}" - description = "Security group for SRE Agent ECS tasks" - group = create_security_group(session, config.vpc_id, name, description) - return _update_config_with_security_group(config, group) - - -def _run_secrets_step( - config: CliConfig, - ecs_config: EcsDeploymentConfig, -) -> CliConfig | None: - """Create Secrets Manager entries for API keys. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Setting up Secrets Manager...[/cyan]") - console.print("[dim]This stores API keys securely for ECS tasks.[/dim]") - session = create_session(ecs_config) - - anthropic_arn = _ensure_secret( - session, - config.secret_anthropic_name, - "Anthropic API key", - config.secret_anthropic_arn, - ) - if anthropic_arn is None: - return None - - slack_arn = _ensure_secret( - session, - config.secret_slack_bot_name, - "Slack bot token", - config.secret_slack_bot_arn, - ) - if slack_arn is None: - return None - - github_arn = _ensure_secret( - session, - config.secret_github_token_name, - "GitHub token", - config.secret_github_token_arn, - ) - if github_arn is None: - return None - - return _update_config_with_secrets(config, anthropic_arn, slack_arn, github_arn) - - -def _run_iam_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: - """Create IAM roles for ECS tasks. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Setting up IAM roles...[/cyan]") - console.print("[dim]This grants ECS tasks access to logs and secrets.[/dim]") - - secret_arns = [ - config.secret_anthropic_arn, - config.secret_slack_bot_arn, - config.secret_github_token_arn, - ] - if any(secret is None for secret in secret_arns): - console.print("[yellow]Secrets are missing. Run the secrets step first.[/yellow]") - return None - - session = create_session(ecs_config) - exec_role_arn, task_role_arn = ensure_roles( - session, - config.project_name, - config.aws_region, - [secret for secret in secret_arns if secret], - _report_step, - ) - return _update_config_with_roles(config, exec_role_arn, task_role_arn) - - -def _run_ecr_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: - """Create ECR repositories for images. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Setting up ECR repositories...[/cyan]") - console.print("[dim]This stores the container images for ECS.[/dim]") - session = create_session(ecs_config) - - _report_step("Ensuring sre-agent repository") - sre_agent_uri = ensure_repository(session, config.ecr_repo_sre_agent) - - _report_step("Ensuring Slack MCP repository") - slack_mcp_uri = ensure_repository(session, config.ecr_repo_slack_mcp) - - return _update_config_with_ecr(config, sre_agent_uri, slack_mcp_uri) - - -def _run_build_push_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: - """Build and push container images. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Building and pushing images...[/cyan]") - console.print("[dim]This builds the agent image and mirrors the Slack MCP image.[/dim]") - if not config.ecr_sre_agent_uri or not config.ecr_slack_mcp_uri: - console.print("[yellow]ECR repositories are missing. Run the ECR step first.[/yellow]") - return None - - session = create_session(ecs_config) - root_dir = project_root() - image_config = ImageBuildConfig( - sre_agent_uri=config.ecr_sre_agent_uri, - slack_mcp_uri=config.ecr_slack_mcp_uri, - image_tag=config.image_tag, - ) - build_and_push_images( - session, - root_dir, - image_config, - _report_step, - ) - return config - - -def _run_task_definition_step( - config: CliConfig, - ecs_config: EcsDeploymentConfig, -) -> CliConfig | None: - """Register the ECS task definition. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Registering ECS task definition...[/cyan]") - console.print("[dim]This defines how the ECS task runs the agent and Slack MCP.[/dim]") - if not config.slack_channel_id: - slack_channel_id = questionary.text("Slack channel ID:").ask() - if not slack_channel_id: - console.print("[yellow]Slack channel ID is required.[/yellow]") - return None - config.slack_channel_id = slack_channel_id - save_config(config) - - ecs_config = _ecs_config_from_cli(config) - session = create_session(ecs_config) - task_definition_arn = register_task_definition(session, ecs_config, _report_step) - return _update_config_with_task_definition(config, task_definition_arn) - - -def _run_cluster_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: - """Ensure the ECS cluster exists. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - - Returns: - The updated configuration, or None if cancelled. - """ - console.print("[cyan]Ensuring ECS cluster...[/cyan]") - console.print("[dim]This creates the ECS cluster if it does not exist.[/dim]") - session = create_session(ecs_config) - cluster_arn = ensure_cluster(session, config.cluster_name) - return _update_config_with_cluster(config, cluster_arn) - - -def _run_task_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> None: - """Run a one-off ECS task. - - Args: - config: CLI configuration values. - ecs_config: ECS deployment configuration. - """ - if not config.task_definition_arn: - console.print("[yellow]Task definition is missing. Register it first.[/yellow]") - return - if not config.private_subnet_ids or not config.security_group_id: - console.print("[yellow]Network configuration is missing.[/yellow]") - return - - confirm = questionary.confirm("Run a one-off ECS task now?", default=False).ask() - if not confirm: - console.print("[dim]Skipping task run.[/dim]") - return - - console.print("[cyan]Running ECS task...[/cyan]") - session = create_session(ecs_config) - ensure_service_linked_role(session, _report_step) - task_arn = run_task( - session, - config.cluster_name, - config.task_definition_arn, - config.private_subnet_ids, - config.security_group_id, - ) - console.print(f"[green]Task started: {task_arn}[/green]") - - -def _ensure_secret( - session: object, - name: str, - label: str, - existing_arn: str | None, -) -> str | None: - """Ensure a secret exists and return its ARN. - - Args: - session: Boto3 session wrapper for AWS calls. - name: Secret name to use. - label: Human-readable label for prompts. - existing_arn: Existing ARN if already stored. - - Returns: - The secret ARN, or None if creation failed. - """ - if existing_arn: - _report_step(f"Using saved secret ARN for {label}") - return existing_arn - - arn = get_secret_arn(session, name) - if arn: - _report_step(f"Found existing secret for {label}") - return arn - - value = questionary.password(f"Enter {label}:").ask() - if not value: - console.print("[yellow]Secret value is required.[/yellow]") - return None - - _report_step(f"Creating secret {name}") - return create_secret(session, name, value) - - -def _print_cleanup_summary(config: CliConfig) -> None: - """Print a summary of resources to be cleaned up. - - Args: - config: CLI configuration values. - """ - console.print("[bold]Resources to clean up:[/bold]") - console.print(f"- VPC: {config.vpc_id or 'not set'}") - console.print(f"- Private subnets: {', '.join(config.private_subnet_ids) or 'not set'}") - console.print(f"- Security group: {config.security_group_id or 'not set'}") - console.print(f"- ECS cluster: {config.cluster_name}") - console.print(f"- Task definition: {config.task_definition_arn or 'not set'}") - console.print(f"- ECR repos: {config.ecr_repo_sre_agent}, {config.ecr_repo_slack_mcp}") - console.print(f"- Log group: {config.log_group_name}") - secret_names = ", ".join( - [ - config.secret_anthropic_name, - config.secret_slack_bot_name, - config.secret_github_token_name, - ] - ) - console.print(f"- Secrets: {secret_names}") - iam_roles = f"{config.project_name}-task-execution, {config.project_name}-task" - console.print(f"- IAM roles: {iam_roles}") - - -def _print_deployment_summary(config: CliConfig) -> None: - """Print a summary of resources that will be created. - - Args: - config: CLI configuration values. - """ - console.print("[bold]Deployment plan:[/bold]") - console.print("- Create a new VPC with one public and one private subnet") - console.print("- Create an internet gateway, NAT gateway, and route tables") - console.print("- Create a dedicated security group for ECS tasks") - secret_names = ", ".join( - [ - config.secret_anthropic_name, - config.secret_slack_bot_name, - config.secret_github_token_name, - ] - ) - console.print(f"- Store secrets in Secrets Manager ({secret_names})") - iam_roles = f"{config.project_name}-task-execution and {config.project_name}-task" - console.print(f"- Create IAM roles: {iam_roles}") - ecr_repos = f"{config.ecr_repo_sre_agent}, {config.ecr_repo_slack_mcp}" - console.print(f"- Create ECR repositories: {ecr_repos}") - console.print("- Build and push container images") - console.print(f"- Register ECS task definition: {config.task_family}") - console.print(f"- Ensure ECS cluster: {config.cluster_name}") - console.print("- Optionally run a one-off ECS task") - - -def _reset_cleanup_state(config: CliConfig) -> None: - """Clear deployment state after clean up. - - Args: - config: CLI configuration values. - """ - config.vpc_id = None - config.private_subnet_ids = [] - config.security_group_id = None - config.secret_anthropic_arn = None - config.secret_slack_bot_arn = None - config.secret_github_token_arn = None - config.exec_role_arn = None - config.task_role_arn = None - config.ecr_sre_agent_uri = None - config.ecr_slack_mcp_uri = None - config.task_definition_arn = None - config.cluster_arn = None - - path = save_config(config) - console.print(f"[green]Cleared deployment state in {path}[/green]") - - -def _update_config_with_secrets( - config: CliConfig, - anthropic_arn: str, - slack_arn: str, - github_arn: str, -) -> CliConfig: - """Persist secret ARNs to config. - - Args: - config: CLI configuration values. - anthropic_arn: Anthropic secret ARN. - slack_arn: Slack bot token secret ARN. - github_arn: GitHub token secret ARN. - - Returns: - The updated configuration. - """ - config.secret_anthropic_arn = anthropic_arn - config.secret_slack_bot_arn = slack_arn - config.secret_github_token_arn = github_arn - path = save_config(config) - console.print(f"[green]Saved secrets configuration to {path}[/green]") - return config - - -def _update_config_with_roles( - config: CliConfig, - exec_role_arn: str, - task_role_arn: str, -) -> CliConfig: - """Persist role ARNs to config. - - Args: - config: CLI configuration values. - exec_role_arn: Execution role ARN. - task_role_arn: Task role ARN. - - Returns: - The updated configuration. - """ - config.exec_role_arn = exec_role_arn - config.task_role_arn = task_role_arn - path = save_config(config) - console.print(f"[green]Saved IAM role configuration to {path}[/green]") - return config - - -def _update_config_with_ecr( - config: CliConfig, - sre_agent_uri: str, - slack_mcp_uri: str, -) -> CliConfig: - """Persist ECR repository URIs to config. - - Args: - config: CLI configuration values. - sre_agent_uri: SRE agent repository URI. - slack_mcp_uri: Slack MCP repository URI. - - Returns: - The updated configuration. - """ - config.ecr_sre_agent_uri = sre_agent_uri - config.ecr_slack_mcp_uri = slack_mcp_uri - path = save_config(config) - console.print(f"[green]Saved ECR repository configuration to {path}[/green]") - return config - - -def _update_config_with_task_definition(config: CliConfig, task_definition_arn: str) -> CliConfig: - """Persist task definition ARN to config. - - Args: - config: CLI configuration values. - task_definition_arn: Task definition ARN. - - Returns: - The updated configuration. - """ - config.task_definition_arn = task_definition_arn - path = save_config(config) - console.print(f"[green]Saved task definition to {path}[/green]") - return config - - -def _update_config_with_cluster(config: CliConfig, cluster_arn: str) -> CliConfig: - """Persist cluster ARN to config. - - Args: - config: CLI configuration values. - cluster_arn: Cluster ARN. - - Returns: - The updated configuration. - """ - config.cluster_arn = cluster_arn - path = save_config(config) - console.print(f"[green]Saved cluster configuration to {path}[/green]") - return config - - -def _update_config_with_network(config: CliConfig, network: NetworkSelection) -> CliConfig: - """Persist network selection to config. - - Args: - config: CLI configuration values. - network: Selected network configuration. - - Returns: - The updated configuration. - """ - config.vpc_id = network.vpc_id - config.private_subnet_ids = network.private_subnet_ids - path = save_config(config) - console.print(f"[green]Saved network configuration to {path}[/green]") - return config - - -def _update_config_with_security_group(config: CliConfig, group: Any) -> CliConfig: - """Persist security group selection to config. - - Args: - config: CLI configuration values. - group: Security group result. - - Returns: - The updated configuration. - """ - config.security_group_id = group.group_id - path = save_config(config) - console.print(f"[green]Saved security group to {path}[/green]") - return config - - -def _report_step(message: str) -> None: - """Report deployment progress to the user. - - Args: - message: Progress message to display. - """ - console.print(f"[bold cyan]•[/bold cyan] {message}") diff --git a/src/sre_agent/cli/mode/remote/__init__.py b/src/sre_agent/cli/mode/remote/__init__.py new file mode 100644 index 00000000..2d8e2acc --- /dev/null +++ b/src/sre_agent/cli/mode/remote/__init__.py @@ -0,0 +1 @@ +"""Remote mode package.""" diff --git a/src/sre_agent/cli/mode/remote/aws/__init__.py b/src/sre_agent/cli/mode/remote/aws/__init__.py new file mode 100644 index 00000000..b855f10e --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/__init__.py @@ -0,0 +1 @@ +"""AWS remote deployment package.""" diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/__init__.py b/src/sre_agent/cli/mode/remote/aws/ecs/__init__.py new file mode 100644 index 00000000..a1986844 --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/__init__.py @@ -0,0 +1 @@ +"""AWS ECS remote deployment package.""" diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/errors.py b/src/sre_agent/cli/mode/remote/aws/ecs/errors.py new file mode 100644 index 00000000..2260f8bd --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/errors.py @@ -0,0 +1,98 @@ +"""AWS ECS remote deployment error helpers for the CLI.""" + +from botocore.exceptions import ( + ClientError, + EndpointConnectionError, + NoCredentialsError, + ProfileNotFound, +) + +from sre_agent.cli.presentation.console import console + + +def report_remote_error(exc: Exception) -> None: + """Render remote deployment errors with actionable guidance. + + Args: + exc: Raised exception from a remote deployment action. + """ + if is_aws_auth_error(exc): + console.print( + "[red]AWS authentication failed. Your credentials are missing, invalid, " + "or expired.[/red]" + ) + console.print( + "[dim]If using AWS profile/SSO, run: aws sso login --profile . " + "If using temporary keys, refresh AWS_SESSION_TOKEN and retry.[/dim]" + ) + return + + if is_aws_endpoint_error(exc): + console.print("[red]Could not reach AWS endpoint from this environment.[/red]") + console.print("[dim]Check network connectivity and AWS region configuration.[/dim]") + return + + console.print(f"[red]Remote deployment failed: {exc}[/red]") + + +def is_aws_auth_error(exc: Exception) -> bool: + """Return true when an exception chain indicates AWS auth issues. + + Args: + exc: Raised exception from a remote deployment action. + + Returns: + True when the chain contains an auth-related error. + """ + auth_codes = { + "ExpiredToken", + "ExpiredTokenException", + # spellchecker:ignore-next-line + "UnrecognizedClientException", + "InvalidClientTokenId", + "InvalidSignatureException", + "AccessDenied", + "AccessDeniedException", + } + for item in exception_chain(exc): + if isinstance(item, (NoCredentialsError, ProfileNotFound)): + return True + if isinstance(item, ClientError): + code = str(item.response.get("Error", {}).get("Code", "")) + if code in auth_codes: + return True + text = str(item) + if "security token included in the request is expired" in text.lower(): + return True + return False + + +def is_aws_endpoint_error(exc: Exception) -> bool: + """Return true when an exception chain indicates endpoint/network errors. + + Args: + exc: Raised exception from a remote deployment action. + + Returns: + True when the chain contains endpoint connection errors. + """ + return any(isinstance(item, EndpointConnectionError) for item in exception_chain(exc)) + + +def exception_chain(exc: BaseException) -> list[BaseException]: + """Return exceptions in cause/context chain. + + Args: + exc: Root exception. + + Returns: + Ordered exception chain from root to cause/context. + """ + chain: list[BaseException] = [] + seen: set[int] = set() + current: BaseException | None = exc + while current is not None and id(current) not in seen: + chain.append(current) + seen.add(id(current)) + current = current.__cause__ or current.__context__ + return chain diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/menu.py b/src/sre_agent/cli/mode/remote/aws/ecs/menu.py new file mode 100644 index 00000000..982a36f0 --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/menu.py @@ -0,0 +1,466 @@ +"""AWS ECS remote deployment mode for the CLI.""" + +from collections.abc import Callable + +import questionary + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.cli.configuration.store import load_config, save_config +from sre_agent.cli.mode.remote.aws.ecs.errors import report_remote_error +from sre_agent.cli.mode.remote.aws.ecs.metadata import ( + STATUS_KEY_ECR_REPOSITORIES, + STATUS_KEY_ECS_CLUSTER, + STATUS_KEY_IAM_ROLES, + STATUS_KEY_PRIVATE_SUBNETS, + STATUS_KEY_SECRETS, + STATUS_KEY_SECURITY_GROUP, + STATUS_KEY_TASK_DEFINITION, + STATUS_KEY_VPC, +) +from sre_agent.cli.mode.remote.aws.ecs.status import ( + collect_deployment_status, + is_status_present, + print_deployment_status_table, + should_block_deploy, +) +from sre_agent.cli.mode.remote.aws.ecs.steps import ( + build_container_overrides, + ecs_config_from_cli, + print_cleanup_summary, + print_deployment_summary, + prompt_diagnosis_inputs, + report_step, + reset_cleanup_state, + run_build_push_step, + run_cluster_step, + run_ecr_step, + run_iam_step, + run_network_step, + run_secrets_step, + run_security_group_step, + run_task_definition_step, + run_task_step, + start_one_off_task, + wait_for_task_completion, +) +from sre_agent.cli.presentation.console import console +from sre_agent.core.deployments.aws_ecs import ( + EcsDeploymentConfig, + cleanup_resources, + create_session, + get_identity, +) + +DeploymentStep = Callable[[CliConfig, EcsDeploymentConfig], CliConfig | None] + + +class _RepairCancelledError(Exception): + """Signal that the repair flow was cancelled.""" + + +def run_aws_ecs_mode() -> None: + """Run AWS ECS deployment actions.""" + while True: + target = questionary.select( + "AWS ECS:", + choices=_aws_ecs_menu_choices(load_config()), + ).ask() + + if target in (None, "Back"): + return + + action = _aws_ecs_menu_action(target) + if action is None: + continue + + try: + action() + except Exception as exc: # noqa: BLE001 + report_remote_error(exc) + + +def _aws_ecs_menu_choices(config: CliConfig) -> list[str]: + """Return AWS ECS menu choices for the current deployment state. + + Args: + config: CLI configuration values. + + Returns: + Menu options appropriate for current deployment state. + """ + choices = [ + "Deploy to AWS ECS", + "Check deployment status", + ] + if _has_completed_deployment(config): + choices.extend( + [ + "Run diagnosis job", + "Repair deployment", + "Clean up deployment", + ] + ) + choices.append("Back") + return choices + + +def _aws_ecs_menu_action(target: str) -> Callable[[], None] | None: + """Return the action callable for a menu selection. + + Args: + target: Menu option selected by the user. + + Returns: + Matching action callable, if supported. + """ + return { + "Deploy to AWS ECS": _deploy_to_ecs, + "Check deployment status": _check_deployment, + "Run diagnosis job": _run_diagnosis_job, + "Repair deployment": _repair_deployment, + "Clean up deployment": _cleanup_menu, + }.get(target) + + +def _has_completed_deployment(config: CliConfig) -> bool: + """Return true when config indicates an existing completed deployment. + + Args: + config: CLI configuration values. + + Returns: + True when core deployment state exists in config. + """ + return bool( + config.deployment.vpc_id + and config.deployment.private_subnet_ids + and config.deployment.security_group_id + and config.deployment.task_definition_arn + and config.deployment.cluster_arn + ) + + +def _deploy_to_ecs() -> None: + """Run the full ECS deployment flow.""" + config = load_config() + print_deployment_summary(config) + confirm = questionary.confirm( + "Proceed with ECS deployment?", + default=True, + ).ask() + if not confirm: + console.print("[dim]Deployment cancelled.[/dim]") + return + + _validate_aws_session(ecs_config_from_cli(config)) + status = collect_deployment_status(config) + if should_block_deploy(status): + console.print( + "[yellow]Deployment blocked because existing deployment resources " + "were detected.[/yellow]" + ) + print_deployment_status_table(config, status) + console.print( + "[dim]Use 'Repair deployment' to fix/reuse them, or 'Clean up deployment' first.[/dim]" + ) + return + + steps: list[DeploymentStep] = [ + run_network_step, + run_security_group_step, + run_secrets_step, + run_iam_step, + run_ecr_step, + run_build_push_step, + run_task_definition_step, + run_cluster_step, + ] + updated = config + for step in steps: + next_config = step(updated, ecs_config_from_cli(updated)) + if next_config is None: + return + updated = next_config + + run_task_step(updated, ecs_config_from_cli(updated)) + + +def _check_deployment() -> None: + """Check current deployment resources.""" + config = load_config() + console.print("[cyan]Checking current deployment (live AWS status scan)...[/cyan]") + results = collect_deployment_status(config) + print_deployment_status_table(config, results) + + +def _run_diagnosis_job() -> None: + """Run a temporary ECS task for one diagnosis job.""" + config = load_config() + ecs_config = ecs_config_from_cli(config) + + if not config.deployment.task_definition_arn: + console.print("[yellow]Task definition is missing. Deploy or repair first.[/yellow]") + return + if not config.deployment.private_subnet_ids or not config.deployment.security_group_id: + console.print("[yellow]Network configuration is missing. Deploy or repair first.[/yellow]") + return + + _validate_aws_session(ecs_config) + confirm = questionary.confirm("Run one-off diagnosis job now?", default=True).ask() + if not confirm: + console.print("[dim]Diagnosis job cancelled.[/dim]") + return + + inputs = prompt_diagnosis_inputs() + if inputs is None: + console.print("[dim]Diagnosis job cancelled.[/dim]") + return + + session, task_arn = start_one_off_task( + config, + ecs_config, + build_container_overrides(*inputs), + ) + wait_for_task_completion(session, config.ecs.cluster_name, task_arn) + + +def _repair_deployment() -> None: + """Repair missing or unhealthy deployment resources.""" + config = load_config() + console.print("[cyan]Repairing deployment using strict live status checks...[/cyan]") + + current_status = collect_deployment_status(config) + print_deployment_status_table(config, current_status) + + if all(is_status_present(status) for status in current_status.values()): + console.print("[green]No repair actions required. All resources are healthy.[/green]") + return + + confirm = questionary.confirm( + "Attempt automatic repair for missing/unhealthy resources?", + default=True, + ).ask() + if not confirm: + console.print("[dim]Repair cancelled.[/dim]") + return + + try: + updated = _run_repair_flow(config) + except _RepairCancelledError: + console.print("[dim]Repair cancelled.[/dim]") + return + + _report_repair_result(updated) + + +def _run_repair_flow(config: CliConfig) -> CliConfig: + """Run the ordered repair steps and return updated config. + + Args: + config: CLI configuration values. + + Returns: + Updated config after repair workflow. + """ + updated = config + task_definition_refresh_required = False + + updated, _ = _repair_network_if_needed(updated) + + for status_key, label, step, refresh_task_definition in _repair_steps(): + updated, repaired = _repair_resource_if_missing(updated, status_key, label, step) + if repaired and refresh_task_definition: + task_definition_refresh_required = True + + if _should_rebuild_images_during_repair(): + updated = _require_repair_step_result( + run_build_push_step(updated, ecs_config_from_cli(updated)) + ) + + updated, _ = _repair_task_definition_if_needed( + updated, + task_definition_refresh_required, + ) + updated, _ = _repair_resource_if_missing( + updated, + STATUS_KEY_ECS_CLUSTER, + "ECS cluster", + run_cluster_step, + ) + return updated + + +def _repair_steps() -> list[tuple[str, str, DeploymentStep, bool]]: + """Return ordered resource repair steps. + + Returns: + Ordered repair step metadata. + """ + return [ + (STATUS_KEY_SECURITY_GROUP, "security group", run_security_group_step, False), + (STATUS_KEY_SECRETS, "secrets", run_secrets_step, True), + (STATUS_KEY_IAM_ROLES, "IAM roles", run_iam_step, True), + (STATUS_KEY_ECR_REPOSITORIES, "ECR repositories", run_ecr_step, True), + ] + + +def _repair_network_if_needed(config: CliConfig) -> tuple[CliConfig, bool]: + """Repair VPC/subnets when missing. + + Args: + config: CLI configuration values. + + Returns: + Updated config and whether repair was performed. + """ + status = collect_deployment_status(config) + vpc_ok = is_status_present(status.get(STATUS_KEY_VPC, "")) + subnets_ok = is_status_present(status.get(STATUS_KEY_PRIVATE_SUBNETS, "")) + if vpc_ok and subnets_ok: + return config, False + + console.print("[cyan]Repairing network resources...[/cyan]") + updated = _require_repair_step_result(run_network_step(config, ecs_config_from_cli(config))) + if updated.deployment.security_group_id: + updated.deployment.security_group_id = None + save_config(updated) + report_step("Cleared saved security group. A new one will be created for the new VPC") + return updated, True + + +def _repair_resource_if_missing( + config: CliConfig, + status_key: str, + label: str, + step: DeploymentStep, +) -> tuple[CliConfig, bool]: + """Run a repair step when its status is not present. + + Args: + config: CLI configuration values. + status_key: Resource key in deployment status map. + label: Display label for progress messages. + step: Repair step callable. + + Returns: + Updated config and whether repair was performed. + """ + status = collect_deployment_status(config) + if is_status_present(status.get(status_key, "")): + return config, False + + console.print(f"[cyan]Repairing {label}...[/cyan]") + updated = _require_repair_step_result(step(config, ecs_config_from_cli(config))) + return updated, True + + +def _repair_task_definition_if_needed( + config: CliConfig, + refresh_required: bool, +) -> tuple[CliConfig, bool]: + """Repair task definition when missing or after dependency changes. + + Args: + config: CLI configuration values. + refresh_required: Whether dependencies changed and force refresh is needed. + + Returns: + Updated config and whether repair was performed. + """ + status = collect_deployment_status(config) + if not refresh_required and is_status_present(status.get(STATUS_KEY_TASK_DEFINITION, "")): + return config, False + + console.print("[cyan]Repairing task definition...[/cyan]") + updated = _require_repair_step_result( + run_task_definition_step(config, ecs_config_from_cli(config)) + ) + return updated, True + + +def _should_rebuild_images_during_repair() -> bool: + """Return true when the user wants image rebuild in repair. + + Returns: + True when image rebuild should be included. + """ + confirm = questionary.confirm( + "Build and push images as part of repair?", + default=False, + ).ask() + return bool(confirm) + + +def _require_repair_step_result(config: CliConfig | None) -> CliConfig: + """Return config from a repair step or raise cancellation. + + Args: + config: Optional config returned from a repair step. + + Returns: + Required config value. + + Raises: + _RepairCancelledError: If the step returned None. + """ + if config is None: + raise _RepairCancelledError() + return config + + +def _report_repair_result(config: CliConfig) -> None: + """Print final repair status and optional diagnosis run action. + + Args: + config: Updated CLI configuration values. + """ + final_status = collect_deployment_status(config) + console.print("[cyan]Deployment status after repair:[/cyan]") + print_deployment_status_table(config, final_status) + + if all(is_status_present(item) for item in final_status.values()): + console.print("[green]Repair complete.[/green]") + run_task_step(config, ecs_config_from_cli(config)) + return + console.print( + "[yellow]Repair finished with unresolved items. Review the status table.[/yellow]" + ) + + +def _cleanup_menu() -> None: + """Clean up deployment resources.""" + console.print("[cyan]Clean up deployment resources[/cyan]") + console.print("[dim]This removes ECS resources created by the deployment flow.[/dim]") + + config = load_config() + ecs_config = ecs_config_from_cli(config) + print_cleanup_summary(config) + + confirm = questionary.confirm( + "This will delete the resources listed above. Continue?", + default=False, + ).ask() + if not confirm: + console.print("[dim]Clean up cancelled.[/dim]") + return + + force_delete = questionary.confirm( + "Delete secrets immediately (no recovery window)?", + default=False, + ).ask() + + cleanup_resources(ecs_config, report_step, force_delete) + reset_cleanup_state(config) + + +def _validate_aws_session(config: EcsDeploymentConfig) -> None: + """Validate AWS session before running deployment actions. + + Args: + config: ECS deployment configuration. + """ + session = create_session(config) + identity = get_identity(session) + account = identity.get("Account", "unknown") + arn = identity.get("Arn", "unknown") + console.print(f"[dim]AWS identity: {arn} (account {account})[/dim]") diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/metadata.py b/src/sre_agent/cli/mode/remote/aws/ecs/metadata.py new file mode 100644 index 00000000..cf6b0498 --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/metadata.py @@ -0,0 +1,88 @@ +"""Shared metadata helpers for AWS ECS remote deployment flows.""" + +from sre_agent.cli.configuration.models import CliConfig + +STATUS_KEY_VPC = "VPC" +STATUS_KEY_PRIVATE_SUBNETS = "Private subnets" +STATUS_KEY_SECURITY_GROUP = "Security group" +STATUS_KEY_SECRETS = "Secrets" +STATUS_KEY_IAM_ROLES = "IAM roles" +STATUS_KEY_ECR_REPOSITORIES = "ECR repositories" +STATUS_KEY_LOG_GROUP = "Log group" +STATUS_KEY_TASK_DEFINITION = "Task definition" +STATUS_KEY_ECS_CLUSTER = "ECS cluster" + + +def secret_names(config: CliConfig) -> tuple[str, str, str]: + """Return configured secret names. + + Args: + config: CLI configuration values. + + Returns: + Secret names for Anthropic, Slack, and GitHub. + """ + return ( + config.ecs.secret_anthropic_name, + config.ecs.secret_slack_bot_name, + config.ecs.secret_github_token_name, + ) + + +def secret_arns(config: CliConfig) -> tuple[str | None, str | None, str | None]: + """Return configured secret ARNs. + + Args: + config: CLI configuration values. + + Returns: + Secret ARNs for Anthropic, Slack, and GitHub. + """ + return ( + config.deployment.secret_anthropic_arn, + config.deployment.secret_slack_bot_arn, + config.deployment.secret_github_token_arn, + ) + + +def joined_secret_names(config: CliConfig) -> str: + """Return configured secret names as a comma-separated string. + + Args: + config: CLI configuration values. + + Returns: + Comma-separated secret names. + """ + return ", ".join(secret_names(config)) + + +def default_iam_role_names(config: CliConfig) -> tuple[str, str]: + """Return default IAM role names for remote deployment. + + Args: + config: CLI configuration values. + + Returns: + Execution and task role names. + """ + return ( + f"{config.ecs.project_name}-task-execution", + f"{config.ecs.project_name}-task", + ) + + +def iam_role_targets(config: CliConfig) -> tuple[str, str]: + """Return IAM role display targets (ARN when available). + + Args: + config: CLI configuration values. + + Returns: + Execution and task role targets. + """ + default_execution, default_task = default_iam_role_names(config) + return ( + config.deployment.exec_role_arn or default_execution, + config.deployment.task_role_arn or default_task, + ) diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/status.py b/src/sre_agent/cli/mode/remote/aws/ecs/status.py new file mode 100644 index 00000000..9f993a2c --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/status.py @@ -0,0 +1,155 @@ +"""AWS ECS remote deployment status helpers for the CLI.""" + +from rich.table import Table + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.cli.mode.remote.aws.ecs.metadata import ( + STATUS_KEY_ECR_REPOSITORIES, + STATUS_KEY_ECS_CLUSTER, + STATUS_KEY_IAM_ROLES, + STATUS_KEY_LOG_GROUP, + STATUS_KEY_PRIVATE_SUBNETS, + STATUS_KEY_SECRETS, + STATUS_KEY_SECURITY_GROUP, + STATUS_KEY_TASK_DEFINITION, + STATUS_KEY_VPC, + iam_role_targets, + joined_secret_names, +) +from sre_agent.cli.mode.remote.aws.ecs.steps import ecs_config_from_cli +from sre_agent.cli.presentation.console import console +from sre_agent.core.deployments.aws_ecs import check_deployment, create_session + + +def collect_deployment_status(config: CliConfig) -> dict[str, str]: + """Collect live deployment status from AWS. + + Args: + config: CLI configuration values. + + Returns: + Deployment status values keyed by resource name. + """ + ecs_config = ecs_config_from_cli(config) + session = create_session(ecs_config) + return check_deployment(session, ecs_config) + + +def print_deployment_status_table(config: CliConfig, results: dict[str, str]) -> None: + """Print a deployment status table. + + Args: + config: CLI configuration values. + results: Deployment status values keyed by resource name. + """ + targets = deployment_resource_targets(config) + table = Table(title="Deployment resources", show_header=True, header_style="bold cyan") + table.add_column("Resource", style="white", no_wrap=True) + table.add_column("Name/ID", style="bright_white") + table.add_column("Status", style="white", no_wrap=True) + + for name, status in results.items(): + table.add_row(name, targets.get(name, "-"), style_status(status)) + + console.print(table) + + +def is_status_present(status: str) -> bool: + """Return true when a resource status is healthy/present. + + Args: + status: Resource status string. + + Returns: + True when the status indicates a present resource. + """ + return status.startswith("present") + + +def should_block_deploy(results: dict[str, str]) -> bool: + """Return true when deploy should be blocked to avoid duplicates. + + Args: + results: Deployment status values keyed by resource name. + + Returns: + True when existing resources or uncertain statuses are present. + """ + return any( + status_indicates_existing_resource(resource, status) for resource, status in results.items() + ) + + +def status_indicates_existing_resource(resource: str, status: str) -> bool: + """Return true when status implies existing/uncertain resources. + + Args: + resource: Resource name. + status: Resource status string. + + Returns: + True when deploy should treat the resource as existing or uncertain. + """ + if status == "not set": + return False + if status.startswith("missing"): + return False + if resource == STATUS_KEY_ECS_CLUSTER and status.strip().lower() == "status inactive": + return False + return ( + status.startswith("present") or status.startswith("status ") or status.startswith("error") + ) + + +def deployment_resource_targets(config: CliConfig) -> dict[str, str]: + """Return display names and IDs for deployment resources. + + Args: + config: CLI configuration values. + + Returns: + Display labels keyed by resource name. + """ + task_definition_name = config.ecs.task_family + if config.deployment.task_definition_arn: + task_definition_name = f"{config.ecs.task_family} ({config.deployment.task_definition_arn})" + + iam_targets = iam_role_targets(config) + ecr_targets = [config.deployment.ecr_sre_agent_uri or config.ecs.ecr_repo_sre_agent] + cluster_target = config.ecs.cluster_name + if config.deployment.cluster_arn: + cluster_target = f"{config.ecs.cluster_name} ({config.deployment.cluster_arn})" + + return { + STATUS_KEY_VPC: config.deployment.vpc_id or "not set", + STATUS_KEY_PRIVATE_SUBNETS: ", ".join(config.deployment.private_subnet_ids) or "not set", + STATUS_KEY_SECURITY_GROUP: config.deployment.security_group_id or "not set", + STATUS_KEY_SECRETS: joined_secret_names(config), + STATUS_KEY_IAM_ROLES: ", ".join(iam_targets), + STATUS_KEY_ECR_REPOSITORIES: ", ".join(ecr_targets), + STATUS_KEY_LOG_GROUP: config.ecs.log_group_name, + STATUS_KEY_TASK_DEFINITION: task_definition_name, + STATUS_KEY_ECS_CLUSTER: cluster_target, + } + + +def style_status(status: str) -> str: + """Return colourised status text for terminal output. + + Args: + status: Resource status string. + + Returns: + Rich-marked status text. + """ + if status.startswith("present"): + return f"[green]{status}[/green]" + if status == "not set": + return "[yellow]not set[/yellow]" + if status.startswith("missing"): + return f"[red]{status}[/red]" + if status.startswith("error"): + return f"[red]{status}[/red]" + if status.startswith("status "): + return f"[yellow]{status}[/yellow]" + return status diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/steps.py b/src/sre_agent/cli/mode/remote/aws/ecs/steps.py new file mode 100644 index 00000000..f9ad0d65 --- /dev/null +++ b/src/sre_agent/cli/mode/remote/aws/ecs/steps.py @@ -0,0 +1,689 @@ +"""AWS ECS remote deployment step helpers for the CLI.""" + +from datetime import UTC, datetime + +import questionary +from boto3.session import Session + +from sre_agent.cli.configuration.models import CliConfig +from sre_agent.cli.configuration.store import save_config +from sre_agent.cli.env import load_env_values +from sre_agent.cli.mode.paths import project_root +from sre_agent.cli.mode.remote.aws.ecs.metadata import ( + default_iam_role_names, + joined_secret_names, + secret_arns, +) +from sre_agent.cli.presentation.console import console +from sre_agent.core.deployments.aws_ecs import ( + EcsDeploymentConfig, + ImageBuildConfig, + NetworkSelection, + SecurityGroupInfo, + build_and_push_images, + create_basic_vpc, + create_secret, + create_security_group, + create_session, + ensure_cluster, + ensure_repository, + ensure_roles, + ensure_service_linked_role, + get_secret_info, + register_task_definition, + restore_secret, + run_task, +) +from sre_agent.core.deployments.aws_ecs import ( + wait_for_task_completion as wait_for_ecs_task_completion, +) + + +def ecs_config_from_cli(config: CliConfig) -> EcsDeploymentConfig: + """Build an ECS deployment config from CLI config. + + Args: + config: CLI configuration values. + + Returns: + The ECS deployment configuration. + """ + return EcsDeploymentConfig( + aws_region=config.aws.region, + aws_profile=config.aws.profile, + project_name=config.ecs.project_name, + cluster_name=config.ecs.cluster_name, + task_family=config.ecs.task_family, + task_cpu=config.ecs.task_cpu, + task_memory=config.ecs.task_memory, + task_cpu_architecture=config.ecs.task_cpu_architecture, + image_tag=config.ecs.image_tag, + vpc_id=config.deployment.vpc_id, + private_subnet_ids=config.deployment.private_subnet_ids, + security_group_id=config.deployment.security_group_id, + ecr_repo_sre_agent=config.ecs.ecr_repo_sre_agent, + ecr_repo_slack_mcp=config.ecs.ecr_repo_slack_mcp, + secret_anthropic_name=config.ecs.secret_anthropic_name, + secret_slack_bot_name=config.ecs.secret_slack_bot_name, + secret_github_token_name=config.ecs.secret_github_token_name, + secret_anthropic_arn=config.deployment.secret_anthropic_arn, + secret_slack_bot_arn=config.deployment.secret_slack_bot_arn, + secret_github_token_arn=config.deployment.secret_github_token_arn, + exec_role_arn=config.deployment.exec_role_arn, + task_role_arn=config.deployment.task_role_arn, + ecr_sre_agent_uri=config.deployment.ecr_sre_agent_uri, + task_definition_arn=config.deployment.task_definition_arn, + cluster_arn=config.deployment.cluster_arn, + model=config.integrations.model, + slack_channel_id=config.integrations.slack_channel_id, + github_mcp_url=config.integrations.github_mcp_url, + log_group_name=config.ecs.log_group_name, + slack_mcp_host=config.ecs.slack_mcp_host, + slack_mcp_port=config.ecs.slack_mcp_port, + ) + + +def run_network_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Run the VPC selection step. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Starting ECS network setup...[/cyan]") + console.print("[dim]This will create a new VPC, private subnet, and NAT gateway.[/dim]") + session = create_session(ecs_config) + report_step("Creating a new VPC with a private subnet and NAT gateway") + network = create_basic_vpc(session, ecs_config.project_name, report_step) + return _update_config_with_network(config, network) + + +def run_security_group_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create a security group. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + if not config.deployment.vpc_id: + console.print("[yellow]No VPC selected yet. Run network setup first.[/yellow]") + return None + + console.print("[cyan]Setting up security group...[/cyan]") + console.print("[dim]This will create a dedicated security group for ECS tasks.[/dim]") + session = create_session(ecs_config) + report_step("Creating a new security group for ECS tasks") + suffix = datetime.now(UTC).strftime("%Y%m%d-%H%M%S") + name = f"{ecs_config.project_name}-tasks-{suffix}" + description = "Security group for SRE Agent ECS tasks" + group = create_security_group(session, config.deployment.vpc_id, name, description) + return _update_config_with_security_group(config, group) + + +def run_secrets_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create Secrets Manager entries for API keys. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up Secrets Manager...[/cyan]") + console.print("[dim]This stores API keys securely for ECS tasks.[/dim]") + session = create_session(ecs_config) + env_values = load_env_values() + + anthropic_arn = _ensure_secret( + session, + config.ecs.secret_anthropic_name, + "Anthropic API key", + config.deployment.secret_anthropic_arn, + env_values.get("ANTHROPIC_API_KEY"), + ) + if anthropic_arn is None: + return None + + slack_arn = _ensure_secret( + session, + config.ecs.secret_slack_bot_name, + "Slack bot token", + config.deployment.secret_slack_bot_arn, + env_values.get("SLACK_BOT_TOKEN"), + ) + if slack_arn is None: + return None + + github_arn = _ensure_secret( + session, + config.ecs.secret_github_token_name, + "GitHub token", + config.deployment.secret_github_token_arn, + env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"), + ) + if github_arn is None: + return None + + return _update_config_with_secrets(config, anthropic_arn, slack_arn, github_arn) + + +def run_iam_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create IAM roles for ECS tasks. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up IAM roles...[/cyan]") + console.print("[dim]This grants ECS tasks access to logs and secrets.[/dim]") + + configured_secret_arns = secret_arns(config) + if any(secret is None for secret in configured_secret_arns): + console.print("[yellow]Secrets are missing. Run the secrets step first.[/yellow]") + return None + + session = create_session(ecs_config) + exec_role_arn, task_role_arn = ensure_roles( + session, + config.ecs.project_name, + config.aws.region, + [secret for secret in configured_secret_arns if secret], + report_step, + ) + return _update_config_with_roles(config, exec_role_arn, task_role_arn) + + +def run_ecr_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Create ECR repositories for images. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Setting up ECR repositories...[/cyan]") + console.print("[dim]This stores the sre-agent container image for ECS.[/dim]") + session = create_session(ecs_config) + + report_step("Ensuring sre-agent repository") + sre_agent_uri = ensure_repository(session, config.ecs.ecr_repo_sre_agent) + return _update_config_with_ecr(config, sre_agent_uri) + + +def run_build_push_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Build and push container images. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Building and pushing images...[/cyan]") + console.print("[dim]This builds the agent image and uses Slack MCP from GHCR.[/dim]") + if not config.deployment.ecr_sre_agent_uri: + console.print("[yellow]ECR repository is missing. Run the ECR step first.[/yellow]") + return None + + session = create_session(ecs_config) + image_config = ImageBuildConfig( + sre_agent_uri=config.deployment.ecr_sre_agent_uri, + image_tag=config.ecs.image_tag, + ) + task_cpu_architecture = build_and_push_images( + session, + project_root(), + image_config, + report_step, + ) + if config.ecs.task_cpu_architecture != task_cpu_architecture: + config.ecs.task_cpu_architecture = task_cpu_architecture + _save_config_and_report( + config, + f"Saved task CPU architecture ({task_cpu_architecture}) to {{path}}", + ) + return config + + +def run_task_definition_step( + config: CliConfig, _ecs_config: EcsDeploymentConfig +) -> CliConfig | None: + """Register the ECS task definition. + + Args: + config: CLI configuration values. + _ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Registering ECS task definition...[/cyan]") + console.print("[dim]This defines how the ECS task runs the agent and Slack MCP.[/dim]") + + existing_channel = config.integrations.slack_channel_id + slack_channel_id = (existing_channel or "").strip() + if not slack_channel_id: + user_input = questionary.text("Slack channel ID:").ask() + slack_channel_id = (user_input or "").strip() + if not slack_channel_id: + console.print("[yellow]Slack channel ID is required.[/yellow]") + return None + + if existing_channel != slack_channel_id: + config.integrations.slack_channel_id = slack_channel_id + _save_config_and_report(config, "Saved Slack channel ID to {path}") + + updated_ecs_config = ecs_config_from_cli(config) + session = create_session(updated_ecs_config) + task_definition_arn = register_task_definition(session, updated_ecs_config, report_step) + return _update_config_with_task_definition(config, task_definition_arn) + + +def run_cluster_step(config: CliConfig, ecs_config: EcsDeploymentConfig) -> CliConfig | None: + """Ensure the ECS cluster exists. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + + Returns: + The updated configuration, or None if cancelled. + """ + console.print("[cyan]Ensuring ECS cluster...[/cyan]") + console.print("[dim]This creates the ECS cluster if it does not exist.[/dim]") + session = create_session(ecs_config) + cluster_arn = ensure_cluster(session, config.ecs.cluster_name) + return _update_config_with_cluster(config, cluster_arn) + + +def run_task_step(config: CliConfig, _ecs_config: EcsDeploymentConfig) -> None: + """Show next-step guidance after deployment. + + Args: + config: CLI configuration values. + _ecs_config: ECS deployment configuration. + """ + if not config.deployment.task_definition_arn: + console.print("[yellow]Task definition is missing. Register it first.[/yellow]") + return + if not config.deployment.private_subnet_ids or not config.deployment.security_group_id: + console.print("[yellow]Network configuration is missing.[/yellow]") + return + + console.print( + "[dim]Deployment is ready. Use 'Run diagnosis job' to trigger a one-off run " + "when needed.[/dim]" + ) + + +def start_one_off_task( + config: CliConfig, + ecs_config: EcsDeploymentConfig, + container_overrides: list[dict[str, str | list[dict[str, str]]]] | None = None, +) -> tuple[Session, str]: + """Start a one-off ECS task. + + Args: + config: CLI configuration values. + ecs_config: ECS deployment configuration. + container_overrides: Optional ECS container overrides. + + Returns: + The active session and started task ARN. + """ + if not config.deployment.task_definition_arn or not config.deployment.security_group_id: + raise RuntimeError("Task definition and security group must be configured first.") + + console.print("[cyan]Running ECS task...[/cyan]") + session = create_session(ecs_config) + ensure_service_linked_role(session, report_step) + task_arn = run_task( + session, + ecs_config, + container_overrides, + ) + console.print(f"[green]Task started: {task_arn}[/green]") + return session, task_arn + + +def wait_for_task_completion(session: Session, cluster_name: str, task_arn: str) -> None: + """Wait for task completion and report outcome. + + Args: + session: Session wrapper used by AWS ECS helpers. + cluster_name: ECS cluster name. + task_arn: Running task ARN. + """ + console.print("[cyan]Waiting for diagnosis task to complete...[/cyan]") + completed, message = wait_for_ecs_task_completion(session, cluster_name, task_arn) + if completed: + console.print(f"[green]{message}[/green]") + return + console.print(f"[yellow]Diagnosis task failed: {message}[/yellow]") + + +def prompt_diagnosis_inputs() -> tuple[str, str, int] | None: + """Prompt for one-off diagnosis input values. + + Returns: + Cleaned service/log/time-range values, or None when cancelled/invalid. + """ + service_name = (questionary.text("Service name:").ask() or "").strip() + if not service_name: + console.print("[yellow]Service name is required.[/yellow]") + return None + + log_group = (questionary.text("CloudWatch log group:").ask() or "").strip() + if not log_group: + console.print("[yellow]CloudWatch log group is required.[/yellow]") + return None + + raw_minutes = (questionary.text("Time range minutes:", default="10").ask() or "").strip() + if not raw_minutes: + console.print("[yellow]Time range minutes is required.[/yellow]") + return None + try: + time_range_minutes = int(raw_minutes) + except ValueError: + console.print("[yellow]Time range minutes must be an integer.[/yellow]") + return None + if time_range_minutes <= 0: + console.print("[yellow]Time range minutes must be greater than 0.[/yellow]") + return None + return service_name, log_group, time_range_minutes + + +def build_container_overrides( + service_name: str, + log_group: str, + time_range_minutes: int, +) -> list[dict[str, str | list[dict[str, str]]]]: + """Build container overrides for a diagnosis job run. + + Args: + service_name: Target service name for diagnosis. + log_group: CloudWatch log group name. + time_range_minutes: Diagnosis window in minutes. + + Returns: + ECS container overrides payload. + """ + return [ + { + "name": "sre-agent", + "environment": [ + {"name": "SERVICE_NAME", "value": service_name}, + {"name": "LOG_GROUP", "value": log_group}, + {"name": "TIME_RANGE_MINUTES", "value": str(time_range_minutes)}, + ], + } + ] + + +def print_cleanup_summary(config: CliConfig) -> None: + """Print a summary of resources to be cleaned up. + + Args: + config: CLI configuration values. + """ + private_subnets = ", ".join(config.deployment.private_subnet_ids) or "not set" + console.print("[bold]Resources to clean up:[/bold]") + console.print(f"- VPC: {config.deployment.vpc_id or 'not set'}") + console.print(f"- Private subnets: {private_subnets}") + console.print(f"- Security group: {config.deployment.security_group_id or 'not set'}") + console.print(f"- ECS cluster: {config.ecs.cluster_name}") + console.print(f"- Task definition: {config.deployment.task_definition_arn or 'not set'}") + console.print(f"- ECR repo: {config.ecs.ecr_repo_sre_agent}") + console.print(f"- Legacy Slack ECR repo (if present): {config.ecs.ecr_repo_slack_mcp}") + console.print(f"- Log group: {config.ecs.log_group_name}") + console.print(f"- Secrets: {joined_secret_names(config)}") + iam_execution_role, iam_task_role = default_iam_role_names(config) + iam_roles = f"{iam_execution_role}, {iam_task_role}" + console.print(f"- IAM roles: {iam_roles}") + + +def print_deployment_summary(config: CliConfig) -> None: + """Print a summary of resources that will be created. + + Args: + config: CLI configuration values. + """ + console.print("[bold]Deployment plan:[/bold]") + console.print("- Create a new VPC with one public and one private subnet") + console.print("- Create an internet gateway, NAT gateway, and route tables") + console.print("- Create a dedicated security group for ECS tasks") + console.print(f"- Store secrets in Secrets Manager ({joined_secret_names(config)})") + iam_execution_role, iam_task_role = default_iam_role_names(config) + iam_roles = f"{iam_execution_role} and {iam_task_role}" + console.print(f"- Create IAM roles: {iam_roles}") + console.print(f"- Create ECR repository: {config.ecs.ecr_repo_sre_agent}") + console.print("- Build and push the sre-agent container image") + console.print("- Use Slack MCP image directly from GHCR") + console.print(f"- Register ECS task definition: {config.ecs.task_family}") + console.print(f"- Ensure ECS cluster: {config.ecs.cluster_name}") + console.print("- Optionally run a one-off diagnosis job") + + +def reset_cleanup_state(config: CliConfig) -> None: + """Clear deployment state after clean up. + + Args: + config: CLI configuration values. + """ + config.deployment.vpc_id = None + config.deployment.private_subnet_ids = [] + config.deployment.security_group_id = None + config.deployment.secret_anthropic_arn = None + config.deployment.secret_slack_bot_arn = None + config.deployment.secret_github_token_arn = None + config.deployment.exec_role_arn = None + config.deployment.task_role_arn = None + config.deployment.ecr_sre_agent_uri = None + config.deployment.task_definition_arn = None + config.deployment.cluster_arn = None + + _save_config_and_report(config, "Cleared deployment state in {path}") + + +def report_step(message: str) -> None: + """Report deployment progress to the user. + + Args: + message: Progress message to display. + """ + console.print(f"[bold cyan]•[/bold cyan] {message}") + + +def _ensure_secret( + session: Session, + name: str, + label: str, + existing_arn: str | None, + configured_value: str | None, +) -> str | None: + """Ensure a secret exists and return its ARN. + + Args: + session: Boto3 session wrapper for AWS calls. + name: Secret name to use. + label: Human-readable label for prompts. + existing_arn: Existing ARN if already stored. + configured_value: Value from local configuration. + + Returns: + The secret ARN, or None if creation failed. + """ + info = get_secret_info(session, name) + if info and info.scheduled_for_deletion: + report_step(f"Secret {name} is scheduled for deletion. Restoring it") + arn = restore_secret(session, name) + report_step(f"Restored secret for {label}") + return arn + + if info: + if existing_arn and existing_arn == info.arn: + report_step(f"Using saved secret ARN for {label}") + elif existing_arn and existing_arn != info.arn: + report_step(f"Saved secret ARN for {label} is stale. Using current secret") + else: + report_step(f"Found existing secret for {label}") + return info.arn + + if existing_arn: + report_step(f"Saved secret ARN for {label} was not found. Recreating secret") + + value = (configured_value or "").strip() + if value: + report_step(f"Creating secret {name} from configured {label}") + return create_secret(session, name, value) + + value = questionary.password(f"Enter {label}:").ask() + if not value: + console.print("[yellow]Secret value is required.[/yellow]") + return None + + report_step(f"Creating secret {name}") + return create_secret(session, name, value) + + +def _update_config_with_secrets( + config: CliConfig, + anthropic_arn: str, + slack_arn: str, + github_arn: str, +) -> CliConfig: + """Persist secret ARNs to config. + + Args: + config: CLI configuration values. + anthropic_arn: Anthropic secret ARN. + slack_arn: Slack bot token secret ARN. + github_arn: GitHub token secret ARN. + + Returns: + The updated configuration. + """ + config.deployment.secret_anthropic_arn = anthropic_arn + config.deployment.secret_slack_bot_arn = slack_arn + config.deployment.secret_github_token_arn = github_arn + return _save_config_and_report(config, "Saved secrets configuration to {path}") + + +def _update_config_with_roles( + config: CliConfig, + exec_role_arn: str, + task_role_arn: str, +) -> CliConfig: + """Persist role ARNs to config. + + Args: + config: CLI configuration values. + exec_role_arn: Execution role ARN. + task_role_arn: Task role ARN. + + Returns: + The updated configuration. + """ + config.deployment.exec_role_arn = exec_role_arn + config.deployment.task_role_arn = task_role_arn + return _save_config_and_report(config, "Saved IAM role configuration to {path}") + + +def _update_config_with_ecr(config: CliConfig, sre_agent_uri: str) -> CliConfig: + """Persist ECR repository URI to config. + + Args: + config: CLI configuration values. + sre_agent_uri: SRE agent repository URI. + + Returns: + The updated configuration. + """ + config.deployment.ecr_sre_agent_uri = sre_agent_uri + return _save_config_and_report(config, "Saved ECR repository configuration to {path}") + + +def _update_config_with_task_definition(config: CliConfig, task_definition_arn: str) -> CliConfig: + """Persist task definition ARN to config. + + Args: + config: CLI configuration values. + task_definition_arn: Task definition ARN. + + Returns: + The updated configuration. + """ + config.deployment.task_definition_arn = task_definition_arn + return _save_config_and_report(config, "Saved task definition to {path}") + + +def _update_config_with_cluster(config: CliConfig, cluster_arn: str) -> CliConfig: + """Persist cluster ARN to config. + + Args: + config: CLI configuration values. + cluster_arn: Cluster ARN. + + Returns: + The updated configuration. + """ + config.deployment.cluster_arn = cluster_arn + return _save_config_and_report(config, "Saved cluster configuration to {path}") + + +def _update_config_with_network(config: CliConfig, network: NetworkSelection) -> CliConfig: + """Persist network selection to config. + + Args: + config: CLI configuration values. + network: Selected network configuration. + + Returns: + The updated configuration. + """ + config.deployment.vpc_id = network.vpc_id + config.deployment.private_subnet_ids = network.private_subnet_ids + return _save_config_and_report(config, "Saved network configuration to {path}") + + +def _update_config_with_security_group(config: CliConfig, group: SecurityGroupInfo) -> CliConfig: + """Persist security group selection to config. + + Args: + config: CLI configuration values. + group: Security group result. + + Returns: + The updated configuration. + """ + config.deployment.security_group_id = group.group_id + return _save_config_and_report(config, "Saved security group to {path}") + + +def _save_config_and_report(config: CliConfig, message_template: str) -> CliConfig: + """Save CLI config and print a formatted success message. + + Args: + config: CLI configuration values. + message_template: Message template containing `{path}` placeholder. + + Returns: + The saved configuration. + """ + path = save_config(config) + message = message_template.format(path=path) + console.print(f"[green]{message}[/green]") + return config diff --git a/src/sre_agent/cli/mode/remote/menu.py b/src/sre_agent/cli/mode/remote/menu.py new file mode 100644 index 00000000..e9e6af27 --- /dev/null +++ b/src/sre_agent/cli/mode/remote/menu.py @@ -0,0 +1,21 @@ +"""Remote deployment mode for the CLI.""" + +import questionary + +from sre_agent.cli.mode.remote.aws.ecs.menu import run_aws_ecs_mode + + +def run_remote_mode() -> None: + """Run the remote deployment actions.""" + target = questionary.select( + "Remote Deployment:", + choices=[ + "AWS ECS", + "Back", + ], + ).ask() + + if target in (None, "Back"): + return + if target == "AWS ECS": + run_aws_ecs_mode() diff --git a/src/sre_agent/cli/presentation/__init__.py b/src/sre_agent/cli/presentation/__init__.py new file mode 100644 index 00000000..1b758658 --- /dev/null +++ b/src/sre_agent/cli/presentation/__init__.py @@ -0,0 +1 @@ +"""CLI presentation helpers.""" diff --git a/src/sre_agent/cli/ascii_art.py b/src/sre_agent/cli/presentation/ascii_art.py similarity index 100% rename from src/sre_agent/cli/ascii_art.py rename to src/sre_agent/cli/presentation/ascii_art.py diff --git a/src/sre_agent/cli/banner.py b/src/sre_agent/cli/presentation/banner.py similarity index 73% rename from src/sre_agent/cli/banner.py rename to src/sre_agent/cli/presentation/banner.py index 24457374..0d93da75 100644 --- a/src/sre_agent/cli/banner.py +++ b/src/sre_agent/cli/presentation/banner.py @@ -2,11 +2,12 @@ from importlib.metadata import PackageNotFoundError, version +from rich.console import Group from rich.panel import Panel from rich.text import Text -from sre_agent.cli.ascii_art import get_ascii_art -from sre_agent.cli.ui import console +from sre_agent.cli.presentation.ascii_art import get_ascii_art +from sre_agent.cli.presentation.console import console def print_global_banner() -> None: @@ -26,12 +27,17 @@ def print_global_banner() -> None: style="bright_white", ) banner_text.append("Diagnose • Monitor • Debug • Scale\n", style="dim white") - banner_text.append(f"v{_get_version()}\n", style="orange1") + banner_text.append("\n") + + footer_text = Text(justify="right") + footer_text.append(f"v{_get_version()}\n", style="orange1") + footer_text.append("Made by Fuzzy Labs", style="dim white") console.print( Panel( - banner_text, + Group(banner_text, footer_text), title="Welcome to SRE Agent", border_style="cyan", + expand=True, ) ) diff --git a/src/sre_agent/cli/ui.py b/src/sre_agent/cli/presentation/console.py similarity index 100% rename from src/sre_agent/cli/ui.py rename to src/sre_agent/cli/presentation/console.py diff --git a/src/sre_agent/cli/presentation/styles.py b/src/sre_agent/cli/presentation/styles.py new file mode 100644 index 00000000..3376dcb6 --- /dev/null +++ b/src/sre_agent/cli/presentation/styles.py @@ -0,0 +1,28 @@ +"""Questionary styles for the CLI.""" + +import questionary +import questionary.constants as questionary_constants +import questionary.styles as questionary_styles + +QUESTIONARY_STYLE = questionary.Style( + [ + ("qmark", "fg:#5f819d"), + ("question", "fg:#e0e0e0 bold"), + ("answer", "fg:#FF9D00 bold"), + ("search_success", "noinherit fg:#00FF00 bold"), + ("search_none", "noinherit fg:#FF0000 bold"), + ("pointer", "fg:#e0e0e0"), + ("highlighted", "fg:#f2f2f2"), + ("selected", "fg:#e0e0e0"), + ("separator", "fg:#e0e0e0"), + ("instruction", "fg:#e0e0e0"), + ("text", "fg:#e0e0e0"), + ("disabled", "fg:#bdbdbd italic"), + ] +) + + +def apply_questionary_style() -> None: + """Apply the default Questionary style for CLI prompts.""" + questionary_constants.DEFAULT_STYLE = QUESTIONARY_STYLE + setattr(questionary_styles, "DEFAULT_STYLE", QUESTIONARY_STYLE) diff --git a/src/sre_agent/config/__init__.py b/src/sre_agent/config/__init__.py new file mode 100644 index 00000000..a5d9d82f --- /dev/null +++ b/src/sre_agent/config/__init__.py @@ -0,0 +1 @@ +"""Shared configuration helpers.""" diff --git a/src/sre_agent/config/paths.py b/src/sre_agent/config/paths.py new file mode 100644 index 00000000..4ba4f21a --- /dev/null +++ b/src/sre_agent/config/paths.py @@ -0,0 +1,36 @@ +"""Shared filesystem paths for user configuration.""" + +from pathlib import Path + +from platformdirs import user_config_dir + +APP_NAME = "sre-agent" +CLI_CONFIG_FILENAME = "config.json" +ENV_FILENAME = ".env" + + +def config_dir() -> Path: + """Return the user configuration directory. + + Returns: + The user configuration directory path. + """ + return Path(user_config_dir(APP_NAME)) + + +def cli_config_path() -> Path: + """Return the CLI configuration file path. + + Returns: + The CLI configuration file path. + """ + return config_dir() / CLI_CONFIG_FILENAME + + +def env_path() -> Path: + """Return the user env file path. + + Returns: + The user env file path. + """ + return config_dir() / ENV_FILENAME diff --git a/src/sre_agent/core/__init__.py b/src/sre_agent/core/__init__.py index e75ff9b1..3534df48 100644 --- a/src/sre_agent/core/__init__.py +++ b/src/sre_agent/core/__init__.py @@ -1,14 +1,14 @@ """SRE Agent core modules.""" from sre_agent.core.agent import create_sre_agent, diagnose_error -from sre_agent.core.config import AgentConfig, get_config from sre_agent.core.models import ErrorDiagnosis, LogEntry, LogQueryResult +from sre_agent.core.settings import AgentSettings, get_settings __all__ = [ "create_sre_agent", "diagnose_error", - "AgentConfig", - "get_config", + "AgentSettings", + "get_settings", "ErrorDiagnosis", "LogEntry", "LogQueryResult", diff --git a/src/sre_agent/core/agent.py b/src/sre_agent/core/agent.py index 91fca431..78df3cf4 100644 --- a/src/sre_agent/core/agent.py +++ b/src/sre_agent/core/agent.py @@ -2,9 +2,9 @@ from pydantic_ai import Agent -from sre_agent.core.config import AgentConfig, get_config from sre_agent.core.models import ErrorDiagnosis from sre_agent.core.prompts import SYSTEM_PROMPT, build_diagnosis_prompt +from sre_agent.core.settings import AgentSettings, get_settings from sre_agent.core.tools import ( create_cloudwatch_toolset, create_github_mcp_toolset, @@ -12,17 +12,17 @@ ) -def create_sre_agent(config: AgentConfig | None = None) -> Agent[None, ErrorDiagnosis]: +def create_sre_agent(config: AgentSettings | None = None) -> Agent[None, ErrorDiagnosis]: """Create the SRE Agent with all toolsets configured. Args: - config: Optional AgentConfig. If not provided, loads from environment. + config: Optional AgentSettings. If not provided, loads from environment. Returns: Configured pydantic-ai Agent with structured output. """ if config is None: - config = get_config() + config = get_settings() toolsets = [ create_cloudwatch_toolset(config), @@ -42,7 +42,7 @@ async def diagnose_error( log_group: str, service_name: str, time_range_minutes: int = 10, - config: AgentConfig | None = None, + config: AgentSettings | None = None, ) -> ErrorDiagnosis: """Run a diagnosis for errors in a specific log group. @@ -56,7 +56,7 @@ async def diagnose_error( ErrorDiagnosis with findings and suggested fixes. """ if config is None: - config = get_config() + config = get_settings() agent = create_sre_agent(config) prompt = build_diagnosis_prompt(config, log_group, service_name, time_range_minutes) diff --git a/src/sre_agent/core/api.py b/src/sre_agent/core/api.py deleted file mode 100644 index b2bbb692..00000000 --- a/src/sre_agent/core/api.py +++ /dev/null @@ -1,87 +0,0 @@ -"""FastAPI HTTP service for the SRE Agent.""" - -import logging -from collections.abc import AsyncIterator -from contextlib import asynccontextmanager - -from fastapi import FastAPI, HTTPException -from pydantic import BaseModel, Field - -from sre_agent.core.agent import diagnose_error -from sre_agent.core.config import get_config -from sre_agent.core.models import ErrorDiagnosis - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -class DiagnoseRequest(BaseModel): - """Request body for the diagnose endpoint.""" - - log_group: str = Field(description="CloudWatch log group to analyse") - service_name: str = Field(description="Service to that triggers the error") - time_range_minutes: int = Field(default=10, ge=1, le=1440, description="Time range in minutes") - - -class HealthResponse(BaseModel): - """Health check response.""" - - status: str = "ok" - version: str = "0.2.0" - - -@asynccontextmanager -async def lifespan(app: FastAPI) -> AsyncIterator[None]: - """Application lifespan handler.""" - # Validate config on startup - try: - config = get_config() - logger.info(f"SRE Agent starting with model: {config.model}") - logger.info(f"AWS Region: {config.aws.region}") - logger.info(f"Slack MCP URL: {config.slack.mcp_url}") - logger.info(f"GitHub MCP URL: {config.github.mcp_url}") - except Exception as e: - logger.error(f"Configuration error: {e}") - raise - - yield - - logger.info("SRE Agent shutting down") - - -app = FastAPI( - title="SRE Agent API", - description="AI-powered Site Reliability Engineering agent for error diagnosis", - version="0.2.0", - lifespan=lifespan, -) - - -@app.get("/health", response_model=HealthResponse) -async def health_check() -> HealthResponse: - """Health check endpoint for liveness/readiness probes.""" - return HealthResponse() - - -@app.post("/diagnose", response_model=ErrorDiagnosis) -async def diagnose(request: DiagnoseRequest) -> ErrorDiagnosis: - """Diagnose errors in a CloudWatch log group. - - The agent will: - 1. Create a Slack thread to report the investigation - 2. Query CloudWatch for error logs - 3. Search GitHub for relevant code (if errors found) - 4. Reply to the Slack thread with findings - """ - logger.info(f"Diagnosing errors in {request.log_group}") - - try: - result = await diagnose_error( - log_group=request.log_group, - service_name=request.service_name, - time_range_minutes=request.time_range_minutes, - ) - return result - except Exception as e: - logger.exception(f"Diagnosis failed: {e}") - raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/sre_agent/core/deployments/__init__.py b/src/sre_agent/core/deployments/__init__.py index ad79a29d..9a31d0c5 100644 --- a/src/sre_agent/core/deployments/__init__.py +++ b/src/sre_agent/core/deployments/__init__.py @@ -5,8 +5,6 @@ ImageBuildConfig, NetworkSelection, SecurityGroupInfo, - SubnetInfo, - VpcInfo, build_and_push_images, check_deployment, cleanup_resources, @@ -14,16 +12,11 @@ create_secret, create_security_group, create_session, - deploy_agent, ensure_cluster, ensure_repository, ensure_roles, ensure_service_linked_role, get_identity, - get_secret_arn, - list_private_subnets, - list_security_groups, - list_vpcs, register_task_definition, run_task, ) @@ -33,8 +26,6 @@ "ImageBuildConfig", "NetworkSelection", "SecurityGroupInfo", - "SubnetInfo", - "VpcInfo", "build_and_push_images", "check_deployment", "cleanup_resources", @@ -42,16 +33,11 @@ "create_security_group", "create_secret", "create_session", - "deploy_agent", "ensure_cluster", "ensure_repository", "ensure_roles", "ensure_service_linked_role", "get_identity", - "get_secret_arn", - "list_private_subnets", - "list_security_groups", - "list_vpcs", "register_task_definition", "run_task", ] diff --git a/src/sre_agent/core/deployments/aws_ecs/__init__.py b/src/sre_agent/core/deployments/aws_ecs/__init__.py index d4bb1571..fc6227d2 100644 --- a/src/sre_agent/core/deployments/aws_ecs/__init__.py +++ b/src/sre_agent/core/deployments/aws_ecs/__init__.py @@ -1,12 +1,12 @@ """AWS ECS deployment helpers.""" from sre_agent.core.deployments.aws_ecs.cleanup import cleanup_resources -from sre_agent.core.deployments.aws_ecs.deploy import deploy_agent from sre_agent.core.deployments.aws_ecs.ecr import ensure_repository from sre_agent.core.deployments.aws_ecs.ecs_tasks import ( ensure_cluster, register_task_definition, run_task, + wait_for_task_completion, ) from sre_agent.core.deployments.aws_ecs.iam import ensure_roles, ensure_service_linked_role from sre_agent.core.deployments.aws_ecs.images import ImageBuildConfig, build_and_push_images @@ -14,19 +14,15 @@ EcsDeploymentConfig, NetworkSelection, SecurityGroupInfo, - SubnetInfo, - VpcInfo, ) -from sre_agent.core.deployments.aws_ecs.network import ( - create_basic_vpc, - list_private_subnets, - list_vpcs, -) -from sre_agent.core.deployments.aws_ecs.secrets import create_secret, get_secret_arn -from sre_agent.core.deployments.aws_ecs.security_groups import ( - create_security_group, - list_security_groups, +from sre_agent.core.deployments.aws_ecs.network import create_basic_vpc +from sre_agent.core.deployments.aws_ecs.secrets import ( + SecretInfo, + create_secret, + get_secret_info, + restore_secret, ) +from sre_agent.core.deployments.aws_ecs.security_groups import create_security_group from sre_agent.core.deployments.aws_ecs.session import create_session, get_identity from sre_agent.core.deployments.aws_ecs.status import check_deployment @@ -35,8 +31,6 @@ "NetworkSelection", "ImageBuildConfig", "SecurityGroupInfo", - "SubnetInfo", - "VpcInfo", "build_and_push_images", "cleanup_resources", "check_deployment", @@ -44,16 +38,15 @@ "create_security_group", "create_secret", "create_session", - "deploy_agent", "ensure_cluster", "ensure_repository", "ensure_roles", "ensure_service_linked_role", "get_identity", - "get_secret_arn", - "list_private_subnets", - "list_security_groups", - "list_vpcs", + "get_secret_info", + "restore_secret", "register_task_definition", "run_task", + "wait_for_task_completion", + "SecretInfo", ] diff --git a/src/sre_agent/core/deployments/aws_ecs/cleanup.py b/src/sre_agent/core/deployments/aws_ecs/cleanup.py index 4685706d..0f82683b 100644 --- a/src/sre_agent/core/deployments/aws_ecs/cleanup.py +++ b/src/sre_agent/core/deployments/aws_ecs/cleanup.py @@ -4,6 +4,7 @@ from collections.abc import Callable from typing import Any +from boto3.session import Session from botocore.exceptions import ClientError from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig @@ -31,9 +32,8 @@ def cleanup_resources( reporter("Deleting CloudWatch log group") _delete_log_group(session, config.log_group_name, reporter) - reporter("Deleting ECR repositories (if they exist)") + reporter("Deleting ECR repository (if it exists)") _delete_ecr_repo(session, config.ecr_repo_sre_agent, reporter) - _delete_ecr_repo(session, config.ecr_repo_slack_mcp, reporter) reporter("Deleting IAM roles (if they exist)") _delete_roles(session, config, reporter) @@ -48,7 +48,7 @@ def cleanup_resources( _cleanup_vpc(session, config.vpc_id, reporter) -def _stop_tasks(session: Any, cluster_name: str, reporter: Callable[[str], None]) -> None: +def _stop_tasks(session: Session, cluster_name: str, reporter: Callable[[str], None]) -> None: """Stop running ECS tasks in the cluster.""" ecs = session.client("ecs") try: @@ -69,7 +69,7 @@ def _stop_tasks(session: Any, cluster_name: str, reporter: Callable[[str], None] def _deregister_task_definition( - session: Any, + session: Session, task_definition_arn: str, reporter: Callable[[str], None], ) -> None: @@ -81,7 +81,7 @@ def _deregister_task_definition( reporter(f"Failed to deregister task definition: {exc}") -def _delete_cluster(session: Any, cluster_name: str, reporter: Callable[[str], None]) -> None: +def _delete_cluster(session: Session, cluster_name: str, reporter: Callable[[str], None]) -> None: """Delete an ECS cluster if it exists.""" ecs = session.client("ecs") try: @@ -93,7 +93,11 @@ def _delete_cluster(session: Any, cluster_name: str, reporter: Callable[[str], N reporter(f"Failed to delete cluster: {exc}") -def _delete_log_group(session: Any, log_group_name: str, reporter: Callable[[str], None]) -> None: +def _delete_log_group( + session: Session, + log_group_name: str, + reporter: Callable[[str], None], +) -> None: """Delete a CloudWatch log group.""" logs = session.client("logs") try: @@ -104,7 +108,7 @@ def _delete_log_group(session: Any, log_group_name: str, reporter: Callable[[str reporter(f"Failed to delete log group: {exc}") -def _delete_ecr_repo(session: Any, name: str, reporter: Callable[[str], None]) -> None: +def _delete_ecr_repo(session: Session, name: str, reporter: Callable[[str], None]) -> None: """Delete an ECR repository if it exists.""" if not name: return @@ -118,7 +122,7 @@ def _delete_ecr_repo(session: Any, name: str, reporter: Callable[[str], None]) - def _delete_roles( - session: Any, + session: Session, config: EcsDeploymentConfig, reporter: Callable[[str], None], ) -> None: @@ -187,7 +191,7 @@ def _delete_inline_policies(iam: Any, role_name: str, reporter: Callable[[str], def _delete_secret( - session: Any, + session: Session, name: str, force_delete: bool, reporter: Callable[[str], None], @@ -207,7 +211,7 @@ def _delete_secret( reporter(f"Failed to delete secret {name}: {exc}") -def _cleanup_vpc(session: Any, vpc_id: str, reporter: Callable[[str], None]) -> None: +def _cleanup_vpc(session: Session, vpc_id: str, reporter: Callable[[str], None]) -> None: """Delete a VPC and its dependent resources.""" ec2 = session.client("ec2") diff --git a/src/sre_agent/core/deployments/aws_ecs/deploy.py b/src/sre_agent/core/deployments/aws_ecs/deploy.py deleted file mode 100644 index 07246747..00000000 --- a/src/sre_agent/core/deployments/aws_ecs/deploy.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Deployment entrypoint for ECS.""" - -from collections.abc import Callable - -from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig -from sre_agent.core.deployments.aws_ecs.session import create_session, get_identity - - -def deploy_agent(config: EcsDeploymentConfig, reporter: Callable[[str], None]) -> None: - """Deploy the SRE Agent to ECS.""" - reporter("Checking AWS credentials") - session = create_session(config) - identity = get_identity(session) - reporter(f"Using AWS account {identity['Account']} ({identity['Arn']})") - - reporter("Deployment steps are not implemented yet.") diff --git a/src/sre_agent/core/deployments/aws_ecs/ecr.py b/src/sre_agent/core/deployments/aws_ecs/ecr.py index 6d3ec1fe..b5bf1cdd 100644 --- a/src/sre_agent/core/deployments/aws_ecs/ecr.py +++ b/src/sre_agent/core/deployments/aws_ecs/ecr.py @@ -1,11 +1,12 @@ """ECR helpers for ECS deployment.""" -from typing import Any, cast +from typing import cast +from boto3.session import Session from botocore.exceptions import ClientError -def ensure_repository(session: Any, name: str) -> str: +def ensure_repository(session: Session, name: str) -> str: """Ensure an ECR repository exists and return its URI.""" ecr = session.client("ecr") try: diff --git a/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py index 0c0f29e5..f9329d19 100644 --- a/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py +++ b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py @@ -1,14 +1,19 @@ """ECS task and cluster helpers.""" +import time from collections.abc import Callable from typing import Any, cast +from boto3.session import Session from botocore.exceptions import ClientError from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig +SRE_AGENT_CONTAINER_NAME = "sre-agent" +SLACK_MCP_IMAGE = "ghcr.io/korotovsky/slack-mcp-server:latest" -def ensure_log_group(session: Any, log_group_name: str) -> None: + +def ensure_log_group(session: Session, log_group_name: str) -> None: """Ensure a CloudWatch log group exists.""" logs = session.client("logs") try: @@ -20,17 +25,16 @@ def ensure_log_group(session: Any, log_group_name: str) -> None: def register_task_definition( - session: Any, + session: Session, config: EcsDeploymentConfig, reporter: Callable[[str], None], ) -> str: """Register the ECS task definition.""" + cpu_architecture = _normalise_cpu_architecture(config.task_cpu_architecture) if not config.exec_role_arn or not config.task_role_arn: raise RuntimeError("Task roles must be created before registering the task definition.") - if not config.ecr_sre_agent_uri or not config.ecr_slack_mcp_uri: - raise RuntimeError( - "ECR repositories must be created before registering the task definition." - ) + if not config.ecr_sre_agent_uri: + raise RuntimeError("ECR repository for sre-agent must be created first.") if ( not config.secret_anthropic_arn or not config.secret_github_token_arn @@ -48,10 +52,9 @@ def register_task_definition( container_definitions = [ { - "name": "sre-agent", + "name": SRE_AGENT_CONTAINER_NAME, "image": f"{config.ecr_sre_agent_uri}:{config.image_tag}", "essential": True, - "portMappings": [{"containerPort": 8000, "protocol": "tcp"}], "environment": [ {"name": "AWS_REGION", "value": config.aws_region}, {"name": "MODEL", "value": config.model}, @@ -81,9 +84,8 @@ def register_task_definition( }, { "name": "slack", - "image": f"{config.ecr_slack_mcp_uri}:{config.image_tag}", + "image": SLACK_MCP_IMAGE, "essential": True, - "portMappings": [{"containerPort": config.slack_mcp_port, "protocol": "tcp"}], "environment": [ {"name": "SLACK_MCP_ADD_MESSAGE_TOOL", "value": config.slack_channel_id}, {"name": "SLACK_MCP_HOST", "value": config.slack_mcp_host}, @@ -107,6 +109,10 @@ def register_task_definition( family=config.task_family, networkMode="awsvpc", requiresCompatibilities=["FARGATE"], + runtimePlatform={ + "cpuArchitecture": cpu_architecture, + "operatingSystemFamily": "LINUX", + }, cpu=str(config.task_cpu), memory=str(config.task_memory), executionRoleArn=config.exec_role_arn, @@ -116,42 +122,142 @@ def register_task_definition( return cast(str, response["taskDefinition"]["taskDefinitionArn"]) -def ensure_cluster(session: Any, cluster_name: str) -> str: +def _normalise_cpu_architecture(value: str) -> str: + """Return a validated ECS CPU architecture.""" + architecture = value.strip().upper() + if architecture in {"X86_64", "ARM64"}: + return architecture + raise RuntimeError(f"Unsupported ECS CPU architecture '{value}'. Use X86_64 or ARM64.") + + +def ensure_cluster(session: Session, cluster_name: str) -> str: """Ensure an ECS cluster exists.""" ecs = session.client("ecs") response = ecs.describe_clusters(clusters=[cluster_name]) clusters = response.get("clusters", []) if clusters: - return cast(str, clusters[0]["clusterArn"]) + cluster = clusters[0] + status = str(cluster.get("status", "")) + cluster_arn = cast(str, cluster["clusterArn"]) + if status == "ACTIVE": + return cluster_arn + if status != "INACTIVE": + raise RuntimeError( + f"ECS cluster {cluster_name} is in unexpected status {status} and cannot be used." + ) + # If the cluster does not exist or is inactive, create it. response = ecs.create_cluster(clusterName=cluster_name) return cast(str, response["cluster"]["clusterArn"]) def run_task( - session: Any, - cluster_name: str, - task_definition_arn: str, - subnet_ids: list[str], - security_group_id: str, + session: Session, + config: EcsDeploymentConfig, + container_overrides: list[dict[str, Any]] | None = None, ) -> str: """Run a one-off ECS task.""" + if not config.task_definition_arn: + raise RuntimeError("Task definition is missing. Register it before running tasks.") + if not config.security_group_id or not config.private_subnet_ids: + raise RuntimeError( + "Network configuration is missing. Configure subnets and security group." + ) + ecs = session.client("ecs") - response = ecs.run_task( - cluster=cluster_name, - launchType="FARGATE", - taskDefinition=task_definition_arn, - count=1, - networkConfiguration={ + request: dict[str, Any] = { + "cluster": config.cluster_name, + "launchType": "FARGATE", + "taskDefinition": config.task_definition_arn, + "count": 1, + "networkConfiguration": { "awsvpcConfiguration": { - "subnets": subnet_ids, - "securityGroups": [security_group_id], + "subnets": config.private_subnet_ids, + "securityGroups": [config.security_group_id], "assignPublicIp": "DISABLED", } }, - ) + } + if container_overrides: + request["overrides"] = {"containerOverrides": container_overrides} + + try: + response = ecs.run_task(**request) + except ClientError as exc: + code = exc.response.get("Error", {}).get("Code") + if code == "ClusterNotFoundException": + raise RuntimeError( + f"ECS cluster '{config.cluster_name}' is missing or inactive. " + "Re-run deployment to recreate it." + ) from exc + raise RuntimeError(f"Failed to run ECS task: {exc}") from exc + tasks = response.get("tasks", []) if not tasks: failures = response.get("failures", []) raise RuntimeError(f"Failed to run task: {failures}") return cast(str, tasks[0]["taskArn"]) + + +def wait_for_task_completion( + session: Session, + cluster_name: str, + task_arn: str, + timeout_seconds: int = 1800, + poll_interval_seconds: int = 5, +) -> tuple[bool, str]: + """Wait for a task to stop and report container exit status.""" + ecs = session.client("ecs") + deadline = time.time() + timeout_seconds + + while time.time() < deadline: + response = ecs.describe_tasks(cluster=cluster_name, tasks=[task_arn]) + tasks = response.get("tasks", []) + if not tasks: + failures = response.get("failures", []) + return False, f"Task not found while checking completion: {failures}" + + task = tasks[0] + task_status = str(task.get("lastStatus", "")) + if task_status != "STOPPED": + time.sleep(poll_interval_seconds) + continue + + return _task_completion_result(task) + + return False, f"Timed out waiting for task completion after {timeout_seconds} seconds." + + +def _task_completion_result(task: dict[str, Any]) -> tuple[bool, str]: + """Convert ECS task details into a completion result.""" + target = _find_container(task.get("containers", []), SRE_AGENT_CONTAINER_NAME) + if target is None: + stopped_reason = str(task.get("stoppedReason", "task stopped")) + return ( + False, + "Task stopped before container " + f"{SRE_AGENT_CONTAINER_NAME} was observed: {stopped_reason}", + ) + + exit_code = target.get("exitCode") + reason = str(target.get("reason", "")).strip() + if exit_code == 0: + return True, "Diagnosis task completed successfully." + if reason: + return False, reason + if exit_code is not None: + return False, f"Container {SRE_AGENT_CONTAINER_NAME} exited with code {exit_code}." + + stopped_reason = str(task.get("stoppedReason", "task stopped")) + return ( + False, + f"Task stopped without an exit code for {SRE_AGENT_CONTAINER_NAME}: {stopped_reason}", + ) + + +def _find_container(containers: list[dict[str, Any]], name: str) -> dict[str, Any] | None: + """Return a container by name from task container details.""" + for container in containers: + if container.get("name") == name: + return container + return None diff --git a/src/sre_agent/core/deployments/aws_ecs/iam.py b/src/sre_agent/core/deployments/aws_ecs/iam.py index 282c5b7d..f44222ff 100644 --- a/src/sre_agent/core/deployments/aws_ecs/iam.py +++ b/src/sre_agent/core/deployments/aws_ecs/iam.py @@ -4,11 +4,12 @@ from collections.abc import Callable from typing import Any, cast +from boto3.session import Session from botocore.exceptions import ClientError def ensure_roles( - session: Any, + session: Session, project_name: str, region: str, secret_arns: list[str], @@ -49,7 +50,7 @@ def ensure_roles( return exec_role_arn, task_role_arn -def ensure_service_linked_role(session: Any, reporter: Callable[[str], None]) -> None: +def ensure_service_linked_role(session: Session, reporter: Callable[[str], None]) -> None: """Ensure the ECS service-linked role exists.""" iam = session.client("iam") role_name = "AWSServiceRoleForECS" @@ -151,7 +152,7 @@ def _logs_policy(region: str, account_id: str) -> dict[str, Any]: } -def _get_account_id(session: Any) -> str: +def _get_account_id(session: Session) -> str: """Return the AWS account ID.""" client = session.client("sts") response = client.get_caller_identity() diff --git a/src/sre_agent/core/deployments/aws_ecs/images.py b/src/sre_agent/core/deployments/aws_ecs/images.py index c9d44ec3..d9165f88 100644 --- a/src/sre_agent/core/deployments/aws_ecs/images.py +++ b/src/sre_agent/core/deployments/aws_ecs/images.py @@ -6,26 +6,28 @@ from collections.abc import Callable from dataclasses import dataclass from pathlib import Path -from typing import Any +from boto3.session import Session from botocore.exceptions import ClientError +TARGET_PLATFORM = "linux/arm64" +TARGET_ECS_ARCHITECTURE = "ARM64" + @dataclass(frozen=True) class ImageBuildConfig: """Image build settings for the ECS deployment.""" sre_agent_uri: str - slack_mcp_uri: str image_tag: str def build_and_push_images( - session: Any, + session: Session, root_dir: Path, image_config: ImageBuildConfig, reporter: Callable[[str], None], -) -> None: +) -> str: """Build and push container images to ECR.""" _require_docker() @@ -44,11 +46,13 @@ def build_and_push_images( input_bytes=password.encode("utf-8"), ) - reporter("Building and pushing sre-agent image") + reporter(f"Building and pushing sre-agent image ({TARGET_PLATFORM})") _run( [ "docker", "build", + "--platform", + TARGET_PLATFORM, "-t", f"{image_config.sre_agent_uri}:{image_config.image_tag}", str(root_dir), @@ -60,21 +64,8 @@ def build_and_push_images( reporter, ) - reporter("Mirroring Slack MCP image into ECR") - _run(["docker", "pull", "ghcr.io/korotovsky/slack-mcp-server:latest"], reporter) - _run( - [ - "docker", - "tag", - "ghcr.io/korotovsky/slack-mcp-server:latest", - f"{image_config.slack_mcp_uri}:{image_config.image_tag}", - ], - reporter, - ) - _run( - ["docker", "push", f"{image_config.slack_mcp_uri}:{image_config.image_tag}"], - reporter, - ) + reporter(f"Using ECS runtime CPU architecture: {TARGET_ECS_ARCHITECTURE}") + return TARGET_ECS_ARCHITECTURE def _require_docker() -> None: @@ -83,7 +74,7 @@ def _require_docker() -> None: raise RuntimeError("Docker is required to build and push images.") -def _ecr_login(session: Any) -> tuple[str, str, str]: +def _ecr_login(session: Session) -> tuple[str, str, str]: """Return Docker login credentials for ECR.""" ecr = session.client("ecr") try: diff --git a/src/sre_agent/core/deployments/aws_ecs/models.py b/src/sre_agent/core/deployments/aws_ecs/models.py index 172cf334..17df1eb2 100644 --- a/src/sre_agent/core/deployments/aws_ecs/models.py +++ b/src/sre_agent/core/deployments/aws_ecs/models.py @@ -14,6 +14,7 @@ class EcsDeploymentConfig: task_family: str task_cpu: int task_memory: int + task_cpu_architecture: str image_tag: str vpc_id: str | None private_subnet_ids: list[str] @@ -29,7 +30,6 @@ class EcsDeploymentConfig: exec_role_arn: str | None task_role_arn: str | None ecr_sre_agent_uri: str | None - ecr_slack_mcp_uri: str | None task_definition_arn: str | None cluster_arn: str | None model: str @@ -40,25 +40,6 @@ class EcsDeploymentConfig: slack_mcp_port: int -@dataclass -class VpcInfo: - """Representation of a VPC.""" - - vpc_id: str - cidr_block: str - name: str | None - - -@dataclass -class SubnetInfo: - """Representation of a subnet.""" - - subnet_id: str - cidr_block: str - availability_zone: str - name: str | None - - @dataclass class SecurityGroupInfo: """Representation of a security group.""" diff --git a/src/sre_agent/core/deployments/aws_ecs/network.py b/src/sre_agent/core/deployments/aws_ecs/network.py index 6ad56876..51146b14 100644 --- a/src/sre_agent/core/deployments/aws_ecs/network.py +++ b/src/sre_agent/core/deployments/aws_ecs/network.py @@ -3,46 +3,13 @@ from collections.abc import Callable from typing import Any -from sre_agent.core.deployments.aws_ecs.models import NetworkSelection, SubnetInfo, VpcInfo +from boto3.session import Session - -def list_vpcs(session: Any) -> list[VpcInfo]: - """List VPCs for the current account.""" - ec2 = session.client("ec2") - response = ec2.describe_vpcs() - vpcs = [] - for vpc in response.get("Vpcs", []): - vpcs.append( - VpcInfo( - vpc_id=vpc["VpcId"], - cidr_block=vpc["CidrBlock"], - name=_tag_value(vpc.get("Tags")), - ) - ) - return vpcs - - -def list_private_subnets(session: Any, vpc_id: str) -> list[SubnetInfo]: - """List private subnets for a VPC.""" - ec2 = session.client("ec2") - response = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) - subnets = [] - for subnet in response.get("Subnets", []): - if subnet.get("MapPublicIpOnLaunch") is True: - continue - subnets.append( - SubnetInfo( - subnet_id=subnet["SubnetId"], - cidr_block=subnet["CidrBlock"], - availability_zone=subnet["AvailabilityZone"], - name=_tag_value(subnet.get("Tags")), - ) - ) - return subnets +from sre_agent.core.deployments.aws_ecs.models import NetworkSelection def create_basic_vpc( - session: Any, + session: Session, project_name: str, reporter: Callable[[str], None], ) -> NetworkSelection: @@ -118,16 +85,6 @@ def create_basic_vpc( return NetworkSelection(vpc_id=vpc_id, private_subnet_ids=[private_subnet_id]) -def _tag_value(tags: list[dict[str, Any]] | None) -> str | None: - """Extract a Name tag from a tag list.""" - if not tags: - return None - for tag in tags: - if tag.get("Key") == "Name": - return tag.get("Value") - return None - - def _tag_resource(ec2: Any, resource_id: str, name: str) -> None: """Apply a Name tag to a resource.""" ec2.create_tags(Resources=[resource_id], Tags=[{"Key": "Name", "Value": name}]) diff --git a/src/sre_agent/core/deployments/aws_ecs/secrets.py b/src/sre_agent/core/deployments/aws_ecs/secrets.py index 14598c0f..c6dd29d1 100644 --- a/src/sre_agent/core/deployments/aws_ecs/secrets.py +++ b/src/sre_agent/core/deployments/aws_ecs/secrets.py @@ -1,24 +1,39 @@ """Secrets Manager helpers for ECS deployment.""" -from typing import Any, cast +from dataclasses import dataclass +from typing import cast +from boto3.session import Session from botocore.exceptions import ClientError -def get_secret_arn(session: Any, name: str) -> str | None: - """Fetch a secret ARN by name.""" +@dataclass(frozen=True) +class SecretInfo: + """Metadata about a Secrets Manager secret.""" + + arn: str + scheduled_for_deletion: bool + + +def get_secret_info(session: Session, name: str) -> SecretInfo | None: + """Fetch secret metadata by name.""" client = session.client("secretsmanager") try: response = client.describe_secret(SecretId=name) - return cast(str, response["ARN"]) except ClientError as exc: code = exc.response.get("Error", {}).get("Code") if code == "ResourceNotFoundException": return None raise RuntimeError(f"Failed to read secret {name}: {exc}") from exc + deleted_date = response.get("DeletedDate") + return SecretInfo( + arn=cast(str, response["ARN"]), + scheduled_for_deletion=deleted_date is not None, + ) -def create_secret(session: Any, name: str, value: str) -> str: + +def create_secret(session: Session, name: str, value: str) -> str: """Create a secret and return its ARN.""" client = session.client("secretsmanager") try: @@ -26,3 +41,13 @@ def create_secret(session: Any, name: str, value: str) -> str: except ClientError as exc: raise RuntimeError(f"Failed to create secret {name}: {exc}") from exc return cast(str, response["ARN"]) + + +def restore_secret(session: Session, name: str) -> str: + """Restore a secret that is scheduled for deletion.""" + client = session.client("secretsmanager") + try: + response = client.restore_secret(SecretId=name) + except ClientError as exc: + raise RuntimeError(f"Failed to restore secret {name}: {exc}") from exc + return cast(str, response["ARN"]) diff --git a/src/sre_agent/core/deployments/aws_ecs/security_groups.py b/src/sre_agent/core/deployments/aws_ecs/security_groups.py index 487f5b18..367cb90f 100644 --- a/src/sre_agent/core/deployments/aws_ecs/security_groups.py +++ b/src/sre_agent/core/deployments/aws_ecs/security_groups.py @@ -1,30 +1,13 @@ """Security group management for ECS.""" -from typing import Any - +from boto3.session import Session from botocore.exceptions import ClientError from sre_agent.core.deployments.aws_ecs.models import SecurityGroupInfo -def list_security_groups(session: Any, vpc_id: str) -> list[SecurityGroupInfo]: - """List security groups for a VPC.""" - ec2 = session.client("ec2") - response = ec2.describe_security_groups(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]) - groups = [] - for group in response.get("SecurityGroups", []): - groups.append( - SecurityGroupInfo( - group_id=group["GroupId"], - name=group["GroupName"], - description=group.get("Description", ""), - ) - ) - return groups - - def create_security_group( - session: Any, + session: Session, vpc_id: str, name: str, description: str, diff --git a/src/sre_agent/core/deployments/aws_ecs/status.py b/src/sre_agent/core/deployments/aws_ecs/status.py index f6583f95..79d0ed8c 100644 --- a/src/sre_agent/core/deployments/aws_ecs/status.py +++ b/src/sre_agent/core/deployments/aws_ecs/status.py @@ -1,13 +1,12 @@ """Deployment status checks for ECS.""" -from typing import Any - +from boto3.session import Session from botocore.exceptions import ClientError from sre_agent.core.deployments.aws_ecs.models import EcsDeploymentConfig -def check_deployment(session: Any, config: EcsDeploymentConfig) -> dict[str, str]: +def check_deployment(session: Session, config: EcsDeploymentConfig) -> dict[str, str]: """Check whether deployment resources exist.""" results: dict[str, str] = {} @@ -25,7 +24,7 @@ def check_deployment(session: Any, config: EcsDeploymentConfig) -> dict[str, str results["IAM roles"] = _check_roles(session, config) results["ECR repositories"] = _check_ecr_repos( session, - [config.ecr_repo_sre_agent, config.ecr_repo_slack_mcp], + [config.ecr_repo_sre_agent], ) results["Log group"] = _check_log_group(session, config.log_group_name) results["Task definition"] = _check_task_definition(session, config.task_definition_arn) @@ -34,40 +33,60 @@ def check_deployment(session: Any, config: EcsDeploymentConfig) -> dict[str, str return results -def _check_vpc(session: Any, vpc_id: str | None) -> str: +def _check_vpc(session: Session, vpc_id: str | None) -> str: if not vpc_id: return "not set" ec2 = session.client("ec2") try: - ec2.describe_vpcs(VpcIds=[vpc_id]) + response = ec2.describe_vpcs(VpcIds=[vpc_id]) except ClientError as exc: code = exc.response.get("Error", {}).get("Code") if code == "InvalidVpcID.NotFound": return "missing" return f"error: {code}" + vpcs = response.get("Vpcs", []) + if not vpcs: + return "missing" + state = str(vpcs[0].get("State", "")).lower() + if state and state != "available": + return f"status {state}" return "present" -def _check_subnets(session: Any, subnet_ids: list[str]) -> str: +def _check_subnets(session: Session, subnet_ids: list[str]) -> str: if not subnet_ids: return "not set" ec2 = session.client("ec2") missing = 0 + non_available = 0 for subnet_id in subnet_ids: try: - ec2.describe_subnets(SubnetIds=[subnet_id]) + response = ec2.describe_subnets(SubnetIds=[subnet_id]) except ClientError as exc: code = exc.response.get("Error", {}).get("Code") if code == "InvalidSubnetID.NotFound": missing += 1 else: return f"error: {code}" + continue + + subnets = response.get("Subnets", []) + if not subnets: + missing += 1 + continue + + state = str(subnets[0].get("State", "")).lower() + if state and state != "available": + non_available += 1 + if missing == 0: + if non_available > 0: + return f"status non-available {non_available}/{len(subnet_ids)}" return "present" return f"missing {missing}/{len(subnet_ids)}" -def _check_security_group(session: Any, group_id: str | None) -> str: +def _check_security_group(session: Session, group_id: str | None) -> str: if not group_id: return "not set" ec2 = session.client("ec2") @@ -81,24 +100,32 @@ def _check_security_group(session: Any, group_id: str | None) -> str: return "present" -def _check_secrets(session: Any, names: list[str]) -> str: +def _check_secrets(session: Session, names: list[str]) -> str: client = session.client("secretsmanager") missing = 0 + scheduled_deletion = 0 for name in names: try: - client.describe_secret(SecretId=name) + response = client.describe_secret(SecretId=name) except ClientError as exc: code = exc.response.get("Error", {}).get("Code") if code == "ResourceNotFoundException": missing += 1 else: return f"error: {code}" + continue + + if response.get("DeletedDate") is not None: + scheduled_deletion += 1 + if missing == 0: + if scheduled_deletion > 0: + return f"status scheduled deletion {scheduled_deletion}/{len(names)}" return "present" return f"missing {missing}/{len(names)}" -def _check_roles(session: Any, config: EcsDeploymentConfig) -> str: +def _check_roles(session: Session, config: EcsDeploymentConfig) -> str: iam = session.client("iam") role_names = { f"{config.project_name}-task-execution", @@ -119,7 +146,7 @@ def _check_roles(session: Any, config: EcsDeploymentConfig) -> str: return f"missing {missing}/{len(role_names)}" -def _check_ecr_repos(session: Any, names: list[str]) -> str: +def _check_ecr_repos(session: Session, names: list[str]) -> str: ecr = session.client("ecr") missing = 0 for name in names: @@ -136,28 +163,33 @@ def _check_ecr_repos(session: Any, names: list[str]) -> str: return f"missing {missing}/{len(names)}" -def _check_log_group(session: Any, log_group_name: str) -> str: +def _check_log_group(session: Session, log_group_name: str) -> str: logs = session.client("logs") response = logs.describe_log_groups(logGroupNamePrefix=log_group_name) groups = [group["logGroupName"] for group in response.get("logGroups", [])] return "present" if log_group_name in groups else "missing" -def _check_task_definition(session: Any, task_definition_arn: str | None) -> str: +def _check_task_definition(session: Session, task_definition_arn: str | None) -> str: if not task_definition_arn: return "not set" ecs = session.client("ecs") try: - ecs.describe_task_definition(taskDefinition=task_definition_arn) + response = ecs.describe_task_definition(taskDefinition=task_definition_arn) except ClientError as exc: code = exc.response.get("Error", {}).get("Code") if code in {"ClientException", "InvalidParameterException"}: return "missing" return f"error: {code}" + + task_definition = response.get("taskDefinition", {}) + status = str(task_definition.get("status", "")).upper() + if status and status != "ACTIVE": + return f"status {status}" return "present" -def _check_cluster(session: Any, cluster_name: str) -> str: +def _check_cluster(session: Session, cluster_name: str) -> str: ecs = session.client("ecs") response = ecs.describe_clusters(clusters=[cluster_name]) clusters = response.get("clusters", []) diff --git a/src/sre_agent/core/prompts.py b/src/sre_agent/core/prompts.py index 8def7fff..11d06fb5 100644 --- a/src/sre_agent/core/prompts.py +++ b/src/sre_agent/core/prompts.py @@ -2,7 +2,7 @@ from pathlib import Path -from sre_agent.core.config import AgentConfig +from sre_agent.core.settings import AgentSettings PROMPTS_DIR = Path(__file__).parent / "prompts" @@ -17,7 +17,7 @@ def _load_prompt(filename: str) -> str: def build_diagnosis_prompt( - config: AgentConfig, + config: AgentSettings, log_group: str, service_name: str, time_range_minutes: int = 10, diff --git a/src/sre_agent/core/config.py b/src/sre_agent/core/settings.py similarity index 62% rename from src/sre_agent/core/config.py rename to src/sre_agent/core/settings.py index 4ceb0eb0..993ed00d 100644 --- a/src/sre_agent/core/config.py +++ b/src/sre_agent/core/settings.py @@ -1,13 +1,17 @@ -"""Configuration management for the SRE Agent.""" +"""Runtime settings for the SRE Agent.""" from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict +from sre_agent.config.paths import env_path -class AWSConfig(BaseSettings): +ENV_FILE_PATH = str(env_path()) + + +class AWSSettings(BaseSettings): """AWS configuration for CloudWatch access.""" - model_config = SettingsConfigDict(env_prefix="AWS_", env_file=".env", extra="ignore") + model_config = SettingsConfigDict(env_prefix="AWS_", env_file=ENV_FILE_PATH, extra="ignore") region: str = Field(default="eu-west-2", description="AWS region") access_key_id: str | None = Field(default=None, description="AWS Access Key ID") @@ -15,42 +19,54 @@ class AWSConfig(BaseSettings): session_token: str | None = Field(default=None, description="AWS Session Token") -class GitHubConfig(BaseSettings): +class GitHubSettings(BaseSettings): """GitHub configuration for MCP server via SSE.""" - model_config = SettingsConfigDict(env_prefix="GITHUB_", env_file=".env", extra="ignore") + model_config = SettingsConfigDict( + env_prefix="GITHUB_", + env_file=ENV_FILE_PATH, + extra="ignore", + ) # Required: cannot be empty personal_access_token: str = Field(description="GitHub Personal Access Token") mcp_url: str = Field(description="URL of GitHub MCP server (SSE)") -class SlackConfig(BaseSettings): +class SlackSettings(BaseSettings): """Slack configuration for korotovsky/slack-mcp-server.""" - model_config = SettingsConfigDict(env_prefix="SLACK_", env_file=".env", extra="ignore") + model_config = SettingsConfigDict( + env_prefix="SLACK_", + env_file=ENV_FILE_PATH, + extra="ignore", + ) # Required: cannot be empty channel_id: str = Field(description="Slack channel ID (Cxxxxxxxxxx)") mcp_url: str = Field(description="URL of Slack MCP server (SSE)") -class AgentConfig(BaseSettings): +class AgentSettings(BaseSettings): """Main agent configuration.""" - model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") + model_config = SettingsConfigDict( + env_file=ENV_FILE_PATH, + env_file_encoding="utf-8", + extra="ignore", + ) # LLM Provider anthropic_api_key: str | None = Field(default=None, alias="ANTHROPIC_API_KEY") model: str = Field(default="claude-sonnet-4-5-20250929", alias="MODEL") # Sub-configs (required) - aws: AWSConfig - github: GitHubConfig - slack: SlackConfig + aws: AWSSettings + github: GitHubSettings + slack: SlackSettings -def get_config() -> AgentConfig: +def get_settings() -> AgentSettings: """Load and return the agent configuration. The sub-configs are automatically populated from the environment @@ -58,8 +74,8 @@ def get_config() -> AgentConfig: """ # We use type: ignore[call-arg] because mypy doesn't know BaseSettings # will populate these fields from the environment variables. - return AgentConfig( - aws=AWSConfig(), - github=GitHubConfig(), # type: ignore[call-arg] - slack=SlackConfig(), # type: ignore[call-arg] + return AgentSettings( + aws=AWSSettings(), + github=GitHubSettings(), # type: ignore[call-arg] + slack=SlackSettings(), # type: ignore[call-arg] ) diff --git a/src/sre_agent/core/tools/cloudwatch.py b/src/sre_agent/core/tools/cloudwatch.py index 0f8c286f..a0eef3aa 100644 --- a/src/sre_agent/core/tools/cloudwatch.py +++ b/src/sre_agent/core/tools/cloudwatch.py @@ -8,9 +8,9 @@ from botocore.exceptions import ClientError from pydantic_ai import FunctionToolset -from sre_agent.core.config import AgentConfig from sre_agent.core.interfaces import LoggingInterface from sre_agent.core.models import LogEntry, LogQueryResult +from sre_agent.core.settings import AgentSettings logger = logging.getLogger(__name__) @@ -96,7 +96,7 @@ def _parse_events(self, events: list[dict[str, Any]]) -> list[LogEntry]: return entries -def create_cloudwatch_toolset(config: AgentConfig) -> FunctionToolset: +def create_cloudwatch_toolset(config: AgentSettings) -> FunctionToolset: """Create a FunctionToolset with CloudWatch tools for pydantic-ai.""" toolset = FunctionToolset() cw_logging = CloudWatchLogging(region=config.aws.region) diff --git a/src/sre_agent/core/tools/github.py b/src/sre_agent/core/tools/github.py index 78fca83d..0c8c7a67 100644 --- a/src/sre_agent/core/tools/github.py +++ b/src/sre_agent/core/tools/github.py @@ -4,12 +4,12 @@ from pydantic_ai.mcp import MCPServerStreamableHTTP -from sre_agent.core.config import AgentConfig +from sre_agent.core.settings import AgentSettings logger = logging.getLogger(__name__) -def create_github_mcp_toolset(config: AgentConfig) -> MCPServerStreamableHTTP: +def create_github_mcp_toolset(config: AgentSettings) -> MCPServerStreamableHTTP: """Create GitHub MCP server toolset for pydantic-ai. Connects to an external GitHub MCP server via Streamable HTTP. diff --git a/src/sre_agent/core/tools/slack.py b/src/sre_agent/core/tools/slack.py index dd7c591e..14abcf7f 100644 --- a/src/sre_agent/core/tools/slack.py +++ b/src/sre_agent/core/tools/slack.py @@ -5,7 +5,7 @@ from pydantic_ai.mcp import MCPServerSSE from pydantic_ai.toolsets import FilteredToolset -from sre_agent.core.config import AgentConfig +from sre_agent.core.settings import AgentSettings logger = logging.getLogger(__name__) @@ -13,7 +13,7 @@ ALLOWED_SLACK_TOOLS = {"conversations_add_message"} -def create_slack_mcp_toolset(config: AgentConfig) -> FilteredToolset: +def create_slack_mcp_toolset(config: AgentSettings) -> FilteredToolset: """Create Slack MCP server toolset for pydantic-ai. Connects to an external Slack MCP server via SSE. diff --git a/src/sre_agent/run.py b/src/sre_agent/run.py new file mode 100644 index 00000000..585da872 --- /dev/null +++ b/src/sre_agent/run.py @@ -0,0 +1,83 @@ +"""Run the SRE Agent to diagnose errors.""" + +import asyncio +import logging +import os +import sys + +from dotenv import load_dotenv + +from sre_agent import diagnose_error +from sre_agent.config.paths import env_path + +load_dotenv(env_path()) + +# Configure logging to see tool calls and agent thoughts +logging.basicConfig(level=logging.INFO) +# Set pydantic_ai to INFO to see agent activity +logging.getLogger("pydantic_ai").setLevel(logging.INFO) + + +def _load_request_from_args_or_env() -> tuple[str, str, int]: + """Load diagnosis inputs from CLI args or environment.""" + if len(sys.argv) >= 3: + log_group = sys.argv[1] + service_name = sys.argv[2] + time_range_minutes = int(sys.argv[3]) if len(sys.argv) > 3 else 10 + return log_group, service_name, time_range_minutes + + log_group = os.getenv("LOG_GROUP", "").strip() + service_name = os.getenv("SERVICE_NAME", "").strip() + if not log_group or not service_name: + print("Usage: python -m sre_agent.run [time_range_minutes]") + print( + "Or set environment variables: LOG_GROUP, SERVICE_NAME, TIME_RANGE_MINUTES (optional)" + ) + raise SystemExit(1) + + raw_time_range = os.getenv("TIME_RANGE_MINUTES", "10").strip() + try: + time_range_minutes = int(raw_time_range) + except ValueError as exc: + print("TIME_RANGE_MINUTES must be an integer.") + raise SystemExit(1) from exc + + if time_range_minutes <= 0: + print("TIME_RANGE_MINUTES must be greater than 0.") + raise SystemExit(1) + return log_group, service_name, time_range_minutes + + +async def main() -> None: + """Run the SRE Agent.""" + log_group, service_name, time_range_minutes = _load_request_from_args_or_env() + + print(f"Diagnosing errors in {log_group}") + print(f"Service: {service_name}") + print(f"Time range: last {time_range_minutes} minutes") + print("-" * 60) + + try: + result = await diagnose_error( + log_group=log_group, + service_name=service_name, + time_range_minutes=time_range_minutes, + ) + + print("-" * 60) + print("DIAGNOSIS RESULT") + print("-" * 60) + print(f"\nSummary: {result.summary}") + print(f"\nRoot cause: {result.root_cause}") + + if result.suggested_fixes: + print("\nSuggested fixes:") + for fix in result.suggested_fixes: + print(f"- {fix.description}") + except Exception as exc: # noqa: BLE001 + print(f"\nFATAL ERROR: {exc}") + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/uv.lock b/uv.lock index 8851b5ed..6645e233 100644 --- a/uv.lock +++ b/uv.lock @@ -103,15 +103,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - [[package]] name = "annotated-types" version = "0.7.0" @@ -543,21 +534,6 @@ lua = [ { name = "lupa" }, ] -[[package]] -name = "fastapi" -version = "0.128.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, -] - [[package]] name = "fastavro" version = "1.12.1" @@ -2494,14 +2470,12 @@ source = { editable = "." } dependencies = [ { name = "boto3" }, { name = "click" }, - { name = "fastapi" }, { name = "platformdirs" }, { name = "pydantic-ai" }, { name = "pydantic-settings" }, { name = "python-dotenv" }, { name = "questionary" }, { name = "rich" }, - { name = "uvicorn" }, ] [package.dev-dependencies] @@ -2513,14 +2487,12 @@ dev = [ requires-dist = [ { name = "boto3", specifier = ">=1.42.39" }, { name = "click", specifier = ">=8.3.1" }, - { name = "fastapi", specifier = ">=0.128.0" }, { name = "platformdirs", specifier = ">=4.5.1" }, { name = "pydantic-ai", specifier = ">=1.51.0" }, { name = "pydantic-settings", specifier = ">=2.12.0" }, { name = "python-dotenv", specifier = ">=1.2.1" }, { name = "questionary", specifier = ">=2.1.1" }, { name = "rich", specifier = ">=14.3.2" }, - { name = "uvicorn", specifier = ">=0.40.0" }, ] [package.metadata.requires-dev] From 10fafe9aec8254b629dc31407c9ff90f9ad9c103 Mon Sep 17 00:00:00 2001 From: Oscar <25309418+osw282@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:59:19 +0000 Subject: [PATCH 3/8] Set up Opik for agent evaluation (#155) * Allow user to select which repo the agent to look at * Add dataset * Add metrics * Common * Update test case * Use opik span * Add mock tools * Add mock tools * Update prompts * Add experiment readme * Update readme * Update readme --- AGENTS.md | 1 + docker-compose.yaml | 2 +- pyproject.toml | 1 + src/sre_agent/cli/configuration/models.py | 3 + src/sre_agent/cli/configuration/wizard.py | 62 ++++++++++- .../cli/mode/remote/aws/ecs/steps.py | 3 + .../core/deployments/aws_ecs/ecs_tasks.py | 3 + .../core/deployments/aws_ecs/models.py | 3 + src/sre_agent/core/prompts.py | 7 +- .../core/prompts/diagnosis_prompt.txt | 9 ++ src/sre_agent/core/settings.py | 3 + src/sre_agent/eval/README.md | 55 ++++++++++ src/sre_agent/eval/__init__.py | 1 + src/sre_agent/eval/common/__init__.py | 5 + src/sre_agent/eval/common/case_loader.py | 69 +++++++++++++ src/sre_agent/eval/imgs/opik_dataset.png | Bin 0 -> 283757 bytes src/sre_agent/eval/imgs/opik_experiment.png | Bin 0 -> 426972 bytes src/sre_agent/eval/tool_call/README.md | 86 ++++++++++++++++ src/sre_agent/eval/tool_call/__init__.py | 1 + src/sre_agent/eval/tool_call/config.py | 6 ++ .../eval/tool_call/dataset/__init__.py | 9 ++ .../tool_call/dataset/create_and_populate.py | 41 ++++++++ .../eval/tool_call/dataset/schema.py | 31 ++++++ .../test_cases/cartservice_test_case_01.json | 26 +++++ .../test_cases/cartservice_test_case_02.json | 27 +++++ .../currencyservice_test_case_01.json | 30 ++++++ .../no_error_log_found_test_case_01.json | 13 +++ src/sre_agent/eval/tool_call/experiment.py | 97 ++++++++++++++++++ .../eval/tool_call/github_toolset.py | 59 +++++++++++ .../eval/tool_call/metrics/__init__.py | 6 ++ .../metrics/expected_tool_select_order.py | 90 ++++++++++++++++ .../metrics/expected_tool_selection.py | 84 +++++++++++++++ .../eval/tool_call/metrics/span_tools.py | 45 ++++++++ .../eval/tool_call/mocks/__init__.py | 6 ++ .../eval/tool_call/mocks/cloudwatch.py | 50 +++++++++ src/sre_agent/eval/tool_call/mocks/runtime.py | 12 +++ src/sre_agent/eval/tool_call/mocks/slack.py | 29 ++++++ src/sre_agent/eval/tool_call/mocks/toolset.py | 44 ++++++++ src/sre_agent/eval/tool_call/prompts.py | 30 ++++++ src/sre_agent/eval/tool_call/run.py | 29 ++++++ 40 files changed, 1070 insertions(+), 8 deletions(-) create mode 100644 src/sre_agent/eval/README.md create mode 100644 src/sre_agent/eval/__init__.py create mode 100644 src/sre_agent/eval/common/__init__.py create mode 100644 src/sre_agent/eval/common/case_loader.py create mode 100644 src/sre_agent/eval/imgs/opik_dataset.png create mode 100644 src/sre_agent/eval/imgs/opik_experiment.png create mode 100644 src/sre_agent/eval/tool_call/README.md create mode 100644 src/sre_agent/eval/tool_call/__init__.py create mode 100644 src/sre_agent/eval/tool_call/config.py create mode 100644 src/sre_agent/eval/tool_call/dataset/__init__.py create mode 100644 src/sre_agent/eval/tool_call/dataset/create_and_populate.py create mode 100644 src/sre_agent/eval/tool_call/dataset/schema.py create mode 100644 src/sre_agent/eval/tool_call/dataset/test_cases/cartservice_test_case_01.json create mode 100644 src/sre_agent/eval/tool_call/dataset/test_cases/cartservice_test_case_02.json create mode 100644 src/sre_agent/eval/tool_call/dataset/test_cases/currencyservice_test_case_01.json create mode 100644 src/sre_agent/eval/tool_call/dataset/test_cases/no_error_log_found_test_case_01.json create mode 100644 src/sre_agent/eval/tool_call/experiment.py create mode 100644 src/sre_agent/eval/tool_call/github_toolset.py create mode 100644 src/sre_agent/eval/tool_call/metrics/__init__.py create mode 100644 src/sre_agent/eval/tool_call/metrics/expected_tool_select_order.py create mode 100644 src/sre_agent/eval/tool_call/metrics/expected_tool_selection.py create mode 100644 src/sre_agent/eval/tool_call/metrics/span_tools.py create mode 100644 src/sre_agent/eval/tool_call/mocks/__init__.py create mode 100644 src/sre_agent/eval/tool_call/mocks/cloudwatch.py create mode 100644 src/sre_agent/eval/tool_call/mocks/runtime.py create mode 100644 src/sre_agent/eval/tool_call/mocks/slack.py create mode 100644 src/sre_agent/eval/tool_call/mocks/toolset.py create mode 100644 src/sre_agent/eval/tool_call/prompts.py create mode 100644 src/sre_agent/eval/tool_call/run.py diff --git a/AGENTS.md b/AGENTS.md index 9fe4e410..7b925c28 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ - Keep code simple, with a strong focus on readability and maintainability. - Use UK English. - No em dashes in comments and documentations. +- Use python 3.13 syntax. ## Functions - Keep each function concise, easy to read, and clearly named. diff --git a/docker-compose.yaml b/docker-compose.yaml index 9839e64f..af9261f3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,7 +6,7 @@ name: sre-agent services: - # SRE Agent (FastAPI) + # SRE Agent sre-agent: build: context: . diff --git a/pyproject.toml b/pyproject.toml index 347ed6dc..15404ab3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ [project.scripts] sre-agent = "sre_agent.cli.main:main" +sre-agent-run-tool-call-eval = "sre_agent.eval.tool_call.run:main" [dependency-groups] dev = [ diff --git a/src/sre_agent/cli/configuration/models.py b/src/sre_agent/cli/configuration/models.py index 7c994648..ef75ea85 100644 --- a/src/sre_agent/cli/configuration/models.py +++ b/src/sre_agent/cli/configuration/models.py @@ -41,6 +41,9 @@ class IntegrationConfig(BaseModel): logging_platform: str = "cloudwatch" slack_channel_id: str | None = None github_mcp_url: str = "https://api.githubcopilot.com/mcp/" + github_owner: str = "" + github_repo: str = "" + github_ref: str = "main" class DeploymentState(BaseModel): diff --git a/src/sre_agent/cli/configuration/wizard.py b/src/sre_agent/cli/configuration/wizard.py index 7c48f8a4..e2a19954 100644 --- a/src/sre_agent/cli/configuration/wizard.py +++ b/src/sre_agent/cli/configuration/wizard.py @@ -43,6 +43,9 @@ class _WizardSelections: model_provider: str notification_platform: str code_repository_provider: str + github_owner: str | None + github_repo: str | None + github_ref: str | None deployment_platform: str logging_platform: str slack_channel_id: str | None @@ -110,7 +113,12 @@ def _run_config_wizard( force_reconfigure, updates, ) - code_repository_provider = _configure_code_repository_provider( + ( + code_repository_provider, + github_owner, + github_repo, + github_ref, + ) = _configure_code_repository_provider( config, env_values, force_reconfigure, @@ -131,6 +139,9 @@ def _run_config_wizard( model_provider=model_provider, notification_platform=notification_platform, code_repository_provider=code_repository_provider, + github_owner=github_owner, + github_repo=github_repo, + github_ref=github_ref, deployment_platform=deployment_platform, logging_platform=logging_platform, slack_channel_id=slack_channel_id, @@ -207,7 +218,7 @@ def _configure_code_repository_provider( env_values: dict[str, str], force_reconfigure: bool, updates: dict[str, str], -) -> str: +) -> tuple[str, str | None, str | None, str | None]: """Prompt for code repository provider and required credentials.""" code_repository_provider = _prompt_choice( "Remote code repository:", @@ -222,9 +233,34 @@ def _configure_code_repository_provider( env_values.get("GITHUB_PERSONAL_ACCESS_TOKEN"), force_reconfigure, ) + github_owner = _prompt_text( + "GitHub repository owner:", + env_values.get("GITHUB_OWNER") or config.integrations.github_owner, + force_reconfigure, + ) + github_repo = _prompt_text( + "GitHub repository name:", + env_values.get("GITHUB_REPO") or config.integrations.github_repo, + force_reconfigure, + ) + github_ref = _prompt_text( + "GitHub repository ref:", + env_values.get("GITHUB_REF") or config.integrations.github_ref, + force_reconfigure, + ) + updates["GITHUB_OWNER"] = github_owner + updates["GITHUB_REPO"] = github_repo + updates["GITHUB_REF"] = github_ref + return code_repository_provider, github_owner, github_repo, github_ref else: - _clear_env_keys(updates, "GITHUB_PERSONAL_ACCESS_TOKEN") - return code_repository_provider + _clear_env_keys( + updates, + "GITHUB_PERSONAL_ACCESS_TOKEN", + "GITHUB_OWNER", + "GITHUB_REPO", + "GITHUB_REF", + ) + return code_repository_provider, None, None, None def _configure_deployment_platform( @@ -345,6 +381,12 @@ def _persist_wizard_choices( config.integrations.model_provider = selections.model_provider config.integrations.notification_platform = selections.notification_platform config.integrations.code_repository_provider = selections.code_repository_provider + if selections.github_owner is not None: + config.integrations.github_owner = selections.github_owner + if selections.github_repo is not None: + config.integrations.github_repo = selections.github_repo + if selections.github_ref is not None: + config.integrations.github_ref = selections.github_ref config.integrations.deployment_platform = selections.deployment_platform config.integrations.logging_platform = selections.logging_platform config.integrations.slack_channel_id = selections.slack_channel_id @@ -433,6 +475,18 @@ def _append_repository_missing_items( "GITHUB_PERSONAL_ACCESS_TOKEN" ): missing.append(_MissingConfigItem("GitHub token", visible=False)) + if code_repository_provider == CODE_REPOSITORY_PROVIDER_GITHUB and not env_values.get( + "GITHUB_OWNER" + ): + missing.append(_MissingConfigItem("GitHub repository owner")) + if code_repository_provider == CODE_REPOSITORY_PROVIDER_GITHUB and not env_values.get( + "GITHUB_REPO" + ): + missing.append(_MissingConfigItem("GitHub repository name")) + if code_repository_provider == CODE_REPOSITORY_PROVIDER_GITHUB and not env_values.get( + "GITHUB_REF" + ): + missing.append(_MissingConfigItem("GitHub repository ref")) def _append_deployment_missing_items( diff --git a/src/sre_agent/cli/mode/remote/aws/ecs/steps.py b/src/sre_agent/cli/mode/remote/aws/ecs/steps.py index f9ad0d65..6bc3f9ce 100644 --- a/src/sre_agent/cli/mode/remote/aws/ecs/steps.py +++ b/src/sre_agent/cli/mode/remote/aws/ecs/steps.py @@ -77,6 +77,9 @@ def ecs_config_from_cli(config: CliConfig) -> EcsDeploymentConfig: model=config.integrations.model, slack_channel_id=config.integrations.slack_channel_id, github_mcp_url=config.integrations.github_mcp_url, + github_owner=config.integrations.github_owner, + github_repo=config.integrations.github_repo, + github_ref=config.integrations.github_ref, log_group_name=config.ecs.log_group_name, slack_mcp_host=config.ecs.slack_mcp_host, slack_mcp_port=config.ecs.slack_mcp_port, diff --git a/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py index f9329d19..95dcf617 100644 --- a/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py +++ b/src/sre_agent/core/deployments/aws_ecs/ecs_tasks.py @@ -61,6 +61,9 @@ def register_task_definition( {"name": "SLACK_CHANNEL_ID", "value": config.slack_channel_id}, {"name": "SLACK_MCP_URL", "value": slack_mcp_url}, {"name": "GITHUB_MCP_URL", "value": config.github_mcp_url}, + {"name": "GITHUB_OWNER", "value": config.github_owner}, + {"name": "GITHUB_REPO", "value": config.github_repo}, + {"name": "GITHUB_REF", "value": config.github_ref}, ], "secrets": [ { diff --git a/src/sre_agent/core/deployments/aws_ecs/models.py b/src/sre_agent/core/deployments/aws_ecs/models.py index 17df1eb2..acbdfa64 100644 --- a/src/sre_agent/core/deployments/aws_ecs/models.py +++ b/src/sre_agent/core/deployments/aws_ecs/models.py @@ -35,6 +35,9 @@ class EcsDeploymentConfig: model: str slack_channel_id: str | None github_mcp_url: str + github_owner: str + github_repo: str + github_ref: str log_group_name: str slack_mcp_host: str slack_mcp_port: int diff --git a/src/sre_agent/core/prompts.py b/src/sre_agent/core/prompts.py index 11d06fb5..f5c8564b 100644 --- a/src/sre_agent/core/prompts.py +++ b/src/sre_agent/core/prompts.py @@ -27,9 +27,10 @@ def build_diagnosis_prompt( log_group=log_group, time_range_minutes=time_range_minutes, service_display=service_name, + owner=config.github.owner, + repo=config.github.repo, + ref=config.github.ref, + channel_id=config.slack.channel_id, ) - # Add Slack context - prompt += f"\n\nSlack Context:\n- Channel ID: {config.slack.channel_id}" - return prompt diff --git a/src/sre_agent/core/prompts/diagnosis_prompt.txt b/src/sre_agent/core/prompts/diagnosis_prompt.txt index 0c2241ad..9b97a4ab 100644 --- a/src/sre_agent/core/prompts/diagnosis_prompt.txt +++ b/src/sre_agent/core/prompts/diagnosis_prompt.txt @@ -7,4 +7,13 @@ ORDER OF OPERATIONS: 3. If logs found, search GitHub and diagnose. 4. Reply to the Slack thread with the result. +GitHub Context: +- Repository name: {repo} +- Repository owner: {owner} +- Repository ref: {ref} +- Search and inspect code in this repository only. + +Slack Context: +- Channel ID: {channel_id} + DO NOT skip step 1. Start the thread before doing anything else. diff --git a/src/sre_agent/core/settings.py b/src/sre_agent/core/settings.py index 993ed00d..f3f47c5a 100644 --- a/src/sre_agent/core/settings.py +++ b/src/sre_agent/core/settings.py @@ -31,6 +31,9 @@ class GitHubSettings(BaseSettings): # Required: cannot be empty personal_access_token: str = Field(description="GitHub Personal Access Token") mcp_url: str = Field(description="URL of GitHub MCP server (SSE)") + owner: str = Field(description="Default GitHub repository owner") + repo: str = Field(description="Default GitHub repository name") + ref: str = Field(description="Preferred GitHub ref (branch, tag, or SHA)") class SlackSettings(BaseSettings): diff --git a/src/sre_agent/eval/README.md b/src/sre_agent/eval/README.md new file mode 100644 index 00000000..e653e60e --- /dev/null +++ b/src/sre_agent/eval/README.md @@ -0,0 +1,55 @@ +# SRE Agent Evaluation + +This directory contains evaluation suites for the SRE agent. + +## Scope + +Evaluations use intentionally flawed service snippets from: + +- [sre-agent-eval](https://github.com/fuzzylabs/sre-agent-eval) + +Evaluations are implemented with [Opik](https://github.com/comet-ml/opik). + +## Structure + +- `common`: shared helpers used across suites. +- `tool_call`: evaluates tool selection and tool call order. + +## Current suite + +The active suite is `tool_call`. + +It validates: + +- required tool usage +- expected tool order +- optional GitHub usage expectations per case + +It uses: + +- real GitHub MCP calls +- mocked Slack and CloudWatch calls +- Opik tool spans (`task_span`) for scoring + +## Run + +If you are running Opik locally, start the Opik platform first: + +```bash +# Clone the Opik repository +git clone https://github.com/comet-ml/opik.git + +# Navigate to the repository +cd opik + +# Start the Opik platform +./opik.sh +``` + +See [comet-ml/opik](https://github.com/comet-ml/opik) for details. + +When the server is running, open [http://localhost:5173/](http://localhost:5173/) to view datasets and experiments. + +For suite-specific details, see: + +- `src/sre_agent/eval/tool_call/README.md` diff --git a/src/sre_agent/eval/__init__.py b/src/sre_agent/eval/__init__.py new file mode 100644 index 00000000..88c2ed0a --- /dev/null +++ b/src/sre_agent/eval/__init__.py @@ -0,0 +1 @@ +"""Evaluation suite.""" diff --git a/src/sre_agent/eval/common/__init__.py b/src/sre_agent/eval/common/__init__.py new file mode 100644 index 00000000..f14c3d26 --- /dev/null +++ b/src/sre_agent/eval/common/__init__.py @@ -0,0 +1,5 @@ +"""Common helpers for evaluation suites.""" + +from sre_agent.eval.common.case_loader import load_json_case_models + +__all__ = ["load_json_case_models"] diff --git a/src/sre_agent/eval/common/case_loader.py b/src/sre_agent/eval/common/case_loader.py new file mode 100644 index 00000000..71836b75 --- /dev/null +++ b/src/sre_agent/eval/common/case_loader.py @@ -0,0 +1,69 @@ +"""Shared case loader helpers for evaluation suites.""" + +from pathlib import Path + +from pydantic import BaseModel + + +def load_json_case_models[CaseT: BaseModel]( + cases_dir: Path, + model_type: type[CaseT], + *, + case_id_field: str = "case_id", +) -> list[CaseT]: + """Load JSON case files into validated Pydantic models. + + Args: + cases_dir: Directory containing case files. + model_type: Pydantic model used to validate each case. + case_id_field: Case id attribute used for duplicate checks. + + Returns: + Validated case models in stable filename order. + """ + case_files = sorted(cases_dir.glob("*.json")) + if not case_files: + msg = f"No JSON case files found in {cases_dir}." + raise ValueError(msg) + + cases: list[CaseT] = [] + seen_case_ids: set[str] = set() + + for case_file in case_files: + case = model_type.model_validate_json(case_file.read_text(encoding="utf-8")) + _validate_unique_case_id( + case=case, + case_file=case_file, + seen_case_ids=seen_case_ids, + case_id_field=case_id_field, + ) + cases.append(case) + + return cases + + +def _validate_unique_case_id[CaseT: BaseModel]( + case: CaseT, + case_file: Path, + seen_case_ids: set[str], + *, + case_id_field: str, +) -> None: + """Ensure case ids are unique across loaded files. + + Args: + case: The case to validate. + case_file: The file containing the case. + seen_case_ids: The set of seen case ids. + case_id_field: The field containing the case id. + """ + case_id = getattr(case, case_id_field, None) + if not isinstance(case_id, str) or not case_id.strip(): + msg = f"Missing or invalid '{case_id_field}' in {case_file}." + raise ValueError(msg) + + if case_id in seen_case_ids: + msg = f"Duplicate '{case_id_field}' detected: '{case_id}'." + raise ValueError(msg) + + seen_case_ids.add(case_id) diff --git a/src/sre_agent/eval/imgs/opik_dataset.png b/src/sre_agent/eval/imgs/opik_dataset.png new file mode 100644 index 0000000000000000000000000000000000000000..bf578a46bc9dd522b543b31a4068cfead8335f46 GIT binary patch literal 283757 zcmb4q1zc3!_BIM4B_IljNSCw%Lw7e4LzhyLLw86kEh#X9NY^0UGXl~A(!$U^Gz^{J z!290&uY2G7e)D5y&YW}hK6|gd*0Y|q*5<=2Rk;T^PjS%D&>kqrOKYH^VYi~8p?|}| zM4cgpBrT$$;i*_lNxf2#lA?a)46?Mgvp_?W|B$3}U-yv$c?NqS;av<`YK8;G_wv}( z3?U@q-lR!!7+4G)Az$9e#R#=jMq+6nJeF=5eiYXh=FN-WK0|=Z(&Obx>AsvvC?0#> ziyRQGJl__YZ9@9Z_HXy2iSH~Z<<=K4qIt#p>M4H{V`UZymGQg2Wlk~psC|{A@xo* z7>setWAFsAoT7=WiX4lH@{4!dCDQAjaQuixqb^3CRw$$KoKX+35SmL;2u6|cD}=nk z{^3t}zO>v&DA|PdhWsshXAbM(1i}Piex<_gC!H`b@T{ds7ZX=NoGoQ#fAu00aWc!m zsq<1e3{bMJR}uHb;^zxctg~eZbGhIT5Ch`jM?zclcMEUkzcdqZS3aiMWAPc0rCx0X z-(mcbuvA~(L?bHOMSvCjn{WY7!9n^YPDMS8U)~xW8Ls^v>E%SYAz?@tf4>8N;#or9 zdos#n{jjKBvW3iji(t{`hhnVG3j!>D%0fy%$cH+g;Z`mX1v-!2Vf!KuCcIOQ`Qza} z`1d!O^pRFJhacafl^ZlR9)K+fRS@EOkJ&iB37%PW# z66@y2cVcT|QTH|;De&*L3DSCLoH(*cs*8VNaR;--|L838G*nC5p~_y5ge$mYhiy6q zs5^X%=L_anH6|3As->nIfM|ZddFYdt) zie{DOUDZ({j1zH8u0Sk)yq9evME4Eocc0ldkJZSDYWC3=3Ph6qu^08t3}E-C*N`=VMbRXN zu^&vRgP%XQz-y0v!`+Y{_Bm|heFWFI$*SD4?U_8Zo{l}cbJah|kESUsy56w$0~bQPpPQNuK5%B2U3iZQ1R3X9?(TvID7{YTBs^^_rhGV~6E{2GF&X!U-OLo<&Q!Sn`vcEA zVuVd7IY>sfx8W{A)Gqrj0h`asS3*L@)+VaP2=n1cJziE^_EACh?CpiIg zni=VG9^o@B;DJMhzLPx*>Ri0*%Lqj?3Da2sc+&Y3!J=k6&vyqfp0>o9s_oM6Vkv|u zx3WoMXMw*ZIL0~RK$WIL#-(XLDpWBxw0XQt{iFg`GL)T?wxgz`b0?xAI#-~N7wyhi zQi-77Q#j2b8p0f+98w=*(BMO`>qXm1x-;JYsM$s2{IDL}l= zPKs7@+Z>P16xtTU3tdJvM~T;aM?DIuG+~8T5wh8OgBNd^t9&P&_1U*DwkWqsF{Rqn zgY#)dbPLpT2Me4O7VmwQtj%#*8(s@Na6Y?pcF((vQD#^%p}2e8@QYrqmprn;5=J6r z8>F41oiWB$I<0S2onezuRa%8!9Xi=Q39nXrD`swr=Yp9+W6Jh=(-i#1b%W5Y#LZK1 zLeN6c$4$`n%(dFh$n}w{)rK~La%}6(8)a6;*wkLbmZ^cJ4qvg3w|G`jZ+{NGJT&8o z_R8kd9#B)u(J31L;sM-g7F7~^B_+2#&qGqFXx#p({ZC?Dk~jG~Nh5~>;H zR5Hq4^`2>S6bHG*k6PKhefp;8#kA$*>$8@hlWa33T_r7>5P?y!ea36(SW{oqK2f{_NvkCkdHl9#=Ehm%1iC8b2A&}y3sZ<|u9zDkZNNF~%vs&cP7)mqcur9Js= zZ%tqMw7Jh9ezi+=Z$)uMqiO79<3!{Z--JgDT@>;O6Pb?4c0+IjdS}-6tp0f@-SEq) z@~-mky>`#YJ?2H7Xfcti%7kJ~$S{6B{81HCi0bpSUtIHQRzg;RE=kCw4^(*t*FG2B z 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 z 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º-Gq&BDYN3 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 z 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

R?l-251q79fBqc-w=e(R&GC z3`P}%)sEKs7b}g@!Kar7RR{OJ5S~R>2q98pT?H;QjO*~ z=4Aw2E0qYjZsJ!q`WJzANpSNUm5(_W2AK`bzc8Ht@~iG6xc(aRc&yeAijU$Uv@;aC zZ%~)M52dm!D8-%sg9{)A5X9wn=vzO{G5vDwWxH!5h`=D@RI+&sjC*4VZELhyXtj|h ziySO}c)GX^dEPbL`>HlUs^?vx_FX-7YN)BMKs?mpv5ii3I7^am+6_cY(N7qhyV{Y% z*{5S@tb@2X2T=xGZ)Eta#)HRGjc*1|TYH^GTz(=Evyh0@L(hE!A-c zD*aN`TNM>+3H|JH!}KEbKD8h02V{c4gMce#BYz1zh$n*G^m6{;Vl`xuHDP}(HUH1b z;eY!VG8ycLEJ7U#FNB&$WkgF333A*vH&SZO@8y=8<{sLg#QP>(xan)H?#B6_=)jbA znxMg=y3cvMb1H$lRNRo6_?c8t1iUf0x7x)ho5&$!Dpa9ZG7lsxBaN1X6*5-jy0zup zGWSAhsa{LW{pl5&opPz)vLXnk6@W=Rl$Rq>aX@oJ{CzMQIiDk9@DzzhW|)7x0;6cybrzVh`~5Zh+r!WCCWTRH`LPmV4ni zPup8=>!tf9^qj7S1uUM>vuW3fFe2-K+PzBV~4v;9?<6Y25pBD!2Pl<##U@FDpS-a4OZhN2vX9f zIc8RngoE}MeD34;8ODFbPmxJ^*iT#Sp^U$kV0u6fx}OqnTZICn)!H1E-qE(s9Y+?P z<~)by3#}Xnwx94=!5;ztq9h`-NdCN)8sv@(XYxQ9118z&S3 zi#io00I;~P`&%Qm&z&b9^PR~j5c)2l6A$oI;NtazINmP4=79H&`lWdsDKuCOsLw^ju22vIC0YY_IN5V-vnVi z~cALv;t?vncXu3zW?2G%#RL#u*U)K=aR>6r%8{f4`-+rQjjYQ1b z%8S0n17KR7ec+3^KTr;&{Q%o@V}Y7WJFvV90ORTIZ?7|~W*R!uDl34Bi)kcFr{3`r zSp(_(kk%s`7`lhM4!@n!rgP{+lP3Wv?8T4f+@GO?sFwL&s@DwgvE#iZ6MBfz7+s&G z0|DVS3PQcQ2Un}>c;ou~?}=TDY(IJ!eWajFY39kiu6Buv?_Z+BSn9~~>$f`D)K2Y} zwAW@Xmu!**oWiZ#94zaO)rvfGoh99`wsODELX0<<&BqEG=jSD~h6;Sf+|!8<*G^M% zEF(nSV={Ow1{*fs1GysK;KeK0oc92`9u<_L6v%aYN~2;bQ59*UwOu#;@}Hu92-Gza z#6~PO{KvG7hf8-5(1nQ_UirSY+8Lql#%RwN0W9Pw$8XI~Tti#=RB+e&=3W9QI|k#f zp~y?rge0pigBr`C+f7*I+ixCJx!|51^p+8qu3PVgh!GX7PqWotl0TViWcE(pqw?NX zs^pl5p}QN@PspvcTo>>he$>mt?lFUjznpR2+}ibW8xW(96mG5<1dI>QujrtugwgFg za-!vrjqk53+z+MsR7R1{mIuZ@zjg%!t6p&NEyDv|fD|2qzvk z4AIeMct)psO%PzNVNYIe0Gp7DhwwST&SPt@qyQx`AhDpeJWF}Oq?}#ad!Mvb__M=Z zs7CL0b+obfcAK1cJkYL7ld3Jcwgi~_eHt+&`S+pd(VwvS1HM*Hi=61I zT#}H+cliuF4R3&xP$Tsn^&4_s%|t8`^NpbTxC`sV=X8+<4+$sl{lzd~9w7{=PRzj8 zX@DfzR2X-|^Jq$7?X*V;od)lrdoc)F0*>VGWZojqlt2SY1z1fM^Eas}nw?K@fp~!& z2)GJ0#K{7Tr5J(k7*-!Yv_kYfO4$$M7uEA^Lx(VwdEU%W;RJQS3%c&#QC;J)Ytey?@U>C;2AXm&~Ytg@ePBj z^?4mK`jLPq*s?Nk?+JVeBCFdY9V&Zli3V6wFP^sOH@uDMVPymKG4Ug@@H-wp1T~0H z;q~7vYgK_eKp$xLR*kX$uVp87YS3;+iu9x`xh^;^{_#+Y!S8JT=AmLq0>-M0l)$qt z@R#-(7tQ0}(*!O@@d(>F4scXz%>8J8^N5y#rv!*7V|AsPJ*t0Ub$32Icok&Shrgr! z*siMW4Xpj6=Jju^447gO0aGVHv~)S~SoV*1nNEDLqnGah-Pl^`zQ@O>QOnBxH+@sc z!wsSjkRftN4|xonWQ31)LCpvpgNAw1HJ}r^`Vw#b??zQXC-mVsLW~F5{}hk!jUSHq zM*tAER$nqX7yy?|cdZrrx9;~7WU6q$#DJ>QzTb`J&qU8k_Q7W2N&!Emn`83-%1Z&? zESz^p;_-q-enE!wIQ))<`{3;=`yV_zI1bf8{BPbq9-x{71cTaNZ6y7 z)J($S@g5hDs3K+I%9BCi#XK=9`+!~bQS$cs*Zx}z`11xSfxqlv`uOu-b`be!2TQEQ zO7soV;mYj)pZ!IkPxK$l`ZwqDC;tD(vi{@N|Bh$>k6ZswPVt|d;-9Bzk^iUW6#thc zwA5-3>drTmJ|ujMK0g+k?=QKTgB><{=`W8~qn}uodVczilAV+COB~c<4-> zf3t?@4t=PbtDmT^WoeO@n4@5ME{Vi6p3 z{n}CwEfNNDsBQB-`CJ`FEce1ZE|yo3Bxxk>z;@v0E~I~aD)fg>Wf!gx&(p&gz#hN2 z*m{yJ2ER<>>rfFyW^-TSW06phWKA&m@Cj;VLU#V<2bd0g=$opG)u>r=NtHB-w=u75 z#RSQe$*`dC3sL?x-0>v^{&W3i<7j)2a+D=>(o0Eif!{%IoVVY?v&+Jn^}-p|ke=nH z9mKZ6Q`@FrXr{4m1zlS{)F`e0T^~9!74BgN{c>yLRyp%xEt=hIaJ8Sx;j!useX_$a zkMNH#?DO;ipD;h$q#|_bSOO>&$vhBcJv8JSiS8?2y?(eQ-P&;Iq(Y233%4SncPw%X zaR4mQ3-NO#o_g^p^-PI|#jP1cf6^G9zP zF0WD!R!3x*$M$#o;%k+Po2z3t?8|JRw})nIJ4u0EdD)eCGCC4tW<^##;@-^az z`C!B0L_Jv7??$;!)xLJS*XW@Kk*)X*dxUsp{x?AWu{Hq6G@g<6Ojwg{ay$?`71w1Z zKJapYd+A?8gEmh!^u5H@3qr_|&~}7h;-&f&>koGOVABjlmo(jRWvax!bc++PGsto5 zk^nc~oGdwt2K(w;vjyV4 zda&$3lPppYskEt=WUarT{|fHcWS}=n6wye5$N$6Bdxx_bw_*PslvayY?V>tp?NPgE z&8pqjCZTHY8H7-yilVLBt3}OseU;wK|;8%|7g%o?dWlZ(DnM&dZkN` zH7_(4ZAzqXc(ixKVAi*G*y7KDzaqC$jZ}Zfac2Wr_^Ri3m^1h6R(clU>1gkkl&0FX z#m2{9(6T;p?rlDf&mfjap;vcG@fz6ro0K5>bQWUR38MqHibxGvvVlK>FH zG#5*(CN%CsZ~gDRxv2qI>|3&fQSg@xRyPK|C0AUdGWu1(C^P4*vtHIvc1tk4YF-n> z49std#?%p%&qq!YA3cFl!nw1yXm-Z68o0@o%)Y9rldV z3UfWe(xx26P{5CJLlw_oS}S6<#q-=38N@58QzQXp@7AqnksAYaiW4&Y=JlU5mWS1F z-)5iBu<-+w2X>cv4k6Slr?JoGyPP)I)eA4Rk}l?wbGwz z=?Hm#t-3)&UlW@tqRb?uqxiU0lNyMspC9>2y715cp(AUr3~0Y^r%r~uF6LD7C#qI! zEML(C2K^cJ6+-_eP5#9N$eli&dDmWmG4Z0nrUb6zZLaL`-$d%VLp0%Xt*&0e9if)> zOLpldAwH8gb9vJZp0fdFug3sW?5brvb?%45FHU8TH~w@e*;2bs^WWtD`}j@y)D%Em z!ZUk+^4=0PJ|gc}H@*(Ix(Il3iv-ll#y!w3UXV+Pd~-sA=_rUBEmR&vcs0q}w{}w< zaE^_Sm$0UceF%Ck;xk$voKnS1{~sngJ# z(mTCno{;f0Vh$Ndop$M%mf$+5`I^z(z3ZqELUBtwg2cF|1lsyj#FP*RVkcoe zEzQ}%MHZj_862hrRuiEUEkZ_c=L2cK%8pAB?o&A_{$BvM*T2l_WKl|&V?ciu4b(3F zNBkfdxjj&;_Woy5KDNGbr}Zou9CY6zi>=W7CvG^h-mY}Vy~l$hI*bWf^yL4uALjjL(R7VhK8f9#l_)dG-{*oWp% zaJ8Dqvi2LnaRqxsLO&3OrdDa)X$mU z)g2|IN-8~1#4qCZL8b-fIY%$6v>;V)MN}%>skDj<1A^t%3)gF3CIkB?SbXp!ZL_=v zt!mZqFLT1cD(4qq_UIr$Z`Edi6W?c|na#fEcXbgL^u48)lgi0`%TRD=SBwNMhd!>_ zPkMk1pI3%o!`JZkVK-{$=VG5-&_&+1`duGz7kn?jzn_>=l8)|@}ycB zQSt_SlZeYn=Onx$tCUCn@0V0<){Y`|&)%P2^iX&sR1VmTAAtENNneoXNpF+Q3up5! z;DYtPP5Y{1MrMDYeK0N;QHQf5%r=8_!ae~nnEiu;dgb<}x7>QpE+b#e7;4v$BuMYv zJ8EsQYEumH*Skay|4Y`yJtbGZa|D~51$bYt~|3qySiz|w5x*!&0Ju^x^~k@nU5 z?Z3@f-)>%a#@Otz6XNQ*)sLD%WZIPZwl`oh9vda|2>}cn3t6jdFU!xAW*;MIV*nC{ z1~3NBxX&i(P%s;Yn}BN>31mK|+@q>Z*HWqGpOfAM{bz}~@(-hJ^4e86wd8BwwqZ)+ z2xSDd70{Rv8{=>Ez&qDrc3syXB!J?dORIeSLWqM78)h7=HhOTXWW^^D}V<+^t? zK{)W2t#NxynDa^o#T~#zmFi)CzwTlG=7_a@+wcmCaJQdF2atkf=Tl{D}@w z<{{Jg2;01>eYIIVYTJjJzY&vVwz$IXn$S-e3?djFcqcSE5egaw<5=sFca9Boql;E09fhE%eLi=j6#XZ23Rgx`SW-b2e`({hN__)*v zkOzMsVxWi0g9{r!LpL3*8oeWQWf!}pyCp{CAjd=tUOL$LG|#$7X|{%T`E%m_`=L}k z-AVXmPOc0jPGKr_`-#f(p909Wl~NI>wC#1+B&piJ2z=0YJ8 zVS0I)!GZ5<9|ZSmqht)9T?Uf5ePQ)x^IhN~F%mgmaiu8-(;aaxr#l}8!@X&1V5)y+ z_u~3{V6Ay&C@4_c$3!5A{ma2aGD(>D)z&`7uDiqt{OSo|$IQ*`H8xf1bKY1983?J( zG*d*at1D%KYW6bseg`EU!uIKcBxmMke)wYn|2KSo=hyElicKJ+g@humWiI%;R^0?h24scl!=mqS%@U^pRFJ#foy-f?OPxe^Ea}n2{@(Er#q6Q> z1MKxYoI=(i;}*Xcs$qubSJ`#eGd1PANe~4d_8jo0_V~o_Jl*i~q9dgWO`%sov{Ixv zxzs>4QmIvEu8@JXqX8fKM_0y>(ZR$FzGq(jENS>YF`v}(X)V25@a$OAU;JaOizQs< z>pZadP}n&l*$uR~Np46I2oc=09*o&KVwt`VYIPNEW&dN-Av7|tTUO#GTdjKrY4!i- zfD5vJ;DPn(Ai}OC3X{yg2#|uDW*k&zgsloLYRU>X)f+0z>^Gc#e`@@_IePCI*b3mp z*#P?vp}u6sWH28i|Ew+7rT}kc>3YT!N@lHd#!6R$RjM1MbD6t3Yr#3Xi7S_5v@?AF z@#xyUsdA;q_k($Y-tYDCsJUfZJ~!H)F_U~5J5 z_6dV|2=(H@V^uRuz<30^WK%wIPccaz+VVNUKtV{KES6{x6zKUS!5emZ)-heMoGetA z@T`(v#N~EVndpbhF1;}&6Ge6h_gzvhEt`l?SP}0A8wDi)^J+rK1=-l~Y34r-%Kj>$ zu06skq^Ictm1_NLvK!x++NLJvX1{CY-c5{fAMCRbu;%R>N?zmKd4j^&+@$3&Zdcd# zxDtkTE~kZ*{BugOZQ0MhftI(#U5jDS%fR#|qqg*dUQ4;of$(87g2omVVKWh_&1HLN zNFg&?djxLn*4&`k#p_*=7iA5S?rsOXQhCo4s?|!S@U;|SIwM^(PZ>qZEti&R6Hiw8Qfyg}9 zCOxc8I`4Y#cg36Rn>`8sI?5As00VwRqCrJ_U7EBlo+&`d&=v$(;a_2YT#*%;?hHx{ z5vmHafZ_hreDGnU49`yYwwV6rsPg=qcPCqF{b4DuLCr7rnK|Jsi-aOQVt zesW4TObESXP*&YG#WW!C+;7-?{C+J7h`pAT8g6izwM@qz;loA44#K&h>`DD~2_(7k zGvDX}r#JNjorD5#J+Q|#Jzl*LQn9?4DdK&sBKW4ZZl)L5;klM_-R3COt;ce{lz#1> zuUX~++AVf-{2*MQ>T;$!N8`aE5bCW3Y!-YQ!mD_F+>d$Q$k`l8Ji2%{8{h*OTxMJy ziP`|FrLfblfu06>U>#YZZt)*be`?S!?>`lD3gba|*x^Zs*c!eVrsnj#@-fR8L@;e! zyreR)gN;?hueu5cdL~EXGBEp6&o0uQIK8bA>uYLz0&FaP{ z413J*;YF@3R-QES`O2nWS>qbu77|l3Sy07Pg4fZ@8V#>~9J}uS*;9z_I!XN?^&Z&r z29Sd80OKmL^FNgjU+~$i{UJSo1w06z3w{>7%xV;Y^IGDV`(fdw{(WhVtGDjMNxqUI zCB^+t@i3XLH0Cv#%YBnfSg9{3w9nW8wzeK;aid@DjUgjReeDl79$t7ZyZGTBb`Jo( z_-D~Md=vD_O{188fTj>aG>^4ti@&!Mgqq)2dTvgm^uXh$A_!lgchc67b9Jr7J=iI| z`t1e~$36MKIPP^RXrUTI^VRtzK#-9D!nkv0qA&95wkGP4jp`pD+=gJbxvZlfM2;8L z+wsY2^X`$p@iOnA1a{79d|#BGx79|@29dS4Ib+tZT|&&+oeuKG4n3hdNnStN-##q8 z4Fp+uWuEu>{o6HM&Y$R+9FBm$9Mx0RuHL&=aj@a{v&-ed=T8)U2x@B>%EC;{f}PS| zExT>j#QN~TJRsaSxjn@>y|;U`-qb|oSlWFm40@`bJ>v9~6Hl@a4Kh~}XjA12XP12& z`pN)Fe^hgWV@$bWzL979cu}RgwCTt551nD)6wxh{RB4;qIdqG*mqb|2SBCLfdz>2G zwI2pExHy9wcS3aF0CetA3L~vV%~h7X4$UzNL?zcyUCeAgTRO;UvRk9l-6Ks^itsyx z{8g-L=|i0&i@R6)^LvWicj7vFrris<(z41HLyPb4OQGy!`gagr!US$4;v$?8Zf1N|k*lsqgm(VRrYCK#n+^ zWx&@i{wPFnMd^xjvfujkFx6YttaE`*>AyKPtt6I@0B9h+@%sn~Nu(u0M-^d7Tv@!x zku6PY=)+Z-F!B8TmT^O1?Mgq?`E3|LDXbH8|AoN5cY)0NAjA`xwpedEFB)NJ{FL8DnkU^k+%l=GK)(*P^BR*&$idc(GE zS=M)>pmfxdsg9!$!Yn&#RWU^|6Z&&%4W>}utUE|MU0EAt4HGS*#5va?k)zHd^Wc;> zD`!cm`M+q}9OF2)F=Bw{xXh_dY8YgInVi=s{~C@Lm0Mu7pOV#Xa;~-hOvQ8`jiTo6 zpu}W5A^m1{Nk)Y z2-RN>rUlEz?zNRe4!dAYVo)9KRnAVbR8z6woHVZ?%^K*}n_b_ow|uf%{qMvXm=lPw zH{v^3w+*M4$wN$cweIXYAudVIY~x+4`nSx)GMq%`7kD_%I=;G zM(f@`?*pLpr9UXPG}&a|=2&n1Mkn9ym3Kzvf(DoGzwDl^x6;l;0u;)GFV^|%HmAza zt9S~nrSh$9+t@AX2`YNw=@v<2H`cPJeA};+_^}7t@*$Y9>vyMD7YuZXH8!~$m#bvB z>B`M5F?5G(dAnhwCZZpH+8TF?UL^FM3q2|>{gLK!e8`|V zwJ!60RRG5zk*`$YZBU{wm{_B23|2j#we7l}8-xjCY}w^>8|~ly7~~75LkR$PJ#$iV3SAt$C zo>eO@1XSE$xUvlsL^Qvvv-`~ZD;qjs5+@b)Vrl8vuQ^+3l?D^dlR|2f=lN9q7MuKx z%@-*=adX@4@~_-jd!k-*xO%U zB=XM|^ns{vTE^J~0+Ehw)h~(0N8qT;{Or?hp}?FLMSi1_<)`!fNquAglD-Q;RjH#? zVlE@@E{8XFw=q-X(%Viz&8s1G=?gcKjy^MM>4n$m$o-ivurBDZx%I;K(wfCPB-hUD z@6jLVf)EVF+{1p!F{d5rw{=eC{U1^N5?x#V14+PJt8@GE1R08!cxbayu7K+oEmOZ2 z-md9Bx+X}>@QsMSv8LuF72wRx#%7L|mH+W45G8x^A)WEblD07rc_nG^(IDY)6(5noH(l+MMUrAjxyXN091|)|Y1A zCklFnx!yS~7SLQ!&Y%DN#kTJ3kgbXO<8Ma5U6%L2lHxTW25arIv_t{f<`e!mI#jZP z!veB>6o^@oDKt57%hqZGdb@q%&x?3VQoUsx4?=mccF&2+Es1o%^`l?J8jV$xFh+0# ziyAyQDqtK9Pk7i&S3v;nMC4Rv3(lL>BWN&Bvj*L|6r|2I-|63COvcP$)2*q3!mKZ)MX?=i zv7Lzujc$hM0m*I7<~d~mjX({uU?!9s5f+vKGq=4#CxO`Hw5s_^;=om4Yi zy}52vfnbu4t%VtITu04&CtnX}B3g^D=3X8+QWhdRn#J4A2e}>3cbs)urNx7+Ctikw ztodd~A-X0J8Lk(7?cNWC4R?{=V*ts+K?h{V6%~ zU3kCbWE8uF@Vj5yqa{Pm1?b`qzZu{8Hseug3n@E_I?hn+J9DsPwy22wP8I^c-cV5E z(sXUsdvD}JdSA-AnyvHL6&GD!2?k@f>e7>J*7HiPIQw(084;hL5^m?y-kC2uV9_k~ zt8}W>-zb)*)6L1(<=>bGOqWD(i9F_?E{SIhW4137xC$Gk?`?&=~j?z6J z&qOrpp>}h#gU`nIm)>Ep2NsLK6YDOo#vYaJ`=iS7Z;k>Fk7o#zvc0mVqVrJ)#tpcd zt+IwkBR&X0Cpb8$lWPKh`y8EL=aHRn{b>{SI{Q8tc9?1I*Hd$L)=s>$9z68jA3=U- zfr4!{*kwLqXG3My>gLW!Fl68YBymgP|4_^EEM|LN{;o7C(fO`?6syvZ(rH z4)O_0!!hvo5}X8s^r(&gw!94pSj~Pre>wgD4$VXv#k*Lwx|r>yj?(syqSs6*#yksE z3#3sIuG6Viwmpkvox|9LI}mU1aD1GLyJB=DSM*HKkx@rTB&WyMx?;R{Kp^`pN#+>7 z;@MNs?=!l!doXF2;rq+Kz+{{~_Z&y!>Q5mqiye%knwRVm;zP!9PAJ#KNf5WS?9rW* zh-WT$Ewfck4V#&9A|O_S^jtD8Im8rGwr%{YbJ%V|!YB(9TQit0HCQjsg=PIX62z7j zwAA_u+SANM(~!NDN3Zj~n8o!EJN{#%Y4kP6L~CRaB*D=3Qr;wY5k0QIZhpVdZF(6g zy4~|B>N)fWrJVfq-L1SVS`|liO;wZD@7J_kswHNFNHrj9c(1H`%|2%{`RZ757F2}^ z{3{W+7bYjdbi21fn3>#%stwZSm0+2r{fVQ|VL>q0>+q~6S6JZ!aE%4X+4G@i!cdOc zvHhXR<)gZimEuwn2GAh0H62){+x>KYVPb?s0VEI#u()kthdCiC@ru z4X^mDQ+;13om+ZO7#&@b91hkEe|vL)xAhW*BlAUmVy-+S5Z(}kw%1%B_|NJ)xr2iW5|y4&fzyD`pZd6PNI z@q@zO%%a$mplIdTh5IS$?m5S^RXSfSe5WUP_JhmBa()+N`S+O3PZ}>Z4}TPLyjR~= zCOx``t5}9JOJ!NaF|^6=O(x7SBPCsD%V38A_)L_ocA*o}6n&+Ol4IO2pCro-V5l!+YQm?`EMTYY8gU1^C6I%w6ow!FM(DvOVMH!`yQe zgG^}2UCQ8A+4x&Q#gE)4cQ$*TW;&1bJYgTnh|z9$+oh)15-AppZgfk?k9X>wCA}UL zz)pUMeg~PBM^@xnp6oY#K>NNysu(&wY>m>B7e-wQg$ekVo;>4bsXoso%zR+~wH(W3 zQouUdSr1i-|$tqS}a@7mHfdHrnLPV9e$jvQ0Ea<2I{%8YNJIi(?9ZF4rRqMa*_!wPSmhLjIMZpaY|x?+f*)d-A4 zos||U@A=|%%#oOM+{)37P$sXM>keTv1;g>r2Njl&e+jgl`2O0Z@X0}l6ad z`&&fjb$9SeBlQ~DZVtpzkJW2Q;v0?4LZM!5IEfPfRdJyoT!tH6kJ-HM>=2!Vo{;|# z=Nid`RipWUduj;I+ii)JW%j*#8ps$^;JL^(lZ2YMEU?dR4ZvsK_@ovlwV{txEI2q} zV?`iCE5q;Jkxq%bV+nD$7~f4V%ni1R{#2SVuM-?wmfst4FVSoMG4E# zn%4V8Y8be-DA28X$j%%7dia~~p0^kZrdqUmeT#mYMsRXN!xIFzxn&@+mCPww@2cBA z`?^7R9|iJn@t$d#o}QiLf{7(`jk_B9Ea)dr!w)6Wk+h#UJSU^h{`h6k14r(~{-oD@ zd!Q;nf}i8#XEC~NwYX}emLmM3xO8ZPqC*vvy{7KPuDv~Pe zdaxG|LIX=F8_XQ?u{Kw>Zc)X!3t1#_>p`iFe=sT4rxl6!GIN{v?~I#svD&LB*Ue!# z?)Fr5OGGOEF(z(0n-4@pn78quM$+?*N*vQ!b|bKMg_g@B{BC;@cs6w;Qv=G_V&rrF z{C9Ackg>>o{Wts)25)m~7hh;SxYHqMWjickQhwT~t;jP}W;(8#b3}EZ9EQNNe@8UY zKinKDv;DwMV_*dGa_T&p_9m8KV0%&IQ#y{>Alx6#j0kyb0zqY6J^uc$7b*m8 zF{bBBN(nsLVl69IW425m$KAU5;b*T;GsWnitBYeVzcvCK(4=b_#3Pw!6OvqC3a4O< z$fmP3M9y0o_BMrsN#Cx^fjW|)9iuW6l#-V_63*L@)HYHfnD$JFJA;%(Z69=oxT`Dn<@y!p&^6?xdM_c#Evby;9GO&Xv{Y_Br@u@P#B_fEgTz^k)2 z?PEdLD5dKqB&i6CQU5+fl5`-}*Y-6l?z7J5vAL$FI|*o@QD$G>xGsEo|NpT7MB!F- z95-~L>m`0p;|v&w3`0u%!4If-q##C)jB`htMbY9l(x7DM`1 zN**+zLO+OB-*E^d&5181a%au7mOnM$(ud#e!q%;&v1_x{O%4{AuU?aze=xJ{bF&XQ zBvPn#(T7s@Hh8z_TCX?g!}4*(RKOvVsrNRSXHZ=IvwAM z+}{}qS)NP!mGcTTs`d@#H~jY~t$&{R3a8boll!|?t+;p^K3L6GNN5*weQ`x<3ZKhf zZz*XYc+JXz-IE59ZehVjW3Ax0h zeLstzmzmzS+xH1ZCr3$h&pHXG>%gk=U0=CP396eHJiEWufUkudqHwYobA;-g-TzZ7 z#+qYcnG8&vn~KJ+$dc)t^exk+SSj_ttQhmPhf^-eoLPs-N z`4v;}o>UqoY9z)Lf*zdlO9px5MiGiFByborcB%4`UgviE6%vg_mwkA-tS4_YohZ1J z18YpNCr*Yesw}f@2U%lZhM5|gc*Pnt+c3sEfQ5u8s4llHHKNqN*)*1WH^7rE)1UVYAH=87!bHY1v<7F6p3PNbyzY0K0Rby zX7imHuD63L;0aq(gV_tiXDgft7q?kHHyd+*0j}xznBtXcYnKS>#gPM>gt+$&gWQBW zKKH6;CZK-lp@-|V_D&SobI$s6&d%m-waPxR%=@HQu4fSf1wovQo&qBu7+#Fdg|B|* zk;gJ(6N8XHo3A0#CzAllb}*2@fsZqTU(3uMSw24)m6kbQa%##cv z6_B#d@*$w$PRuuaN?bN84SnH5itQ?UL2-~j0Xt563Ezgx)~`MjkB;?2hZnGJC%uij82}KCpkvapx7PSeTB3J3EjV zQTCm7?Z?)0?hMy0pYDnl|3U7{_%8#QNn`z4A-GEZ61jrs5I)?BpHNMG+N8m21lv-$ zyFQ&K>MKCt;uN+9q~41(6f%>d2IO43xW;kL()aGYh=b9n+2c}YU3{?d;{ zWv=+@i3F9&Ji~4sS?r~GqUlaOpRbXVmSM`8=^M=%H|jNx8eHS|j|`p-6W%o$mp?t6 z;n8M`w1wug`swWtvxWK>-&F<|yHGa)|l ze&OoC97p+0#Od^w^zp(ct*x_p_^)Z;Tih8s3J3B_@8&lf%xHt?uq}scBkji0;7VmV zNOHYTR>l3Uza_%7-bzxWIcl5^oS|2eixqzXkg{^M~W(ea zX_2)t2%JffaLM{!Pb=XB_u9f*#OiIeIl+tD1wO;n>0dMKp7ki7Hc1cyI-bKye~Ct z?(##Zo11zNYk5DMX2go)QcYM0qa$~CNh+~2@Mhcib#T8y>4nVGU}it6^%euEhSRS* z8Y;01(TU#Z%iK&^$1m8MJ8$(E9S^<>5mxh zox8iV4ed-#C05ZvpH({8t`< z*W$K7W=AX$r}+&@*JN}ow9+-HF^z7Vljm{9sMN9>!I}dN3w(6BPL=4EgpQyJrzu2| z_E_a_S*Nl^tS7#=9y+teKuBN9$l3EhIx$+=D{+mU7G#Gb;{uO&3(n3gNv^3Bye^(M zOo^;K#_sdE12W4LQ{%=c9rm$XL=WCfhLhnG)7m@dD3#!6b@>tQre_YL1<+7iR48<( zY2YbVddix>aoX>iNn&RZsAuB(+QQ7+o%G0`-hE5N$n17fP}+gMi^&LSG;`8Zaj$CC z(_*RKTB9r58$Rz!@&BB}Y2Sln4`dISj`kZf7$$h2H=PgmnVMqdVz@0kVfc|=$OSxs z6=xE=!o(B)+LHKOIoMi%+?1A9Y3vAG`l8yYPQ~4*_)sZZy@*zAMTKIPlz7BGb?*p?=j)Hi!uX>wzAF8}rM|==e-pTwc_*!%>>T3(t=leTf*#i%uaLN`tAJ8J-XTkACMPBlP|HkXveOy`pamr6VQLBLbU>OUHc!kT zhd?_&rB-6&NYKd9t>Ya1Tsx6G4X}-Pmn*+4@`VKF^F;J`@pa29^plKDkm(5;Qv#`f0tLz)_ zTW`Uuk_3?3Bb6&(vGOzza4Nnj^g0H!a(It&BLT*(8+Y6zG1u${$+Y{m5vg)xW$S~8 z9Y;6hjOd{x#hC?U*+R_kMGZ~n_X~YN3o22gV+Q>o5*KbYt@m(Zw!zD$*M)Gjw8>oD z4q{tR=IN<0e_hZT2b&E;DSkBh$R+^mG2TY3kDUAKdznl$?5;sqDq3}+9yi+>yiGnl z!*W|jm{Kn~<_`#ffyTPs$jg9vt5$f(<|pIIA}KfGmh5DGa9M6sTlgXS?5li6$p#0s z4Wv?wcYf?%#D^#3D^kcp8=ne~Kl%kdDP=9YIrTzpZ6o#fieZsl4}wT{SeW`}Bu@_^ zE8F|f zuM3-JX0G)D%geEaPc&)~@tf4BKzDf z|9jy8C8Rqd`3+K?vQ+aVBHkoL$}P*I$Lq6j=;-^2%SUL72~#hFVc()4QtjOLEe?qM z1G*!w?bSEexHAMRTf!VI0)YZC_k9oG%ez7Rk#+NdKmEJFqh`Ka<`!>DI!F(!8dj5b zkxe>#po|B)ODpQXOWVy|^e1nQ`EA~(nKFnm;+ zjYlWrL9Vu(1hGn3l{W&ruSY1dC+2SnM{SomX4LC_!swfyZTH59K#?P$RYKp-W>Y`x zFi7xS>#Kf-7yJM8I~-ywz=6$bcwW$Fxij`%(N9_b^ypowkr_sG^ zDzNk6>{)=`7^^^xoGU93ckkc`D01=o8psZSM%eFwXnON2XKvB9FJ;k%Hh{-t`GX*0 z`5_Tk5D;nGpY}nue=>h=TSsy0mD>!Aw9QkHF{umkKfEzN?me$e`x&PGW{Y7qT%X`2 z`tPdUXLC4N^Bd@!mGeB7oy&wBI=yGT-mzY)dH-yzsynF}5H3XVm^7xgsMN`#FFV6s zxh0b5w?=r~5x0WLQmxb*U#dOXFFz#2aw&3K zXIqHo!^qOD2f2#u%XkFgOx@+{ulbAd3X5Ih;65TLc6x3ej^SXAliR(ch>H=Xi;wxM zK>h5y*40zlGdv$TUCIqsdRB$ecTHh7XkZ%m5Ildy6-;QRJIq!OK0QemRGYvr>psPF zA;|E^$|D5(r}W2)u7h)3^@!rWyC&nxWGZNIS*)L&HQ2oai#P>{%4ayBh%AG~beU^* z1`u4tRWv5#{#z1vTI9*Ck$5-rIyIXu7b)Bea=qFMd>47I#Ep#g2r|>A-=<5u2v+1; zmK`6&eC>7Hsf!e3;m+kEL~;ew#(WJ(-y1*shV5!O9&sw#|A+v!i6KZW7teM3$ui>hoGg+q9eu zWSi_iUQ_pBjycV>08MwaOxf!&ioh4K@opeqiy+Gn;}uW(6wi9jAy&q>gUu%W<4*S2 z6`NZSI_Z+=c)Sn`>JIFwOCu47Dg(HGT5(p-eK^V)KCw_htq2af;Bep$E=hqd7u zQQ;Qjwan4llg(L#oWfR|=%lm<62}smB-3AC0dn&V)Ee|TJEb&$yDA-0TXAo~gw)cv zY9orXKVIv!$PF%Amw9l*!&+8j|1KxR--!R5a9zxGmyE)b0^l<2JNLv{0W|X?@T{84 z`kcOPijD5H@kRhZFJAt%(+@J|%ZR>)`~7ar@?-|zgvb26tcUxJkn^yP|G~iu`8{MD zScMRS`LvBD=?8~h{(-y9`;xkVH7kIq3RAdD6&Z8#WKK01gy|ZkEoGm*h?1HQ7(}lM zBN$GAkNH^aChr-9+p*lQvo94k^-Xo`a3Om4NfyVa8=R3ozyWQ#`bXZcowIYbu^+e+ z4TIjSn!f3(In@jbT%H~b_C*KI1l6*zBBxoah4IAMk=BO#1=?^xpX?0y5d5@%3k43Q z>;KCbohmF-we$chWez2C81P+qy*L_vm7!{H_$mkF``sRKlWf8fC3ui~Gh)s-sCOYh z)StZ%mCAa@+#@~KIv9PXvGa2_^U*|*{@YH7I&6h&koM)|Db0!ChaG-gz*T;;4Eh0V zGJ=c0{$zdETz{dPpy&(1G-nLfd?U1O+AA4R7mznBkKgv8&V1naz|Bri?JL8ev7{*b|@tfxVM3UrJ|j>$#=B}U(ffphq?xP zFT6I$j^li7BT)WMMZ-&o_3HnmuDp%q>B>BU-Pq&@8l~)nT8^P(KEs+VE3C69d@&DO zsI%z+Rv}%Hjk?M7srL=Ci6DQR!u*rP+67LJ`{h+eE$98qQrZD5>3UsYquF(f|55lT z-5U_=ky>JH`gF?&O2YjCU9})iria6|Z=c5B;`sQYi28wxTMm&`V{{Z`;D3)|x@7YV zGJARfYfx-fucJCZNG_mK!hy82HwSM4L(u}5C zp1Z^P1a(Jj>fBf)XmkBHNbzwElsAkYVCx(?HI|e1PV!d&N~u^dcd#V8V?NcCZGuK? zn{ZUjrb*xF;opK(&GKIoYpgds0J#HGEhx-!UW*!SjpPI~N)0%VPR?fspGa6gwOubM zo3V%rc55KR&X3`RQpabDwk$r1%(?$M@cU;_(}|QQ!7eOBVLjPw9^w4xomSUct1yKl z&BYmCl|%h&H`^HZVYU1DR{Ps09~Yj)4lqn115B+Zu7gB_wR0c+lU9;{Hi{e6TpwQ1h5j zK@d^LiLs}%-c{R`qrRRv6W)N{*5EMCU^O^$!!>wc#xCP7T5{fay4u9N&*o1{xV{9I-7W66xQBJs7ZHfMZ3vKDo_r&Wt64J%R6MOxJ=derIb3t7aIY`OA3<*sg*~3fArjDPuFn|EUR_Alr?1MGr8pB8< z70XwHB^iBsrgK!i(jy2?;i36%UPWAP1st`zOH(i4{G8}~-0d;OTqx~kqo($_j2Br!u5d z{w9T7*EfWyL51CCSA-Qk|3(DDAKusd)^$Y@+c3603RRq!_$Ys%p8YB5d02Mh!z6pb z$fG%Ml6hq_dX)@2>p*3d4CQO5|NRlUojoi(@X?r?-xVF%;72`Y)JjqnO^K3P<~w+I zo;B@7;8^`M8h3cLK`7TJx8;1hQIjqhDO3;7@gXnmpX&xV*ZmXpnP|gZ`4sSm5bvs#Mi2Fv4Md(QtrrWxdm=oT&66{HwoZhIf z2k0Jo&}`)v(QsxtlB?b%E!An}hL9PmV;+cvd(=$g#H)@U9TfKtdL%>*KZV}O3M8rq zL%HGlvE?RliYMm6Wtos!!vQ(y50VMGkZ|<40KASx*|p!NL#K_mq-#iuzV#N4n@FBW zzaqHR8YvX0ux$%h^qDUaazLAEJiIkNzej_c_{K>LNcCREF(F1~R zf2%+W!EXo5^Jrw3{A*)6V_ha2M_U$Lp$pYk6G5;59C%(TYsm41R01gd8OzMesQMoF|;_^q=}V z<57nm3q`nAjK^XX@^83@)zaDC>xX=i-TwK!v%^_j>gCcZP-bDri+27Ci_de&GFYk7 z#=kFFyHF5nA2-R~gLb+}H^@fg#3t~!jF;fDr9CRQ>jvR-HJ@+(poKqha9aW@^9WFx z!N%1_%zpdSFYm+Ml%NJd%|jpqFZXi{l+J+@c`gb%nmcz_M3h4`u=v86gSvi}FCVPq zHt#pn6~hLYmo3^sGp!Ilq~5vE?LD15lRPXt1*R($XrdGX|IFBCtiBX39HU;P5yBNQ9jw!Jm@FnH%Kx7tep|pg?XYkwhh_PeEXaFhxZtE?!~b{I z9Aa8b6CxQ@)G$Xv|4g30+gX{&#kLHESn%)3=)39-ltAC2Mx;~-8D`J8$+G;;~KMLhSwThesJP1Y9t;Vf|j{XmO zZyifV6;gNtdJoBGTO;xoM=?l$1z{beD8&iA{%qba!t`x*In2dpDnR z&p7wo0Qb+D4t8jiyfZs%yd(DU(pEgpB|U9 znMKnBgeuZ8UFTuxoZXD9dZ+tV$%;+4hO^+j^JlEXDY-%$hlj6^@LNE_t0AN{w;7*m z*8|9BY#5TP>Kl0tv;F)T>&q7ldio=Q#-Py>_omG(uZafu`AkK!Ks7WTw-wYeQ0=sT zwjur_i{fOV@2#=g5^9K_FH*|+WMhX~8N5LEcha8MSM{*?fgK$S*o7SF!=5$)PFO#OnTYD=JrxI+Vk_#U2T*VxW+vtCCrWxE2F@GV0O zppcm*a;X$9aOx-mii=VEn$r8ry(gCCmxkLD{D+e`bd%UyyjG1Hfo11QAzhag;fKiL zJQV8BK2&Y|jA!~?yI-$t@|j$FX0EhJ;O%tX-lLn42CP%-$}ZHhc4DR;wx%oEcCD}T z>cgHu(m^6N-u5?`q6dgXK6#VkdpXW{P-}YCnAAU-hBOf|wFWk=c;aM4a;07F)yl={ zIx~m>SIk=RT6mXsRBF2}6)3J|>$MqH&gZzj;$J7%v?^8OafdYl2Q5D(JDa-W8t(fb zDxMzdkMW<{$fXItcAn3*MJ)$FM^a78o}WGT@;A^+*R%e44rrAt8&51m#g23rO0(?J zpo{{p44oGl2Z}eb%i+6QkgXSc%>TQ*g?m2u?*c?59t7b&yzP3pkib1EwCE7@k3 z8%*}i_{ioI;9F0ZqusNU_em9_O%xsRY_TgpvgA%`YR+2CRy`Mh)Xn*&3QqbG+LbXT@JYgN8BDC@$v63WL>u&v-@_-hMHT16P@3*SFjH>><8a|iVmyD z^S2Xt<~HlgoV#8A!s94<-f$+; z#TWN*w}$fdU`CYFXTgb5x%iDE!;>-f*nH*Oo)G04?M~0ao%iEB3*m2Y95l?fXdKvSR|aE!!q4^}0v zZ%v`^W#hwTH8~ifA6m*ISd&e(`70w_pW9qv{l|n3wM*Zm?uM+`H-v{+q3n{UuIp86Y#od6*Z%q_`vd%`5OQ_?XXuxyQZ`S?SJ&sBuxBwj2=eSn0F+tr z0W>y|y$fkUhR zyIABCtFzsOYgT0HNL_T1AyXC0PqbojZR?rLQowGKr4?R?WR|VNnlm5TBCl_L4^)SVd5iouRx?s$@ws4*TMaN% ze>+yPf6-3nfRYq`6;N zu8H2P0XcwrmwlLZ&4C4_X#@GRph!k zGw-UM#sD3UW>90cRNY(yTL`89h(}a%RHM=ChmuGGu#t{&=U2M4s3gOGXFYu6*5YgU?T_&Pp`n%OtRIxZ8={&=^0TZ)l=u zr?MQoeMd4T>lsOR5;(q);((tucU1u`0|W7$4sIZFHsKL{ttveo!Yz-t!>~6eoKN+> z9apGRRj7|qRCk=nnnMbbsHY3}P5I}+ovQ>Vvz3=8>NB-6Y_H~yw|MP%!4xX^hb4xP z9UXB#A#=*TH@ncN-+CDR5hlrw!T38Pc@zF-ePBnOL9%zPcUJ{D*0W%bLz+*HSC5w9medulBtUa=ZoLz>EeWQ5sRPJ4dk zBt;m67JOJaci@w3gk=NU?0X}Uj*Zb=hAgsKcEVBFNHbxT-}$>#QO_1#MLL%?W1&it|JqjABD|(>(}9b?jmIBo zP`uqdW!Mz6JQ{=0ppsZ}oL1NB(z!bN8pNek;?LZFgoT7?j$^-RX|RHm+5b2e`h73S zq#dHATYV4e;C+Ed9q^uf#^WO39`ZZ+pXg8= zpmu$E5S7zae}VDcGl^h0&(JTC|LyaMns%gV{is}-*G63#txs4UfMv=pAcaa8_f#L} z=8TSU1f&?0nHA|vY{)DpTD~Yx&fc}HoVKB0s}VjM9hTJ?d^G$plx8v_3`5zn^!+kO z|E+`=qTa4i7HVAs$AmMh_Y{rO^A~4)H|RAV z%*34Ag^oqx`AREXaD$YMVka$sMr4A_GV#HS20g3PGok(XS5t~L%oc`mbDuSgN{&nC8insJYi9nK@om!gyiS?DUHBQgHP=3L zoSW(qT~rEZbdI`BTGi2EA3UA0rXftxzZSOQRK6eWWuEiCj)<31og^dak}_pHF1+>X zst2OikY_NVHCyI3x-t)(Y+50R6E0!Fs1s@3r0kjulyy0_H-j4~-3lGz3~*=95uPON zag&aROOm~=+R~UBn}88a>p_}eS|7W=NSS!PPv1Z%vmBAuho*V}eW@nL4|XOEzPWdf z#f6bPka3619JK%Fa@iwI z#stU!z{qS4A<)BX&i2@&Od7Ck-)yW^y#;1M?$F+a!geqdBVIIySbj#n~DKr|j$XpSiykIbBx64?vP|tm4!p z$Iue#sP~pd(r}r|m!PF-F768t#$zFoE~m)7YJVZ`>#;K>=awp3%Q?x4o_m<}ylENnHHpFj;9m zUT!~G^Rp=blg<<7Vc{+tuzHiA)FT~Tmsu&Yp_!4x>NTy<%Ni;mI$(g_)avT;+>~LJ zSPb@O%IWx{kOkeGM-9~LHs8PcaSpXk(h?gK%hhbRb&)c{{qB%U^P0|ixxa9NvIFkt zC!fIP>L7csZN=m)AEO-bw34%9gZ zve$GcP`vpfxAM1^*fodrj!oqHblZ+)qPi(i4jZjUI6k5#I6r)85S_Pa-ts97BW8Uu zv3nP<2&u9lnAA>G1?$FWp#cNJguzh1!X>RoRHYO3s_&ryM*W#T{bCp_PQWfZlZI?^ z_$-%e!Uqd}bFiillVVn)N$q*tm!+5`nUAwqFSS?H<)cIN3cha*yL|mJ)mv^gR2FeQU z3*P)}sjoffS7kLMo`ollcD^G^e*L(wUmhq5in}b*0*4%#4>dCzx<6}~WOtpBmpwgx zFnF*`7c5NZc$K3Z>{hANXbI%9j0d>8Y@b-VHG8xOXAbefZ88SjQV1>0g{{WV)+()A z+X9+jS$=GwO_gu+7>E!&`w%AMPu2=e%x!Vvc6@^Wt)f8@G?f}E%HIp3V*)uBbw%CA zrkwFj&SV@t0y6JRLvtpIno_y0+d8vpm*VAXS7$qEP2e89KCA>@vn(%w~fhyfFqDp5uQgaJphJZApTMA$5 zIjr`_JyOmA*S#~u@7MAhc~EINCr-@E7=~ZFP&2btFuO^uAvaEm`0vm8KPh2;rDId< z`^6k9nrZq@sszX99*|z5zdwB;{^%Py5)M87{pmub$W^`=(e!xn>URr~$E4lYLi#9Q z{64>8k|JiKP^1_@oH(cf=0`UukMldFXw!*4r$9OrIgg(;dBth25HkFb1cHK$CVCI4 zjmlt6)-oGMPB(29e48a-ybI43lzMUX5pz#?o`` zd4;T)4Op`>H(=wv$6F5z!c^k8ORx{)(!eiW^poWK|7%NDy^B@uGt)uwiWbrI(GN0vpEAN2@0 zMo6+Bc9!wYkIr5^R8JZzpu*WyUTjlYI8<|cy(RA zSXC_6;;YFd%Q2u`osX~$m!GlUWTb*ODM58_NW4=}5bq0k^5ZB6s)S}H53A*dZe3ha z`Sg5uzLA)CrY?;4Dz1Rjw0Gaxrus(U;}K?@spMl#aG!>oV21{{Tue5Aao!|WultAw z;?>i1dGoMNJN(u5bN=nY2iHyn&jIDDaD1wMXlpI7_mQB5q2r79z{{miZsbZ%QPaKE zEY`;Fw+N~8?%CwnHx=wEaC3oX6{cAm&F5&Rx6pwG6^r;+h-U!bcxNyF{kNlJNWgWz zg~vNC!IS)pt9?m5rb$-MnTo56^7g6*Iy_i_^Ff0T%bX{;e*>NJ%)v}DBFY4ei9dnP zvz3L{5of|yq}*_fTb;|lG3~!eqwP7ucAz*V&z^T0QGO!gH0`bw+-&T3 zDLYn5;i!}x(c!6jb=dzxD!9y-cx$xwC zeR=5Ptn^xS(r=MDo>&=Hz7$IaKXfL*+p(ifx+w#Rj&w~*xA)0>T|tvJF-Fy)Yzm~6&~!XB+FVS+IvjCJl0x07KP4{G3r`+m3yTz7vBsn z&@DaV8cank%p zw9{JPTrL(=jOfp+92G{gfNmwM1$JLjgnrxWxLN$_z8jA6g>h;+>p2H8N{o*3unp% zvuYgHq_ZS3drEYkq5-BO3Jkgwa+T2x7mrZY_O}a2HM5qsg)sTsL9^TG>HesTPlvQW z44RP!u0aj%K{4HRIrLReW*iJ%3r{Ct@m`vxVY}*W+(1fh;+)B`o32d|_zP zXn!)^e=zCMk4Vn2r<6J@khj6WjtBk*>L)tuSLK{6=EQ*85~FU7uo@k=F^ud_z)Ju7 zCisWv{Oz~zq<;1%QTa2GIfNq+>mHI*JmP6QhGJ;ydQcO@vR72uqEY|Vminis(UaX5 zv<=27aqQ)Pa`)m4e-0vSZfXc6+4)j)jCe}whlMEDBssR?%6CZkz8DI9(bYH8Qig ztQ4Af@EfRlED*?vui23N^#8`-=U@GP0=YNitM8!UYQ+bjkpfLMEA_!xD5t4k5*DS- zV|wSmkZX?G07V9>i^l7@N}6DLqg7?W*q6jp0>;-gHM2sz&_s>>>BD~qsvmtKDqro4 z&M*INQrjYkwJbX#-fU>$iW`b@J|iDErZ_f z@m7&ds)ksEU~MARfR?_0L$Cz*?>lJ-cpW+>*shk&u)yhbY!eW9gZB+<{qfrcs$unBY?@Ta9mFvZLvF@gC6PMkfnkW(m1isNIRyV?{Hc`CVk05 z>Ng3(GY(+|DZ%Yw#3~+SvCEI{d_Vs*PAh&2TEr(Ob*ZYjB4N+`fDL6Cu-g ztw~w`=ynkw{I8T567GXOKWg3s*h{^5h1Ix*R(wAoMlN%~OZ9zC3j-T@b_;dGg_}jrmF7HDE%FvS_*Zd1RQ- z$Gn#P^#aOi$@Kq*~93dt<%&?5u7tQz;G+%6&5rWlZyt-58J>h(tRtqKn2AtxHl>u#UmM6332l+FF_>TM-{5k9QPFcEm87%C4$V3OXPduLv)(B~ z<<)%vA@$pmpZ}Y>A-WG>Vvf0ufWIB66PNJLRvA<8iB|_hKFHzx!F2y=mEEB}2-xBJ zC)l}1sne#;PWSKdZ$PIel0We)vKbN)hDZGI7f?q5Xl5{-4^GKaVLYDyzhTwDzvrL^ zHqh|XzJCG>Q8e`f)!s*cGqyiy_PCtol!gIz| zPsDkD2^WH2;s8{KriG4YdD{ncSJl3n~Rw zsOahd-QBCNEFWplI9{+qWmpwvh&F=?M4K?R#&)r==jH zxuM~tIO{HtL<|q`+Ml9SFYUJXFdXlY}6`1qm1yU2H_4ZPcO_iA8UdxWI11HnA!HC4t-(fK#0UJcIC@Dmv$e4F2N z`aOAU?eGqmVd05MfJyA}1mQ}{cZQMQd-)>`RLmp!y0N12#p^;B!*yP`>bmv}8;uv! zHv$+4-B~;U)CkRprKi3#yZ`CN0b5G3@lHwKC9fERxO^|X&6~GFCME?~<J61ki@r5udRFG+ju=u-bkEvJ9 zJ7_wqWbPHGY^?cQfy(Fij>#{?`TE|nbg;k*YPih5TDQL(`V@qyM;PEf5k;#)TXZo0 zbEHW_`x1Ogs_2s8)XY*KVL6voKr#u8Fq-LeyKaHJ^l}rU!$rUm2PFa|%JONxQ$Da&Ep!Ic}riX!y=d=WhE>zLzErLF46t3(@H~y~vGLAuHYOlzG^XJQQmg zU=GImbNocUe4G+-`Lq!rVY7(_n3mJ`On%uve_r6vBF%CkEEcV37+-s4knE3-G;?`w6g+SXnwWqYdj;RD&S_iGzt%RvB&Y0 ziypC4vD=#gGLBhauj^~Ri_J^@hHGuxaa97&@o7o#ldLQF<(mY)3yOiy4Yew|W?}fo z`Qg?LuBY<@P1iJO9>Qv-iT&Hr-Z?XU!}+hFVEMkn%T-J844qsmqpj!5c5S^j4T!eq zipkZv4N>K!t1M_NGoSFRN`x+9@ zew<*dGsV)^+^KdCoa26n=!46rlg!)8lkZ{_t#cgQFulH(|qg>!PGdjc{)w%rb7yUDF}P zB)jJ@<&}(YT7u<5T0W=j^#t#VIdDQ`L@ZXjz)!XvQE2_70whSXTmGu8|7+-nCfsntlN&Eh{}Uw9%ES5T7ICnP$EG$)nS6ooD6y(QVD zVKe$8gVf6a+ja)KU3RO-)?pVr9M}@#9-2RPW6@IR7k5x zG=Lh9cTdObhznij#EW2aL$?yPP3XfaqUNpOG!G+WtNEF+z^&?Tn&s4|FeA z0U1bFP)m2YR$TVV5u|WHf@PNbrAe|Nn=)4}!Te}#0!gO7T}j(3Ua!$ICWR3`Mkk?uvwUr{d#>JX~wI#&tu}Og*TUE~T1>h>ZDjTmXK)yu<~N z`oDztPv9>qfn)GQ0Q^Kiwhe)WAPu}b!c>F%cTpB$(eFTaa|&^R@mK8u8$m82N8Qj0 zd&Sxmqfrre&6G;g-~ zD+OzAx#c_)_6gYyuI0VP+bHV#-_``S3=g*}1FE)f9PUIZn^mIfG9#z!GtO%xY1W;z z=S{g8IEI;2(o|4nIh$<}e|5&?wzDI@!UibLFK6E9QnudYlIs(gtVyC(}u+>uCYv|<(uN+#gRpa@p8Cz}e1P{*#UVMxK?NMpv9HOCx2#(bm z{rYP-n>p@aG2tA<&C0B3qPTkd8_ccXay<8Q`0;?)Torlktut}Vo!PP@l%a@=`)6eo z)BSwmc>2OI@m&BwnudSSA~K<$KHjm{zdNRhx;wk3ASV32>8sqjsT&(v%+u%WK%}~m z0Mu;!BCwB~3uB;-;a?f({k7*c9xJn}zRcqmfWFJkkSNa?2B+uHro$2@X^AFHY4{L)5e?&U#D2#fmodlOnUC5whyb*c;)xbn&v!f%U7!m0Td;^dlwjoJJ6i&1%t9j$Yx> zcsx*vxpI;e+K8XRx=o0$No_DM58;De-#XRQa0wLfu$6Z&n5^TY7d}q+mIo}Ur_K*){Fp%Pu*016C!co|0zUB#Fb9tRmpVO1gdUb zr{q>qa*oG`00YML&9!P(rvN)eDlVa~!5QSwt_oDH!J{zAK@$nXwx=z{mBYhjC#JgP z{nnirwwDC+zG{6|zaOFkzzq;6wG+}49qS)!^F<(|jq<%pXNTVqt#t4r6#M#G&}m*2 z;Y^xR9Wwx~yi2_QtwXlBn9ONkjz}5Kqom*m?=ICAG

zt8%cpZICTj2YUYhrHRQ*ZwGl&io>x^RM{Ktl0CdtOgntrL^k&yWU z2~K`Jfk4YntwNSH7D8pTD%mP0;~0E_%awAB>?bYx zmjhB^X#K$xOU%VnW%)m?MhQhaApoy0E}8Odo(HxoDsb-eeIm!0pnq+!YFw$};tqCG z^gttgXr^J6v8S7W1hTWC3{ny?AQR~^7lU1h{uTJykE)n>wUbh+QU`VXCu4vTW-5Jqz>g@C~ zcJ!2oV9W{_0*4?T2x2f7b8^|8l=b^E&FiePPOXhm8t915(ux+GtoE^<{;o)XQD+Fm za3r|18o-ISnM_SQBMIBerUsyQQ!3P8FAc<5Uw;O(=iC@9fqEw|_{J*s6d10o2=-Lo zYIbN#qOBPMmzF(OXa&Gw?4?23pDWG^lF0G?{i*(P%0l~0q58z*zjP~*S9=qvfu=C= z+M{N5~FbT5+$7T#a?%Qq> z#M;H=y7t1#XsP(al4agg%Gr2Z??_(Q<*dja;DxAC;zGR^3bd7+05tkp zUci$|fdhaKH7)y8O~zvCI#IV=VzOg5pK3){!QAu%8#!8LTqZMwOfFVpABCxt@b>e< zKc_ZCL;^P#w=&&4IGZ&`w(pr}S3c;z>Y!L&X?#c#rCDz3wv{625`9bV_BJeSV8=y> z{sFnCxIl$1-CWf!Z3l&eMi>R>?!9R*U9U0O!|9+Zj!bK#Zs!A(Tr;a4m+aYo{`sSz zcOCzrn!GJmtG|4vyTXK!dB>@>@ch;j0T_+s^Y2tS z2J5$-cprf~M%9pA9H52?}fL$@6CFR52 z#_Xg~?QlGu8k5PK8G*9k(gK3VC1`dO%hS*X`vc;~*w{oivtQMiiXzr)8uBcaNp{z7 zGpq8bMDZWg8t%@z9q!|`nuK4BeN{2$`yEiV0s&$}kh@iBYEgxbltItCKt97ywq*7is(7e6wnN}R z)Z4`gsb4djacSE5-qp0Lx4hXVZ$Kbff0^KMa?NL}eAZXnUBJY$vujEOV6P}MKm;zFDy`TwoGXX2|Oae-B}neZ9Ih{IpEvYB)OoseMQrQ48gGA#iw}Q&~q^CxO>k1&+<;C}_BFpp?VyBlIEviW-)9HE@7lr~Q_@xv)J- zew}W{x-MIVMdLY(78j7dB*s^1SaI)1Nl|(GA@%-LVjRx<$fWK_eFiv_{eXh{Ai*P6 z;8Hb28pU15sV$$C-&&&&?HkX$ofBv_FJtcab%FVA46oz`!K_NX^Prx#43IhCT&khs zn#&L`g@J9UPqx@9ta8MwfXvril|`l1+G*Jl4+;Dt&u6>w6U24PJ=yEw&iYc#XBsRJa?va_VLXj3y>{a$xY zAL58GfV5@0w=*9%2g%CZgCBnbd}WC}*J)^Q`hh zN8h}ybTvlBs~_RfcBNjPoOG+;nJ{!X^ym3d6-OTu8O&THdI{5tDHoZEGMr~Go_3sn zIcyo^&DNb6FT0e}rW1=)Pv-MNw05il$qrz@ufLZ7mG`!epvDyC2XNunFK+>QhKo$l3j%v z)>~?2EoCyqycs7Z*CkADGfT+oHbn1O{K4L%yoU#C^I(%vZn$bhSA(y~Zdm zUK53_kKOvOlW}Z-op)GmzhSY}F`iOeLuNL+RKU$w2LVO34_gitbG(VNKUpHXn)gOq z)FDP>Ge8J7E)#nGzp!z(PfG8_=jU%3@;QPkr#?_G`@RHYjSiPbjn(Rn;9c8C4OYIA z_L6C4Q|Euz)fbzsab@Kda_9ogC?T;lBXpC{0JW{GG}e_wp@#EvR$1}RJEuM7^5w&P zxP5lJlkzb7Y1?=Q+x}aOAmgw59_WXZ{L*S4`x9a+WUw%v#3qkW@mKp8e|`R(pIH+J z7Y8@y1rj3 zT-;-|zwAd|sdNZdnRGF63}`(aw#((RNus^B?KsG$0a_)uI~v!WE?slrSJK~$WMAAL zCpSQCLTla*H0V%&Kg+6LBYBBUMmMg70=1!^IbZ4^644!8?XNp=7RGNmNY~h|)I|HS zD=C?~;>e>AKB!ixWZ%t@Sv^(fh)Wz&&8k~tI3OFNikmNUG%2lX!9veT{EXpU%A=BF z_IMWmMDhSZdO2{MJgG3M0Q>&MENbAu3FC5K6^5sch#D)*W#J|Ylt zncu>=Jt3(YIo3QfnIQ8+%J-Zhz|ucD8q+sFtf(n#v}?W^W#?14(85@(z8Ue;AB^je zALgd?>P{6Rv9GA13OJI{=5{o=vRizzBVrft)#x5ZKI{E9fz#}%_^^&jxw}O7w>CBT zALd;a?&s@~+r{9x(`X%Jz0yY4P?udv4Dm#+0@<36t3RAGbir7|Qx0n;Zds_%UxE(r zz{{(^wE!yxujKcpSSH1pJCT~3Zr~xX&THbaeBGE5{U+%ds?%;XEAJa32^5Ln^++gh zqB~kpKJ=cpBNdb3Uasf%sQ=Q~QzT&D`yhd#cv7&}SxM2W`yM!{8Vc);i?dJxcbHzC zqCck8nHr`s55n|rrp*XXk=Xesg^-+VlZ2JQC67pzK9Q0 z$I|x#n3*ItCDA?YaeIcUiU*Q=fTX0g;L5C|!lqHL6kB>bWJIUp871iy4P3;G@h*iD1@ldbnpHRJju3iSR3r4d8LEjDu!m(FwFi+zuv>I5x$Ew=Y5_OMu+j^ z@QY&#X+!8pia>3VggL`Enrz*NO_jK&4bcKVjIj)FA;!{sc}xNym%#AdS^2|TPeZ=3 zVx?~%jeFk4dDqAaV!yc^>R~~4QDu6{uERWRqsF4||1l)be0*YcuET#}JC)f+E%%kl z+qm}W05zLeq7iS1)f)w%1(ZD9zq-v}!Dyg|-`~nvC#k8eSXOz;k*fBMF_ojKPgbiD zVVn(wr{p*gmT2`Rk?DCc#NbfIrn7w5ZW6lItt=&Iv8Ti!J_~x9k4-KVDSQ>hBMUUJ zN!XUb>mM6te7u^draC8wA3hfHdU8$PAgm0pP*RDzSs8UNtw<>j+4b1Zi(hUlEbENa zP(1q#SpGdE0l@SfiGPUQLl*th&vP%lm&-T;w3NwSKWqch!qvG-$d+a<(Vc8ef%^-( z24MBQtTUBHY_iF(v>PieR?xXDTyF`1NrwIyB!d2GMSAjP{gN2nMS>M_g#{Dk)u5Kk za&ur1-pL1yI`xR~(Rz?jNLh0$?O;Z0hw)TzzEPfQX$!%LeCn(0O((5_GFGw~g(R1x z0<%_g^2vab`DzA=35e#40Q0Ls+n@B5)dCDXB6HVYaI$G)cy6O1qoUI>OtHCDOwl=t zM!9(cb9p+Yq+z7v1LALTC(6-mxBWrxk!Ade)A5lRqh(I&nrc~E62!vaBMMwhU*WIa z;?@ag2cB5IqolY@1iFeDl{Q$f)LjWC%Wz9YOWjk{^l)2*T+f5tgWftY%T>6bAR`Zs zo=GVHhlobbVD)a=Irl5qAaTVyr}wXU)hq5PvAMLJt|aTaHBAYdZfA@;9InlK88O@2 zV>?Ma2Cm;OZLxv5Eq`Dq4N2!aRIjbbPYIqs-YpKWv%MnO*@0bm{K^;6d=tk$ClgHz z%`Q{lYn}EwK4-Y5RGDP>MwdOBuVZUAC$9s(;XcHjt2y)0cppzQ(&MZ9u_|XjF6j7d z{{SzaXN`Llm?7x=Yg==&6>CbiN<$%EI8!zIb1!*fm0iKP1XyZd}ZQrT;4zTFmFPhR3y4P&QnIH&SG7h zuR(V8J&}nc4>jYniPE>A-R+l4Dn;Y!=k+ud?tQc&ALOz?K_j8TL)7B=@3Pk-@m#iN z*^nJMO`NuV(vCuv9T^o`Z##z|^p5))%Zie@7-{%9sZ$G`PAoV0iKY9Y)bT`dOyYcU zg2Y5_^X61*F0X?ieE50xV-W2W)Z;C~R|S_r#C4PWd7$m*H6h{wX6qvw#(=wf|E9*Vp*`Exi|;sV>$1*q|(pRNk$%14|-P# zqn=@j^MUn~B6>(4EqYvOIbz?*`Ku3_C|aBk0f{oopB>NJ{X56=0vND**!Q&gAhW}S zGP%Exf{M@fFa-1V9)F){)2FQUbM^VWMHPDWV2vNDnXZN*oo3z5;f5^!J#oC)VFb9n zdR?&+?he0M!>3KrmZpQvtx_aZc&_2btW3NssPg(pUKi&M%{w=3ZXExj!$uX zOXRtIDBDcU)8DDkt!A^GNe#D>g*^nG5zX}Q2*twp`l%2(v) z{;i+?mz{AB>CtB&qR;22{&w*DjZ;*7dUMt8K@f+dIJEwxs}DU*Tm+Gr&p5)ps_nW= z-vXypm1~h>n6$rfe3v&oVGqMM$z9Uxv9=oBv1?o z$_oB==z1dY=G{OfRRHLN>OlM12R*X3tI(b}@oN1&iD8b&@zJRmdPSV)SsD0fv_A@av*_XxV{+wtDuAg!G-%PsVqxsxJd!Yc} z6U|^iN5%i*r=GmH&xv)g^JtL(5+rxMj*Uyo=BV@P*IC>`Gfd>M?LqlORZ}bgS67JHTub3@1ah|^AFEbkn$u7oN)Z(!f6{^!jj|fE#OL9?&>=T};l16KM(p4}A z#vYD87|MUkq|PjF^oENcOc+saZ5I^GTxH=_{m!%I!#Qbil!)6Hl7y5z;AYD?wG*S4`LA6rmrpHV1Yy_2M_FdaAC>2*xu>E#J_ z!T(UBRhCGdJXUm85{z19zw@Cxn!yjIGKFAR6p*lRi!~X_*$5F`+)$(Nuayp4j6}uq zmXNagPPDIwNHOkb=ZaTLE4PArD(4PkGCb>){oJ{aCzvkbVcMgwew2HJ z>kZHlEQ#BqFM0@>ap!B816pY3vi7vi(=C2w()W!$hn9Q-Y>B;3C!+h<%+3#|e_6iu z8tb{gLmH75pc3GxTI!xS-(@0Kr0!5JL9#T%O(R0$4vr{xZWD7x!@!w zjhNJ?LYpwNO{J3>fgher3wZqIBYQeDL`A(fQ~%0pR-r&)mnGyQaCXYV)w@8e##f@@ zU{LDK5y$Y4UDypf3i9`8RR>|6cZI?*`km$n?usc|s-d(zXB*|KBk-^plZ(m72YE_u zABP)VH#nesliJhnFUocVPn9NrPnCIGcVI}s$50&C_u0D>mhmY^6PbFNgq#B1kBO{$?U(t&J{b$OM|l^Vs6Gp|h4- z?uy(d)dinXd;^Kqzdbz5YTZULD`aLow>@vdfWju^7Bv^C7`En&s$7L`B$@tQPqW#NPS< zC??|*63*avr@X&Z_)ovS&_FUJ71zdCd{*gYw};KHqa*Zdq9%dH^~YI6tM=9Q30V>l zpnxR_S#RE6Vv`c&3sS3$a*!|AW78^qy1E#(bE2Fr+Azx3h!ii7vYy_bJo*` zc_hG&dGmukp?-28kCjfvGkF4yYn|$2JQODn`B*qG{@_F_jv|8mG7> zW9_~32sZuTs!g7-c>~PgZ`a?*Gy3T-!HSa70NoO#x_poS*Nvg)1GtPqz-7ErxxP1V z1;|<$koCsHbG9E>Y_q?#uk>8+d6_>Wy6|bC?fd^)yUwVl)+`#NOAA%$f*>8l2%&`* zAPCYy9v!Lr1PRhXs-Z}UsB}T3C`~{*gd!;A0YYek6s5ONq_r1u-I*5PLy2e|IjcLp%uGNTVqB(1JY2!kNV{lP2HY;7WqNu4jPA~kb zTP;|2^K%`X{m*es!2@6sAz5s8Aw$t~^Z0H8es@F-*RLF7obTTXx$JM1%tgHPX>_|h zGf;lSU~8_^IMd}oi=spI<15|3xq9K4zr5|$Q1Xb8`EDhnl~-Uh&T$k{FZe9u8?(XO z{mD9!j>S7PG(0bOv^=kyBIf5sq~cA|*F9Gv9MKaFbzHJYG+2;@VMbwgD+1j6R+il! z9p?kt(?@LYEm}_%!e8g$O68)Jrq7!iichHVa3fSky^Q>4*=rAhBNPCa;CrFh+cEAZy~Ra>UN~d4Wr5G+wCBv6PkGSi zEo4_BBy#KX`}r}cqQ3ahM&pd42s00FFNM<$h&^2uR3h+uYIH3zb3FU;lvJ5Q&RCKq||QpVj5 zH&|4m!*@MUH2CZU+Gtc&COlwdC7796J+Tmhh!PQL^%Tv2apcDKxsm>!kb3l7c?P{$Y@0JM^bCYxku*V1{=8Mn z7r4W1LphzDeabNFnr+Qo=E(p0#&dJnN_#dxZ{W(-(oS)i4n@sQPE{pxQ!B7me0+E1 zRvvKYOYI!@fU{u6@j~TJoKScPeIPo+=RRY0oiSG3;~V%xs+4nb^Fe1PcR+~J*&cgJ zV-Z=AU;e0DoQqB`5+^;^Z%FVIxr4)d>YA3dn)BtxC zT5D<&FDh2GJl)LarJRzL5+~FPItZLwoHuFS=q!0XdN?`KC`oOps1unx{Wg4qL0|Vw#Km37Pe+i6Sjjwj*x}%+E zu8g-LuVF(3Etkl+)LWb9(ypUIYht`#o&2AA14A?sjGnZQO?E|VLcNDTS_y*mN`=~b zFTvru$5k(#Yv2qnDla#YS#OIj%FGv2^aQpdTU+(_`{<3Q*q|5#O!zO z@Kuc4WLffuE7&A2^%$mCqk|n52Yzr0=iziQGOli}Pha4>LiiQ8CPlJp0-kMHW&lqr zRhc&-RDW0B3@Z>X%#4QF$^Ft+0=jw%MQDX36w|&@M?~*S+mP;S;K@z~b7av^Ez+M_ zZ;ESZ!-_oGeD}P^!l!@ku7`t!sIoo%tyAds*v`?{f{N>&b9g3JKbv8xyT%BOcnAmJ z682X|A%8M5@uDu8Iwo@@#^qEjk7Nd)-e}3d3v=tJ<# zBdSbBp9|;}_-7>HQfXd75v&zHKw#>#uu{!J<*Ol&<#DYxXS{y7X`|7 zXAv6!gvEdMV?&NsQd@3y)EXHqMPS36!IkbZtrROpv@_S0rgI0ejPH^l4JZAZ_77x;%E6KFaCZ&0 zDwHtFUL5fxu-I^P%VRS;Jby;s(a}NKS`Q7nV+rgMUd6AswC7;p6nXl-4uFhhBW?iv z-`baL*+c)}>)#ev*MVU}9kjms1nPFCCGX9h9m{I{=BP?o4nAd&0SQVk#H))pTJdJR z4cg<)ct!I!?OPFm>1gXPX-ofQGDe6_6bbtFu;&TanZ{kiymBQ9r35*7l((~t)24^> zO{W9@m`qlCuX#>UW%g@!yeG(M!`SshlrnnI0QgD%w+|!isxs^G>OLwNM}O?No-~KO z^W%MB0XTv`ObZs{72(Ts(7gGIoF)5AmoK^yC#uRpLPjpuqE0HnUl~I z$C$ayIkyLw%8bLkR9?ss8aiJY@{jpcvC>k>{OCd);~=A#CLSTD_y$s&eL;4KxkWU@a0zNsM_>) z(qG=w5CMXDcRiX43x#*|PfMV?F);djf{xKl(m{)A!a(hr_Lt2HxZ-fp*HZw=ZI~pQ z>Vn$|tIBkFZ8lBi_%tWq)-wEQzP@s(1YD87cj=1RR>7({2O$Pyo(Y*Q*qk-Hr zgI$6=k={;)1vUte#C^xw-_W9bw(j`kfpp zVUv=6R>Z)){qWSb2aTg8!uYXpQONc9M-o5kP`QCH(Hm^+Zi0VDcqSqa)p0IW5gGA|Q;V!h z7r^*7eqrJ0lH-SQ>+0X~KJjj`=P~I#<9obpKfOPR@p|!6@g!p)YRRqCp+~jAkTgXR z{bfrPX3j-*jyyb~H7V=Rl_MRrxlk;oASH_+%&VS^@78WWa3eoAMfD?A1AnJfrnvAu`6A;_@AHeTjyfo^uR5~t zJerE^z^@%k+KRjZBBAACAu|SpK>8D(#jtEd{1itp`+sY+ev;C<0vcBH}h1k@l=M* zh7kq5-1H!lv}6E>=I0BJVFKwm&v5LRompm~=-DNtpLKBJgT+(sL}9bot|XzSAyKx4 zWf`D75Oxo=Q8AxKH8yd0`v0{xB!z1LMe$UvZAhl`v86I~GRe)5aL)8oCLw*s4FtG5 z5B)!!4gP1!pOipxJ29eFqL*t#Q_kIb7b@$vr&rcax%teY{I>XOnIPb~p{}b|qG}uV EFJvp-lmGw# literal 0 HcmV?d00001 diff --git a/src/sre_agent/eval/imgs/opik_experiment.png b/src/sre_agent/eval/imgs/opik_experiment.png new file mode 100644 index 0000000000000000000000000000000000000000..44200803a6124ae7633edd1ab9381222b5be10e0 GIT binary patch literal 426972 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

AjXsNcB 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