From bbf46375bbe35c3524666dd48fe5cd6d45c15f5a Mon Sep 17 00:00:00 2001 From: "Charles P. Cross" <8572939+cpdata@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:57:55 -0400 Subject: [PATCH] Document LLM overrides and provisioning updates --- .github/workflows/ci.yml | 48 + AGENTS.md | 19 + CHANGELOG.md | 197 + CLEANUP.md | 88 + DISCREPANCIES.md | 53 + DUMMIES.md | 17 + Dockerfile | 24 + ENVIRONMENT_NEEDS.md | 30 + FINDINGS.md | 44 + ISSUES.md | 38 + Makefile | 29 +- NEEDED_FOR_TESTING.md | 73 + PLAN.md | 45 + PROJECT.md | 92 + README.md | 459 +-- README_OLD.md | 170 + RECOMMENDATIONS.md | 38 + RESUME_NOTES.md | 50 + SETUP.md | 125 + SOT.md | 155 + TODO.md | 74 + docker-compose.yml | 72 +- docs/api.md | 86 + docs/architecture.md | 62 + docs/configuration.md | 48 + docs/development.md | 44 + docs/operations.md | 57 + docs/overview.md | 34 + docs/persistence.md | 62 + docs/pipelines.md | 47 + docs/retrieval.md | 59 + docs/telemetry.md | 31 + docs/testing.md | 47 + docs/troubleshooting.md | 32 + examples/extract_preprocess_store_example.py | 77 +- meshmind/_compat/pydantic.py | 152 + meshmind/api/grpc.py | 108 + meshmind/api/memory_manager.py | 155 +- meshmind/api/rest.py | 123 + meshmind/api/service.py | 211 ++ meshmind/cli/__main__.py | 67 +- meshmind/cli/admin.py | 147 + meshmind/cli/ingest.py | 32 +- meshmind/client.py | 317 +- meshmind/core/bootstrap.py | 45 + meshmind/core/config.py | 78 +- meshmind/core/embeddings.py | 84 +- meshmind/core/observability.py | 80 + meshmind/core/similarity.py | 48 +- meshmind/core/types.py | 13 +- meshmind/core/utils.py | 62 +- meshmind/db/__init__.py | 14 + meshmind/db/base_driver.py | 60 +- meshmind/db/factory.py | 66 + meshmind/db/in_memory_driver.py | 188 + meshmind/db/memgraph_driver.py | 245 +- meshmind/db/neo4j_driver.py | 200 + meshmind/db/sqlite_driver.py | 308 ++ meshmind/llm_client.py | 259 ++ meshmind/models/registry.py | 25 +- meshmind/pipeline/compress.py | 52 +- meshmind/pipeline/consolidate.py | 168 +- meshmind/pipeline/expire.py | 8 +- meshmind/pipeline/extract.py | 47 +- meshmind/pipeline/preprocess.py | 114 +- meshmind/pipeline/store.py | 80 +- meshmind/retrieval/__init__.py | 39 + meshmind/retrieval/bm25.py | 74 +- meshmind/retrieval/fuzzy.py | 56 +- meshmind/retrieval/graph.py | 225 ++ meshmind/retrieval/rerank.py | 83 + meshmind/retrieval/search.py | 141 +- meshmind/retrieval/vector.py | 57 + meshmind/tasks/celery_app.py | 2 +- meshmind/tasks/scheduled.py | 106 +- meshmind/testing/__init__.py | 14 + meshmind/testing/fakes.py | 237 ++ meshmind/tests/conftest.py | 62 + meshmind/tests/docker/full-stack.yml | 49 + meshmind/tests/docker/memgraph.yml | 22 + meshmind/tests/docker/neo4j.yml | 28 + meshmind/tests/docker/redis.yml | 20 + meshmind/tests/test_cli_admin.py | 68 + meshmind/tests/test_client.py | 41 + meshmind/tests/test_db_drivers.py | 184 + meshmind/tests/test_docs_guard.py | 25 + meshmind/tests/test_driver_factory.py | 30 + meshmind/tests/test_graph_retrieval.py | 121 + meshmind/tests/test_llm_client.py | 54 + meshmind/tests/test_memgraph_driver.py | 17 +- meshmind/tests/test_neo4j_driver.py | 40 + meshmind/tests/test_observability.py | 24 + meshmind/tests/test_pipeline_extract.py | 54 +- meshmind/tests/test_pipeline_maintenance.py | 85 +- .../tests/test_pipeline_preprocess_store.py | 128 +- meshmind/tests/test_retrieval.py | 197 +- meshmind/tests/test_service_interfaces.py | 230 ++ meshmind/tests/test_setup_scripts.py | 31 + meshmind/tests/test_tasks_scheduled.py | 44 + pyproject.toml | 27 +- run/install_setup.sh | 80 + run/maintenance_setup.sh | 83 + scripts/__init__.py | 0 scripts/check_docs_sync.py | 102 + uv.lock | 3299 +++++++++++------ 105 files changed, 10492 insertions(+), 1869 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 AGENTS.md create mode 100644 CHANGELOG.md create mode 100644 CLEANUP.md create mode 100644 DISCREPANCIES.md create mode 100644 DUMMIES.md create mode 100644 Dockerfile create mode 100644 ENVIRONMENT_NEEDS.md create mode 100644 FINDINGS.md create mode 100644 ISSUES.md create mode 100644 NEEDED_FOR_TESTING.md create mode 100644 PLAN.md create mode 100644 PROJECT.md create mode 100644 README_OLD.md create mode 100644 RECOMMENDATIONS.md create mode 100644 RESUME_NOTES.md create mode 100644 SETUP.md create mode 100644 SOT.md create mode 100644 TODO.md create mode 100644 docs/api.md create mode 100644 docs/architecture.md create mode 100644 docs/configuration.md create mode 100644 docs/development.md create mode 100644 docs/operations.md create mode 100644 docs/overview.md create mode 100644 docs/persistence.md create mode 100644 docs/pipelines.md create mode 100644 docs/retrieval.md create mode 100644 docs/telemetry.md create mode 100644 docs/testing.md create mode 100644 docs/troubleshooting.md create mode 100644 meshmind/_compat/pydantic.py create mode 100644 meshmind/api/grpc.py create mode 100644 meshmind/api/rest.py create mode 100644 meshmind/api/service.py create mode 100644 meshmind/cli/admin.py create mode 100644 meshmind/core/bootstrap.py create mode 100644 meshmind/core/observability.py create mode 100644 meshmind/db/factory.py create mode 100644 meshmind/db/in_memory_driver.py create mode 100644 meshmind/db/neo4j_driver.py create mode 100644 meshmind/db/sqlite_driver.py create mode 100644 meshmind/llm_client.py create mode 100644 meshmind/retrieval/graph.py create mode 100644 meshmind/retrieval/rerank.py create mode 100644 meshmind/retrieval/vector.py create mode 100644 meshmind/testing/__init__.py create mode 100644 meshmind/testing/fakes.py create mode 100644 meshmind/tests/conftest.py create mode 100644 meshmind/tests/docker/full-stack.yml create mode 100644 meshmind/tests/docker/memgraph.yml create mode 100644 meshmind/tests/docker/neo4j.yml create mode 100644 meshmind/tests/docker/redis.yml create mode 100644 meshmind/tests/test_cli_admin.py create mode 100644 meshmind/tests/test_client.py create mode 100644 meshmind/tests/test_db_drivers.py create mode 100644 meshmind/tests/test_docs_guard.py create mode 100644 meshmind/tests/test_driver_factory.py create mode 100644 meshmind/tests/test_graph_retrieval.py create mode 100644 meshmind/tests/test_llm_client.py create mode 100644 meshmind/tests/test_neo4j_driver.py create mode 100644 meshmind/tests/test_observability.py create mode 100644 meshmind/tests/test_service_interfaces.py create mode 100644 meshmind/tests/test_setup_scripts.py create mode 100644 meshmind/tests/test_tasks_scheduled.py create mode 100755 run/install_setup.sh create mode 100755 run/maintenance_setup.sh create mode 100644 scripts/__init__.py create mode 100755 scripts/check_docs_sync.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..aeadc71 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: CI + +on: + push: + branches: ["main", "review", "review-1"] + pull_request: + +env: + PIP_DISABLE_PIP_VERSION_CHECK: "1" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install toolchain + run: | + pip install uv + uv pip install --system -e . + uv pip install --system ruff pyright typeguard toml-sort yamllint + - name: Lint and format checks + run: make fmt-check + - name: Docs guard + env: + BASE_REF: ${{ github.event.pull_request.base.sha || 'HEAD~1' }} + run: make docs-guard + + tests: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: | + pip install uv + uv pip install --system -e . + uv pip install --system pytest + - name: Run pytest + run: make test diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..edfdc11 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,19 @@ +# Agent Instructions + +## Documentation Workflow +- After each batch of changes, add a `CHANGELOG.md` entry with an ISO 8601 date/time stamp in United States Eastern time (include the timezone code, e.g., `America/New_York` or `ET`) and developer-facing detail (files, modules, functions, variables, and rationale). Every commit should correspond to a fresh entry. +- Maintain `README.md` as the canonical description of the project; update it whenever behaviour or workflows change. Archive older versions separately when requested. +- Keep the `docs/` wiki and provisioning guides (`SETUP.md`, `ENVIRONMENT_NEEDS.md`) in sync with code updates; add or revise the + relevant page whenever features, modules, or workflows change. +- After each iteration, refresh `ISSUES.md`, `SOT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `TODO.md`, and related documentation to stay in sync with the codebase. +- Ensure `TODO.md` retains the `Completed`, `Priority Tasks`, and `Recommended Waiting for Approval Tasks` sections, moving finished items under `Completed` at the end of every turn. +- Make every task in `TODO.md` atomic: each entry must describe a single, self-contained deliverable with enough detail to execute and verify without cross-referencing additional context. +- Update `RESUME_NOTES.md` at the end of every turn so the next session starts with accurate context. +- When beginning a turn, review `README.md`, `PROJECT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `ISSUES.md`, and `SOT.md` to harvest new actionable work. Maintain at least ten quantifiable, prioritised items in the `Priority Tasks` section of `TODO.md`, adding context or links when needed. +- After completing any task, immediately update `TODO.md`, check for the next actionable item, and continue iterating until all unblocked `Priority Tasks` are exhausted for the session. +- Continuously loop through planning and execution: finish a task, document it, surface new follow-ups, and resume implementation so long as environment blockers allow. If extra guidance would improve throughput, extend these instructions proactively. + +## Style Guidelines +- Use descriptive Markdown headings starting at level 1 for top-level documents. +- Keep lines to 120 characters or fewer when practical. +- Prefer bullet lists for enumerations instead of inline commas. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bd6fddc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,197 @@ +# Changelog + +## [2025-10-14T22:51:20-04:00 (America/New_York)] +### Changed +- Documented LLM override precedence in `README.md`, expanded service documentation in `docs/api.md` and + `docs/configuration.md`, and clarified CLI guidance so per-request model/endpoint overrides are discoverable. +- Updated provisioning guidance in `SETUP.md` and `docs/operations.md` to describe the validation/dry-run flags in + `run/install_setup.sh` and `run/maintenance_setup.sh`, aligning with the new pytest smoke tests. +- Refreshed planning artifacts (`SOT.md`, `PLAN.md`, `PROJECT.md`, `RECOMMENDATIONS.md`, `FINDINGS.md`, `ISSUES.md`, `TODO.md`) + to capture timezone-aware timestamp defaults, FakeLLMClient coverage, and the completed documentation tasks. +- Synced testing references by elaborating on the FakeLLMClient behaviour in `docs/testing.md`, extending `DUMMIES.md`, and + updating environment guides (`ENVIRONMENT_NEEDS.md`, `NEEDED_FOR_TESTING.md`) to note optional packages now install with + network access. +- Revised `RESUME_NOTES.md` with the latest documentation work and remaining priorities to aid the next development session. + +## [2025-10-14T20:40:15-04:00 (America/New_York)] +### Added +- Introduced `meshmind/llm_client.py` with `LLMConfig`, a cached OpenAI-compatible `LLMClient`, and builders that hydrate + per-operation defaults from `LLM_*` environment variables so extraction, embeddings, and reranking share a single + provider abstraction. +- Added `meshmind/tests/test_llm_client.py` to verify configuration overrides and client caching behaviour without + requiring the real SDK. +- Authored `CLEANUP.md` to track post-restriction remediation work for compatibility shims now that full dependencies are + installable. + +### Changed +- Refactored `meshmind.client.MeshMind`, `meshmind/core/embeddings.py`, `meshmind/pipeline/extract.py`, and + `meshmind/retrieval/rerank.py` to consume the new `LLMClient`, cascade rerank defaults, and remove direct `openai` + imports. +- Expanded the CLI ingest command (`meshmind/cli/__main__.py`, `meshmind/cli/ingest.py`) with `--llm-*` flags that override + per-operation models/endpoints and wire them through to `MeshMind`. +- Updated configuration plumbing (`meshmind/core/config.py`, `scripts/check_docs_sync.py`) so `LLM_*` variables are first-class + settings and documentation guard checks map the new module to the wiki. +- Refreshed documentation (`README.md`, `docs/configuration.md`, `docs/pipelines.md`, `docs/retrieval.md`, `docs/operations.md`, + `docs/testing.md`, `docs/troubleshooting.md`, `SETUP.md`, `SOT.md`, `PROJECT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, + `ISSUES.md`, `ENVIRONMENT_NEEDS.md`, `NEEDED_FOR_TESTING.md`, `FINDINGS.md`, `RESUME_NOTES.md`) to describe the provider-agnostic + LLM workflow, new CLI overrides, and related environment variables. +- Extended `TODO.md` and backlog artifacts to mark the LLM wrapper task complete, add follow-up work for surfacing overrides in + service payloads, and record the new cleanup plan. + +## [2025-10-14T19:44:39-04:00 (America/New_York)] +### Added +- Authored `DUMMIES.md` to catalogue compatibility shims (`meshmind/_compat/pydantic.py`), REST/gRPC stubs, Celery fallbacks, + and fake drivers with guidance on which artifacts to retire versus keep for offline testing. + +### Changed +- Updated `README.md`, `SOT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `PROJECT.md`, and `FINDINGS.md` to reference the new + compatibility inventory so contributors know where to track shim removal work. +## [2025-10-14T16:46:48-04:00 (America/New_York)] +### Changed +- Swapped the Memgraph dependency in `pyproject.toml` from `mgclient` to `pymgclient` and confirmed optional packages install + cleanly with the refreshed network access (`uv pip install`). +- Updated environment references—`ENVIRONMENT_NEEDS.md`, `NEEDED_FOR_TESTING.md`, `SETUP.md`, `README.md`, `README_OLD.md`, `SOT.md`, `PROJECT.md`, + `FINDINGS.md`, `ISSUES.md`, `TODO.md`, `docs/` wiki pages—to describe `pymgclient` as the Memgraph package while preserving + the runtime `mgclient` module references. +- Revised `AGENTS.md` to require Eastern Time timestamps with timezone codes for every changelog entry and aligned `RESUME_NOTES.md` + with the newly installed optional dependencies and confirmed internet availability. + +## [2025-10-14T15:53:42-04:00 (America/New_York)] +### Added +- Authored `run/install_setup.sh` and `run/maintenance_setup.sh` bash scripts that install system packages (`build-essential`, + `cmake`, `libssl-dev`, `libopenblas-dev`, etc.) and synchronize Python dependencies via `uv pip sync` so fresh and cached + environments can bootstrap optional tooling (`neo4j`, `mgclient`, `redis`, REST extras) once internet access is available. + +### Changed +- Updated `AGENTS.md` with an atomic-task requirement and refreshed `TODO.md` to prepend granular items for drafting + `CLEANUP.md`, introducing a provider-agnostic `meshmind/llm_client.py`, replacing direct OpenAI imports, and wiring cascaded + LLM overrides across configuration, CLI, API, tests, and documentation. +- Extended planning/backlog documents—`ISSUES.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `SOT.md`, `RESUME_NOTES.md`—to capture the + upcoming LLM client refactor, dependency sync expectations, and the new automation scripts. +- Added setup guidance in `README.md` and `SETUP.md` pointing to the `run/` scripts so developers with sudo access can + bootstrap environments automatically. + +## [2025-10-17T18:45:00Z] +### Added +- Created a Dockerfile for integration workloads and introduced targeted Compose stacks + under `meshmind/tests/docker/` (Memgraph, Neo4j, Redis, full-stack) alongside a + developer-facing provisioning guide in `SETUP.md` to document service bootstrapping + commands and environment requirements. + +### Changed +- Expanded `pyproject.toml` to install optional dependencies (`fastapi`, + `uvicorn[standard]`, `neo4j`, `mgclient`, `redis`) by default and defined extras + (`dev`, `docs`, `testing`); updated the `Makefile` `install` target accordingly and + regenerated setup documentation across `README.md`, `docs/`, `PROJECT.md`, `PLAN.md`, + `SOT.md`, `NEEDED_FOR_TESTING.md`, `ENVIRONMENT_NEEDS.md`, `FINDINGS.md`, + `RECOMMENDATIONS.md`, and `RESUME_NOTES.md` to reference the new workflow and + credentials. +- Reworked the root `docker-compose.yml` to provision Memgraph, Neo4j, and Redis with + health checks and volumes, added Compose variants in `meshmind/tests/docker/`, and + refreshed onboarding materials (`SETUP.md`, `README.md`, `docs/configuration.md`, + `docs/operations.md`, `docs/testing.md`) to call out the new ports, credentials, and + teardown guidance. +- Replaced references to `pymgclient` with `mgclient` throughout dependency notes and + environment files to match the updated driver import. + +### Fixed +- Patched `meshmind/cli/admin.py` to import `argparse`, restoring CLI admin command + registration after the module refactor. +- Updated `.github/workflows/ci.yml` to pass `--system` to `uv pip install`, resolving + the "No virtual environment found" failure during lint/test setup. + +## [2025-10-16T18:30:00Z] +### Fixed +- Adjusted `meshmind/tests/test_service_interfaces.py::test_memory_service_ingest_and_search` to return a hydrated `Memory` + instance from the monkey-patched `list_memories` stub, ensuring pagination-aware search paths remain asserted while avoiding + empty result sets during verification. + +## [2025-10-16T12:00:00Z] +### Added +- Introduced pagination-aware graph access by adding `search_entities` and `count_entities` to every `GraphDriver` implementation, wiring a new `meshmind admin counts` CLI subcommand and REST `/memories/counts` route through `MemoryManager`, `MemoryService`, and the MeshMind client. +- Added `scripts/check_docs_sync.py` plus a Makefile target, CI step, and pytest coverage to guard documentation updates whenever code under mapped modules changes. + +### Changed +- Extended `MemoryManager.list_memories`, MeshMind client helpers, retrieval graph wrappers, and service adapters to forward `offset`, `limit`, and `query` hints, delegating filtering to the active driver before in-memory scoring. +- Updated examples and tests (`meshmind/tests/test_db_drivers.py`, `test_service_interfaces.py`, `test_graph_retrieval.py`, `test_cli_admin.py`, `test_client.py`, `test_docs_guard.py`) to cover pagination, counts, and driver-side search semantics. + +### Documentation +- Refreshed `README.md`, `PROJECT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `ISSUES.md`, `SOT.md`, `FINDINGS.md`, `AGENTS.md`, `TODO.md`, and the developer wiki (`docs/api.md`, `docs/development.md`, `docs/operations.md`, `docs/persistence.md`, `docs/retrieval.md`, `docs/troubleshooting.md`) to describe pagination, counts, docs-guard workflows, and updated service interfaces. +## [2025-10-15T15:30:00Z] +### Added +- Created a developer wiki under `docs/` covering architecture, pipelines, persistence, retrieval, configuration, testing, operations, telemetry, and development workflows so code changes stay synchronized with reference material. +- Authored `ENVIRONMENT_NEEDS.md` to request optional dependency installs and external services, plus `RESUME_NOTES.md` for session-to-session continuity. + +### Changed +- Expanded the `GraphDriver` contract to accept namespace and entity-label filters when listing entities, updating the in-memory, SQLite, Neo4j, and Memgraph drivers to push filtering into their native query layers. +- Propagated the new filtering through `MemoryManager`, `MeshMind.list_memories`, graph-backed retrieval wrappers, and service interfaces (REST/gRPC), ensuring hybrid searches hydrate only the required entity types. +- Updated tests (`meshmind/tests/test_graph_retrieval.py`, `test_pipeline_preprocess_store.py`, `test_service_interfaces.py`) to cover entity-label filtering across client, REST, and gRPC paths. + +### Documentation +- Refreshed `README.md`, `PROJECT.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `ISSUES.md`, `SOT.md`, `DISCREPANCIES.md`, `FINDINGS.md`, `TODO.md`, and `AGENTS.md` to describe the new driver filtering, documentation workflow, environment checklist, and wiki requirements. + +## [2025-02-15T00:45:00Z] +### Added +- Introduced `meshmind/retrieval/graph.py` with hybrid/vector/regex/exact/BM25/fuzzy wrappers that hydrate candidates from the active `GraphDriver` before delegating to existing scorers, plus `meshmind/tests/test_graph_retrieval.py` to verify namespace filtering and hybrid integration. +- Added `meshmind/cli/admin.py` and wired `meshmind/cli/__main__.py` to expose `admin` subcommands for predicate management, maintenance telemetry, and graph connectivity checks; created `meshmind/tests/test_cli_admin.py` to cover the new flows. +- Created `meshmind/tests/test_neo4j_driver.py` and a `Neo4jGraphDriver.verify_connectivity` helper to exercise driver-level sanity checks without a live cluster. +- Logged importance score distributions via `meshmind/pipeline/preprocess.summarize_importance` so telemetry captures mean/stddev/recency metrics after scoring. + +### Changed +- Updated `MeshMind` search helpers (`meshmind/client.py`) to auto-load memories from the configured driver when `memories` is `None`, reusing the new graph-backed wrappers. +- Reworked `meshmind/pipeline/consolidate.py` to return a `ConsolidationPlan` with batch/backoff thresholds and skipped-group tracking; `meshmind/tasks/scheduled.consolidate_task` now emits skip counts and returns a structured summary. +- Tuned Python compatibility metadata to `>=3.11,<3.13` in `pyproject.toml` and refreshed docs (`README.md`, `NEEDED_FOR_TESTING.md`, `SOT.md`) accordingly. +- Enhanced `meshmind/pipeline/preprocess.py` to emit telemetry gauges for importance scoring and added `meshmind/tests/test_pipeline_preprocess_store.py::test_score_importance_records_metrics`. +- Expanded retrieval, CLI, and driver test coverage (`meshmind/tests/test_retrieval.py`, `meshmind/tests/test_tasks_scheduled.py`) to account for graph-backed defaults and new return types. + +### Documentation +- Updated `README.md`, `PROJECT.md`, `PLAN.md`, `SOT.md`, `FINDINGS.md`, `DISCREPANCIES.md`, `RECOMMENDATIONS.md`, `NEEDED_FOR_TESTING.md`, `ISSUES.md`, and `TODO.md` to describe graph-backed retrieval wrappers, CLI admin tooling, consolidation backoff behaviour, telemetry metrics, and revised Python support. +- Copied the refreshed README guidance into `README_OLD.md` as an archival reference while keeping `README.md` as the primary source. + +## [2025-10-14T14:57:47Z] +### Added +- Introduced `meshmind/_compat/pydantic.py` to emulate `BaseModel`, `Field`, and `ValidationError` when Pydantic is unavailable, enabling tests to run in constrained environments. +- Added `meshmind/testing/fakes.py` with `FakeMemgraphDriver`, `FakeRedisBroker`, and `FakeEmbeddingEncoder`, plus a package export and dedicated pytest coverage (`meshmind/tests/test_db_drivers.py`, `meshmind/tests/test_tasks_scheduled.py`). +- Created heuristics-focused test cases for consolidation outcomes, maintenance tasks, and the revised retrieval dispatcher to guarantee behaviour without external services. + +### Changed +- Replaced the constant importance assignment in `meshmind/pipeline/preprocess.score_importance` with a heuristic that factors token diversity, recency, metadata richness, and embedding magnitude. +- Rebuilt `meshmind/pipeline/consolidate` around a `ConsolidationOutcome` dataclass that merges metadata, averages embeddings, and surfaces removal IDs; `meshmind/tasks/scheduled.consolidate_task` now applies updates and deletes duplicates lazily via `_get_manager`/`_reset_manager` helpers. +- Hardened Celery maintenance tasks by logging driver initialization failures, tracking update counts, and returning deterministic totals; compression counts now reflect the number of persisted updates. +- Updated `meshmind/core/similarity`, `meshmind/retrieval/bm25`, and `meshmind/retrieval/fuzzy` with pure-Python fallbacks so numpy, scikit-learn, and rapidfuzz remain optional. +- Adjusted `meshmind/pipeline/extract.extract_memories` to defer `openai` imports until a default client is required, unblocking DummyLLM-driven tests. +- Reworked `meshmind/retrieval/search.search` to rerank the original (filtered) candidate ordering, prepend reranked results, and append hybrid-sorted fallbacks, preventing index drift when rerankers return relative positions. +- Normalised SQLite entity hydration in `meshmind/db/sqlite_driver._row_to_dict` so JSON metadata is decoded only when stored as strings. +- Refreshed pytest fixtures (`meshmind/tests/conftest.py`, `meshmind/tests/test_pipeline_preprocess_store.py`) to use deterministic encoders and driver doubles, ensuring CRUD and retrieval suites run without live services. + +### Documentation +- Promoted `README.md` as the single source of truth (archiving the previous copy in `README_OLD.md`) and documented the new heuristics, compatibility shims, and test doubles. +- Updated `NEEDED_FOR_TESTING.md` with notes about the compatibility layer, optional dependencies, and fake drivers. +- Reconciled `PROJECT.md`, `ISSUES.md`, `PLAN.md`, `SOT.md`, `RECOMMENDATIONS.md`, `DISCREPANCIES.md`, `FINDINGS.md`, `TODO.md`, and `CHANGELOG.md` to capture the new persistence behaviour, heuristics, fallbacks, and remaining roadmap items. + +## [Unreleased] - 2025-02-14 +### Added +- Configurable graph driver factory with in-memory, SQLite, Memgraph, and optional Neo4j implementations plus supporting tests. +- REST and gRPC service layers (with FastAPI stub fallback) for ingestion and retrieval, including coverage in the test suite. +- Observability utilities that collect metrics and structured logs across pipelines and scheduled Celery tasks. +- Docker Compose definition provisioning Memgraph, Redis, and a Celery worker for local development. +- Vector-only, regex, exact-match, and optional LLM rerank retrieval helpers with reranker utilities and exports. +- MeshMind client wrappers for hybrid, vector, regex, and exact searches plus driver accessors. +- Example script demonstrating triplet storage and diverse retrieval flows. +- Pytest fixtures for encoder and memory factories alongside new retrieval tests that avoid external services. +- Makefile targets for linting, formatting, type checks, and tests, plus a GitHub Actions workflow running lint and pytest. +- README_LATEST.md capturing the current implementation and CHANGELOG.md for release notes. + +### Changed +- Settings now surface `GRAPH_BACKEND`, Neo4j, and SQLite options while README/NEEDED_FOR_TESTING document the expanded setup. +- README, README_LATEST, and NEW_README were consolidated so the promoted README reflects current behaviour. +- PROJECT, PLAN, SOT, FINDINGS, DISCREPANCIES, ISSUES, RECOMMENDATIONS, and TODO were refreshed to capture new capabilities and + re-homed backlog items under a "Later" section. +- Updated `SearchConfig` to support rerank models and refreshed MeshMind documentation across PROJECT, PLAN, SOT, FINDINGS, + DISCREPANCIES, RECOMMENDATIONS, ISSUES, TODO, and NEEDED_FOR_TESTING files. +- Revised `meshmind.retrieval.search` to apply filters centrally, expose new search helpers, and integrate reranking. +- Exposed graph driver access on MeshMind and refreshed retrieval-facing examples and docs. + +### Fixed +- Example ingestion script now uses MeshMind APIs correctly and illustrates relationship persistence. +- Tests rely on fixtures rather than deprecated hooks, improving portability across environments without Memgraph/OpenAI. diff --git a/CLEANUP.md b/CLEANUP.md new file mode 100644 index 0000000..8abaf4b --- /dev/null +++ b/CLEANUP.md @@ -0,0 +1,88 @@ +# Cleanup Plan for Temporary Workarounds + +The following actions should be executed once the development environment consistently provides internet access and all +optional dependencies can be installed. Each item references the affected files, the temporary workaround currently in +place, and the exact remediation steps required to bring the implementation in line with production expectations. + +## Compatibility Layers + +### Replace Pydantic Shim +- **Files**: `meshmind/_compat/pydantic.py`, modules importing from `meshmind._compat.pydantic`. +- **Current State**: Custom `BaseModel` drop-in used when `pydantic` is unavailable. +- **Action**: + 1. Reintroduce `pydantic` as a hard dependency and update `pyproject.toml` extras accordingly. + 2. Migrate all models to inherit from the official `pydantic` classes. + 3. Delete `meshmind/_compat/pydantic.py` once no modules import it directly. + 4. Update tests to cover validation using real `pydantic` features (e.g., `.model_dump`). + +### Retire FastAPI Stub +- **Files**: `meshmind/api/rest.py`, `meshmind/api/service.py`, `docs/api.md`, `SETUP.md`. +- **Current State**: A lightweight FastAPI replacement exposes REST endpoints without requiring the framework. +- **Action**: + 1. Install `fastapi`, `uvicorn`, and related extras via the setup scripts. + 2. Replace the stub implementation with a true FastAPI app using pydantic request/response models. + 3. Update tests to spin up the FastAPI test client instead of the stub. + 4. Refresh documentation to reflect the production stack only. + +### Replace gRPC Dataclass Shim +- **Files**: `meshmind/api/grpc.py`, `meshmind/tests/test_service_interfaces.py`, `docs/api.md`. +- **Current State**: Dataclass-based stubs mirror generated proto classes. +- **Action**: + 1. Author protobuf definitions for service contracts and commit generated Python code. + 2. Integrate `grpcio` and `grpcio-tools` into the setup scripts and lockfile. + 3. Update the service module to use generated classes and channel/server implementations. + 4. Convert tests to rely on `grpc.aio` test utilities and remove dataclass shims. + +## Task Scheduling Workarounds + +### Remove Celery Dummy App and Beat Fallback +- **Files**: `meshmind/tasks/celery_app.py`, `meshmind/tasks/scheduled.py`. +- **Current State**: Custom placeholders allow imports without Celery and Celery Beat. +- **Action**: + 1. Make `celery` a required dependency for maintenance tasks. + 2. Refactor scheduling utilities to import real Celery constructs and fail fast when misconfigured. + 3. Add integration tests that execute Celery workers against Redis or RabbitMQ in docker-compose. + 4. Delete fallback classes once coverage exists. + +## Testing Fakes + +### Evaluate Graph/Embedding/Test Fakes +- **Files**: `meshmind/testing/fakes.py`, `meshmind/tests/conftest.py`, `meshmind/tests/test_*`. +- **Current State**: In-memory drivers and dummy encoders enable offline unit tests. +- **Action**: + 1. Keep unit-test fakes for isolation but ensure parallel integration suites use live services. + 2. Document expectations in `docs/testing.md` and mark fakes clearly as test-only utilities. + 3. Remove any production code paths that accidentally import fakes. + +### Replace Memgraph Client Mock +- **Files**: `meshmind/tests/test_memgraph_driver.py`. +- **Current State**: Monkeypatches a fake `mgclient` module for driver tests. +- **Action**: + 1. Install `pymgclient` and run tests against a dockerized Memgraph instance. + 2. Retain the fake only for negative/offline coverage; guard it behind an explicit fixture flag. + +## Observability and Telemetry + +### Confirm Logging Metrics Alignment +- **Files**: `meshmind/core/observability.py`, `meshmind/pipeline/*`, `docs/telemetry.md`. +- **Current State**: Logging/metrics rely on standard library fallbacks. +- **Action**: + 1. Integrate preferred observability stack (e.g., OpenTelemetry) once dependencies are greenlit. + 2. Remove or demote placeholders that mimic external exporters. + +## Tooling and Automation + +### Finalize Setup Scripts +- **Files**: `run/install_setup.sh`, `run/maintenance_setup.sh`. +- **Current State**: Scripts install packages opportunistically. +- **Action**: + 1. Review installed package list after environment unblock and remove conditional guards. + 2. Ensure scripts configure CLI tools (e.g., `grpcurl`, `neo4j-admin`) that were previously skipped offline. + +### Lock Dependency Graph +- **Files**: `pyproject.toml`, `uv.lock`. +- **Current State**: Lockfile may exclude previously unavailable packages. +- **Action**: + 1. Regenerate `uv.lock` after all dependencies are installed. + 2. Commit the updated lockfile and note differences in `CHANGELOG.md`. + diff --git a/DISCREPANCIES.md b/DISCREPANCIES.md new file mode 100644 index 0000000..f992bff --- /dev/null +++ b/DISCREPANCIES.md @@ -0,0 +1,53 @@ +# README vs Implementation Discrepancies + +## Overview +- The legacy README has been superseded by `README.md`, which now reflects the implemented feature set. +- The current codebase delivers extraction, preprocessing, triplet persistence, CRUD helpers, and expanded retrieval strategies + that were missing when the README was written. +- Remaining gaps primarily involve pushing retrieval workloads into the graph backend, exporting observability to external sinks, and automated infrastructure provisioning. + +## API Surface +- ✅ `MeshMind` now exposes CRUD helpers (`create_memory`, `update_memory`, `delete_memory`, `list_memories`, triplet helpers) + that the README referenced implicitly. +- ✅ Triplet storage routes through `store_triplets` and `MemoryManager.add_triplet`, calling `GraphDriver.upsert_edge`. +- ⚠️ The README still references `register_entity`, `register_allowed_predicates`, and `add_predicate`; predicate management is + handled automatically but there is no public API matching those method names. +- ⚠️ README snippets showing `mesh_mind.store_memory(memory)` should be updated to call `store_memories([memory])` or the new + CRUD helpers. + +## Retrieval Capabilities +- ✅ Vector-only, regex, exact-match, hybrid, BM25, fuzzy, and optional LLM rerank searches exist in `meshmind.retrieval.search` + and are surfaced through `MeshMind` helpers. +- ⚠️ README implies retrieval queries the graph directly. Search helpers now fetch candidates from the configured driver when no + list is supplied but still score results in Python; Memgraph/Neo4j-native search remains future work. +- ⚠️ Named helpers like `search_facts` or `search_procedures` never existed; the README should reference the dispatcher plus + specialized helpers now available. + +## Data & Relationship Modeling +- ✅ Predicates are persisted automatically when storing triplets and tracked in `PredicateRegistry`. +- ⚠️ README examples that look up subjects/objects by name still do not match the implementation, which expects UUIDs. Add + documentation explaining how to resolve names to UUIDs before storing edges. +- ⚠️ Consolidation and expiry run via Celery jobs; README narratives should highlight that heuristics require further validation even though persistence is now wired up. + +## Configuration & Dependencies +- ✅ `README.md` and `ENVIRONMENT_NEEDS.md` document required environment variables, dependency guards, and setup steps. +- ⚠️ README still omits optional tooling now required by the Makefile/CI (ruff, pyright, typeguard, toml-sort, yamllint); + highlight these prerequisites more prominently. +- ✅ Python version support in `pyproject.toml` now pins `>=3.11,<3.13`, matching the dependency landscape documented in the README. + +## Example Code Paths +- ✅ Updated example scripts demonstrate extraction, triplet creation, and multiple retrieval strategies. +- ⚠️ Legacy README code that instantiates custom Pydantic entities remains inaccurate; extraction returns `Memory` objects and + validates `entity_label` names only. +- ⚠️ Search examples should be updated to show the new helper functions and optional rerank usage instead of nonexistent + `search_facts`/`search_procedures` calls. + +## Tooling & Operations +- ✅ Makefile and CI workflows now exist, aligning with README promises about automation once the README is refreshed. +- ✅ Docker Compose now provisions Memgraph, Redis, and a Celery worker; README sections should highlight the workflow and + caveats for environments lacking container tooling. +- ⚠️ Celery tasks still depend on optional infrastructure; README should clarify that heuristics and scheduling need production hardening even though persistence now works. + +## Documentation State +- Continue promoting `README.md` as the authoritative guide and propagate updates to supporting docs + (`SOT.md`, `PLAN.md`, `ENVIRONMENT_NEEDS.md`, `docs/`). diff --git a/DUMMIES.md b/DUMMIES.md new file mode 100644 index 0000000..062868a --- /dev/null +++ b/DUMMIES.md @@ -0,0 +1,17 @@ +# Dummies and Compatibility Artifacts + +The following table tracks all temporary shims, fake implementations, and stub services that were introduced while the +sandbox lacked internet access or external infrastructure. Each row notes what the component does today and the +recommended next step now that full dependencies can be installed. + +| Component | Location | Purpose | Current Usage | Recommended Action | +| --- | --- | --- | --- | --- | +| Pydantic compatibility shim | `meshmind/_compat/pydantic.py` | Provides a minimal `BaseModel` replacement when `pydantic` is unavailable. | Imported in service payloads and tests whenever the real package is missing. | Replace with real `pydantic` models once package installation is guaranteed; remove shim after migration. | +| FastAPI REST stub | `meshmind/api/rest.py` (`RestAPIStub`, `create_app`) | Exposes REST behaviour without the FastAPI dependency. | Tests use the stub when FastAPI is not installed; production should use FastAPI. | Keep temporarily for offline tests but plan to gate it behind an explicit test flag once FastAPI becomes mandatory. | +| gRPC dataclass shim | `meshmind/api/grpc.py` (`GrpcServiceStub` and request/response dataclasses) | Mimics generated gRPC classes so tests can exercise the service layer without `grpcio`. | Entire gRPC surface currently relies on this shim. | Replace with proto definitions and generated stubs when `grpcio-tools` is available; keep shim only for unit tests. | +| Celery dummy app | `meshmind/tasks/celery_app.py` (`_DummyCeleryApp`) | Allows module imports when Celery is missing. | Unit tests rely on the dummy to avoid Celery; production should use real Celery. | Retire dummy once Celery is a hard dependency; until then ensure tests explicitly exercise the real app when installed. | +| Celery beat fallback | `meshmind/tasks/scheduled.py` (`crontab` shim) | Supplies a no-op crontab when Celery beat is missing. | Prevents import errors in test environments without Celery. | Remove once Celery is required; otherwise guard usage with feature flags. | +| Fake graph/storage drivers | `meshmind/testing/fakes.py` (`FakeMemgraphDriver`, `FakeRedisBroker`, `FakeEmbeddingEncoder`) | Provide offline stand-ins for Memgraph, Redis, and embedding models. | Pytest fixtures and documentation rely on these for isolation. | Keep as long as offline tests are desired; supplement with integration suites that use real services. | +| Fake LLM client | `meshmind/testing/fakes.py` (`FakeLLMClient`) | Records per-request overrides and emits deterministic responses so tests exercise reranking without installing the OpenAI SDK. | Service/interface tests (`meshmind/tests/test_service_interfaces.py`, `test_client.py`) and the CLI fixtures inject this stub when `openai` is unavailable. | Keep for unit tests; add integration tests with real providers once keys and network access are provisioned. | +| Dummy encoder fixture | `meshmind/tests/conftest.py` (`dummy_encoder`) and dependent tests | Supplies a lightweight embedding encoder for search tests. | Used across retrieval and service tests to avoid network calls. | Keep for unit tests; add integration coverage with real encoders once APIs are configured. | +| Fake mgclient module | `meshmind/tests/test_memgraph_driver.py` (monkeypatch of `mgclient`) | Simulates the Memgraph client so driver code runs without the native binary. | Enables driver unit tests without installing `mgclient`. | Replace with real `mgclient`-backed tests once package access is ensured; keep shim for fallback coverage. | diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7fe6f4e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.11-slim + +ENV PIP_NO_CACHE_DIR=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + libssl-dev \ + libkrb5-dev \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +COPY . /app + +RUN pip install uv \ + && uv pip install --system -e .[dev,docs,testing] + +CMD ["bash"] diff --git a/ENVIRONMENT_NEEDS.md b/ENVIRONMENT_NEEDS.md new file mode 100644 index 0000000..27bf9b9 --- /dev/null +++ b/ENVIRONMENT_NEEDS.md @@ -0,0 +1,30 @@ +# Tasks for Human Project Manager + +- Keep the Python package layer aligned with the project extras during base image + refreshes. The `run/install_setup.sh` and `run/maintenance_setup.sh` scripts now + install the full optional stack (neo4j driver, `pymgclient`, Redis, Celery extras, + FastAPI/Uvicorn, LLM tooling, and developer linters/testers). Ensure cached + environments either run the maintenance script or bake these dependencies into the + image so cold starts do not regress coverage. +- Provide system-level build dependencies for the graph drivers (e.g., `build-essential`, + `cmake`, `libssl-dev`, `libkrb5-dev`) so `pymgclient` (and its `mgclient` module) install cleanly. +- Provision external services and credentials (compose files now exist under the project + root and `meshmind/tests/docker/`): + - Neo4j instance reachable from the execution environment with `NEO4J_URI`, + `NEO4J_USERNAME`, `NEO4J_PASSWORD` (defaults to `neo4j` / `meshminD123`). + - Memgraph instance with `MEMGRAPH_URI`, `MEMGRAPH_USERNAME`, `MEMGRAPH_PASSWORD` + (anonymous access acceptable locally). + - Redis instance with `REDIS_URL`. + - LLM provider API key (`LLM_API_KEY` or fallback `OPENAI_API_KEY`) plus any + alternative base URLs/models required for OpenRouter, Azure, or Google-hosted + endpoints so the new `llm_client` overrides can be exercised end-to-end. +- Supply datasets/fixtures (future request) representing large knowledge graphs to + stress-test consolidation heuristics and pagination under load. +- Maintain outbound package download access to PyPI and vendor repositories; this + session confirmed package installation works when the network is open, and future + sessions need the same capability to refresh locks or install new optional + integrations. +- Enable Docker or container runtime access (future request) so the provided + `docker-compose.yml` files can run inside this environment; alternatively, provision + remote services accessible to CI. +- Document credential management procedures and rotation cadence so secrets stay current. diff --git a/FINDINGS.md b/FINDINGS.md new file mode 100644 index 0000000..f83e571 --- /dev/null +++ b/FINDINGS.md @@ -0,0 +1,44 @@ +# Findings + +## General Observations +- Core modules are now wired through the `MeshMind` client, including CRUD, triplet storage, and retrieval helpers. Graph-backed wrappers fetch namespace/entity-label filtered candidates from the configured driver automatically; remaining integration work focuses on server-side query optimisation and heuristic evaluation loops. +- Optional dependencies are largely guarded behind lazy imports, compatibility shims, or factory functions, improving portability. Environments still need to install tooling referenced by the Makefile and CI (ruff, pyright, typeguard, toml-sort, yamllint). `DUMMIES.md` now tracks every shim so we can retire them as integration coverage expands. +- LLM usage is now centralized in `meshmind.llm_client` with per-operation defaults cascading from `LLM_*` environment variables and CLI overrides, reducing the risk of divergent configurations across pipelines and retrieval. REST/gRPC payloads now provide matching override dictionaries so services can experiment per request. +- Timestamp helpers default to timezone-aware UTC, eliminating naive datetime outputs that previously leaked into maintenance pipelines and compatibility shims. +- Documentation artifacts (`README.md`, `SOT.md`, `ENVIRONMENT_NEEDS.md`, `docs/`) stay current when updated with each iteration; the legacy README has been archived as `README_OLD.md`. A docs-guard script now enforces synchronized updates during CI. + +## Dependency & Environment Notes +- `MeshMind` defers driver creation until persistence is required, enabling workflows without the `mgclient` module (install `pymgclient` to obtain it) and selecting between + in-memory, SQLite, Memgraph, or Neo4j backends. CLI helpers (`meshmind admin graph`) now expose connectivity sanity checks. +- Package management via `uv` now succeeds (optional dependencies installed via `uv pip install` this session); keep network access available so `uv.lock` regeneration can proceed when prioritized. Provisioning scripts (`run/install_setup.sh`, `run/maintenance_setup.sh`) now validate optional extras and support dry-run mode, with pytest coverage ensuring the flags continue to behave. +- Project metadata now advertises Python `>=3.11,<3.13`, aligning with available wheels for optional dependencies. +- Encoder registration occurs during bootstrap, but custom deployments must ensure compatible models are registered before + extraction or hybrid search. +- The OpenAI embedding adapter still expects dictionary-like responses; adapting to SDK objects remains on the backlog. +- Repository audit confirmed there are no lingering `import openai` statements outside guarded sections; the client and + embedding modules already encapsulate the SDK behind optional imports, easing the upcoming refactor to a dedicated + wrapper. +- Celery tasks initialize lazily, yet Redis/Memgraph services are still required at runtime. Docker Compose now provisions + Memgraph, Neo4j, and Redis, while targeted stacks under `meshmind/tests/docker/` support integration testing. `SETUP.md` + explains provisioning and teardown commands. + +## Data Flow & Persistence +- Triplet storage now persists relationships and tracks predicates automatically, closing an earlier data-loss gap. +- Consolidation and compression utilities now persist updates through the maintenance tasks, enforce batch/backoff thresholds, and surface skipped groups; larger-scale validation remains necessary. +- Importance scoring uses heuristics (token diversity, recency, metadata richness, embedding magnitude) and now records telemetry summaries; continued evaluation will raise retrieval quality. + +## CLI & Tooling +- CLI ingestion bootstraps encoders and entities automatically and now ships `admin` subcommands for predicate maintenance, telemetry dumps, graph connectivity checks, and namespace/entity counts. External backends still require valid credentials and running services. +- The Makefile introduces lint, format, type-check, test, and docs-guard targets, plus Docker helpers. External tooling installation is + required before targets succeed. +- GitHub Actions now run formatting checks, the docs guard, and pytest on push/PR, providing basic CI coverage. + +## Testing & Quality +- Pytest suites rely on fixtures (`memory_factory`, `dummy_encoder`) and compatibility shims to run without external services. Coverage now includes graph-backed retrieval wrappers, Neo4j connectivity shims, CLI admin flows, docs guard checks, and Celery consolidation telemetry. +- Type checking via `pyright` and runtime checks via `typeguard` are exposed in the Makefile; dependency installation is + necessary for full validation. + +## Documentation +- `README.md`/`README_LATEST.md` document setup, pipelines, retrieval, tooling, and now highlight service interfaces and observability. +- Supporting docs (`ISSUES.md`, `PLAN.md`, `RECOMMENDATIONS.md`, `SOT.md`) reflect the latest capabilities and highlight remaining + gaps, aiding onboarding and future planning. diff --git a/ISSUES.md b/ISSUES.md new file mode 100644 index 0000000..229f55b --- /dev/null +++ b/ISSUES.md @@ -0,0 +1,38 @@ +# Issues Checklist + +## Blockers +- [x] MeshMind client fails without the `mgclient` module; introduce lazy driver initialization or documented in-memory fallback. +- [x] Register a default embedding encoder (OpenAI or sentence-transformers) during startup so extraction and hybrid search can run. +- [x] Update OpenAI integration to match the current SDK (Responses API payload, embeddings API response structure). +- [x] Replace eager `tiktoken` imports in `meshmind.core.utils` and `meshmind.pipeline.compress` with guarded, optional imports. +- [x] Align declared Python requirement with supported dependencies (project now pins Python >=3.11,<3.13). + +- [ ] Maintain pip/uv package download access (confirmed working on 2025-10-15) so dependency lock regeneration can proceed reliably across sessions. +## High Priority +- [x] Provide configuration documentation and examples for Memgraph, Redis, and OpenAI environment variables. +- [x] Add automated tests or smoke checks that run without external services (mock OpenAI, stub Memgraph driver). +- [x] Create real docker-compose services for Memgraph and Redis or remove the placeholder file. +- [x] Centralize LLM provider usage behind a configurable client wrapper to remove direct `openai` imports scattered through the codebase. +- [x] Surface LLM override fields via REST/gRPC payloads and integration tests so service clients can select providers/models like the CLI. +- [ ] Document Neo4j driver requirements and verify connectivity against a live cluster (CLI connectivity checks exist but still need validation against a real instance). +- [ ] Exercise the new namespace/entity-label filtering against live Memgraph/Neo4j datasets to confirm Cypher predicates behave as expected. +- [ ] Regenerate `uv.lock` to reflect the updated dependency set (`pymgclient`, `fastapi`, `uvicorn`, extras) so CI tooling stays in sync. +## Medium Priority +- [x] Persist results from consolidation and compression tasks back to the database (currently in-memory only). +- [x] Refine `Memory.importance` scoring to reflect actual ranking heuristics instead of a constant. +- [x] Add vector, regex, and exact-match search helpers to match stated feature set or update documentation to demote them. +- [x] Harden Celery tasks to initialize dependencies lazily and log failures when the driver is unavailable. +- [ ] Validate consolidation heuristics on larger datasets to measure ranking accuracy and resource usage. +- [ ] Document a conflict-resolution/backoff strategy for consolidation when merged metadata conflicts. +- [ ] Revisit the compatibility shim once production environments support Pydantic 2.x so the real models can be restored. +- [ ] Push graph-backed retrieval into Memgraph/Neo4j search capabilities once available (current wrappers now filter/paginate server-side but still score vectors in Python). +- [ ] Reconcile tests that depend on `Memory.pre_init` and outdated OpenAI interfaces with the current implementation. +- [x] Expose `memory_counts` via a gRPC endpoint to keep service interfaces aligned. +- [x] Add linting, formatting, and type-checking tooling to improve code quality. + +- [ ] Validate the new Docker Compose stacks (root and `meshmind/tests/docker/`) on an environment with container support and document host requirements (ports, resources). +## Low Priority / Nice to Have +- [x] Offer alternative storage backends (in-memory driver, SQLite, etc.) for easier local development. +- [x] Provide an administrative dashboard or CLI commands for listing namespaces, counts, and maintenance statistics (CLI admin subcommands now expose predicates, telemetry, and graph checks). +- [ ] Publish onboarding guides and troubleshooting FAQs for contributors. +- [ ] Explore plugin registration for embeddings and retrieval strategies to reduce manual wiring. diff --git a/Makefile b/Makefile index 6693190..09123be 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,34 @@ -.PHONY: install lint fmt test docker +.PHONY: install lint fmt fmt-check typecheck test check docker clean docs-guard install: - pip install -e . + python -m pip install -e .[dev,docs,testing] lint: - ruff . + ruff check . fmt: - isort . - black . + ruff format . + +fmt-check: + ruff format --check . + ruff check . + toml-sort --check pyproject.toml + yamllint .github/workflows + +docs-guard: + python scripts/check_docs_sync.py --base $${BASE_REF:-origin/main} + +typecheck: + pyright + python -m typeguard --check meshmind test: pytest +check: fmt-check lint typecheck test docs-guard + +clean: + rm -rf .pytest_cache .ruff_cache + docker: - docker-compose up \ No newline at end of file + docker compose up diff --git a/NEEDED_FOR_TESTING.md b/NEEDED_FOR_TESTING.md new file mode 100644 index 0000000..a6f7408 --- /dev/null +++ b/NEEDED_FOR_TESTING.md @@ -0,0 +1,73 @@ +# Needed for Testing MeshMind + +> **Note:** `ENVIRONMENT_NEEDS.md` lists infrastructure and package requests for the human project manager. Use this document for developer-side setup details. + +## Python Runtime +- Python 3.11 or 3.12 is recommended; project metadata now pins `>=3.11,<3.13` because several dependencies (`pymgclient` (exposes the `mgclient` module), + `sentence-transformers`) do not yet publish wheels for 3.13. +- Use a virtual environment (`uv`, `venv`, or `conda`) to isolate dependencies. + +## Python Dependencies +- Install the project editable (with extras) using `pip install -e .[dev,docs,testing]` or + `uv pip install --system -e .[dev,docs,testing]` from the repository root. +- Core functionality relies on the OpenAI SDK (or compatible fork), `pydantic`, and `pydantic-settings`, but the repository + ships a compatibility shim (`meshmind/_compat/pydantic.py`) that unlocks tests when Pydantic is unavailable. +- Optional packages improve specific workflows (now bundled in the editable install extras so they install automatically when + running the provisioning scripts): + - `numpy`, `scikit-learn`, and `rapidfuzz` accelerate similarity and lexical search (pure-Python fallbacks are bundled). + - `sentence-transformers`, `tiktoken`, and `pymgclient` enable local embeddings, compression, and Memgraph connectivity. + - `celery[redis]` activates scheduled maintenance with a Redis broker. + - `fastapi` + `uvicorn[standard]` power the REST adapter when exercising HTTP APIs. +- Optional drivers: install `neo4j` if exercising the Neo4j backend; SQLite support ships with the standard library. The + provisioning scripts validate that the extras expose `neo4j`, `pymgclient`, `fastapi`, and `uvicorn`. +- Development tooling referenced by the Makefile and CI (installed via `.[dev,docs,testing]`): + - `ruff` for linting and formatting. + - `pyright` for static type checks. + - `typeguard` for runtime type enforcement (`python -m typeguard --check meshmind`). + - `toml-sort` and `yamllint` for configuration validation. + - `mkdocs`/`mkdocs-material` for future documentation publishing. +- Optional helpers for local workflows: `pytest-cov`, `pre-commit`, `httpx`/`grpcio-tools` (for service interface experimentation). + +## External Services and Infrastructure +- **Graph backend** options: + - In-memory / SQLite require no external services (set `GRAPH_BACKEND=memory` or `sqlite`). + - **Memgraph** reachable via `MEMGRAPH_URI` with credentials exported in `MEMGRAPH_USERNAME`/`MEMGRAPH_PASSWORD`. + - **Neo4j** reachable via `NEO4J_URI` with credentials `NEO4J_USERNAME`/`NEO4J_PASSWORD` when the optional driver is installed + (defaults supplied in `docker-compose.yml`). Use `meshmind admin graph --backend neo4j` to verify connectivity once + credentials are configured. +- **Redis** for Celery task queues, referenced through `REDIS_URL`. +- **LLM provider access** for extraction, embeddings, and reranking (`LLM_API_KEY` or fallback `OPENAI_API_KEY`, plus optional + `LLM_*_BASE_URL` overrides for alternative providers). +- Recommended: Docker Compose (shipped in repo) to run Memgraph, Neo4j, and Redis together when developing locally. Additional + targeted stacks live under `meshmind/tests/docker/` for integration tests. + +## Environment Variables +- `GRAPH_BACKEND` — `memory`, `sqlite`, `memgraph`, or `neo4j` (defaults to `memory`). +- `LLM_API_KEY` / `OPENAI_API_KEY` — required for extraction, embeddings, and reranking (OpenAI-compatible providers). +- `LLM_DEFAULT_MODEL` / `LLM_DEFAULT_BASE_URL` — default Responses model/base URL applied when overrides are absent. +- `LLM_EXTRACTION_MODEL` / `LLM_EXTRACTION_BASE_URL` — optional overrides for extraction calls. +- `LLM_EMBEDDING_MODEL` / `LLM_EMBEDDING_BASE_URL` — optional overrides for embedding requests. +- `LLM_RERANK_MODEL` / `LLM_RERANK_BASE_URL` — optional overrides for reranking requests. +- `MEMGRAPH_URI` — e.g., `bolt://localhost:7687` (when using Memgraph). +- `MEMGRAPH_USERNAME` and `MEMGRAPH_PASSWORD` — credentials for the Memgraph database. +- `NEO4J_URI`, `NEO4J_USERNAME`, `NEO4J_PASSWORD` — optional Neo4j connectivity details. +- `SQLITE_PATH` — filesystem path for the SQLite graph backend (defaults to in-memory). +- `REDIS_URL` — optional Redis connection URI (defaults to `redis://localhost:6379/0`). +- `EMBEDDING_MODEL` — legacy alias for the embedding encoder key (defaults to `LLM_EMBEDDING_MODEL`). +- Optional overrides for Celery broker/backend if using hosted services. + +## Local Configuration Steps +- Ensure an embedding encoder is registered before extraction or hybrid search. The bootstrap utilities invoked by the CLI and + `MeshMind` constructor handle this, but custom scripts must call `bootstrap_encoders()`. +- For REST/gRPC testing, instantiate the `RestAPIStub`/`GrpcServiceStub` with the in-memory driver to avoid external services. +- Use `meshmind/testing` fakes (`FakeMemgraphDriver`, `FakeRedisBroker`, `FakeEmbeddingEncoder`, `FakeLLMClient`) in tests or demos to eliminate external infrastructure requirements. +- Invoke `meshmind admin predicates` and `meshmind admin maintenance` during local runs to inspect predicate registries and telemetry without external services. +- Seed demo data as needed using the `examples/extract_preprocess_store_example.py` script after configuring environment + variables. +- Create a `.env` file storing the environment variables above for consistent local configuration. + +## Current Blockers in This Environment +- Neo4j/Memgraph binaries and Docker are unavailable in this workspace, preventing local graph provisioning; use the in-memory or SQLite drivers instead. +- Redis cannot be installed without container or host-level access; Celery tasks remain untestable locally until a remote + instance is provisioned (the fake broker satisfies unit tests but not end-to-end runs). +- External network restrictions may limit installation of proprietary packages or access to OpenAI endpoints. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..ac9c42a --- /dev/null +++ b/PLAN.md @@ -0,0 +1,45 @@ +# Plan of Action + +## Phase 1 – Stabilize Runtime Basics ✅ +1. **Dependency Guards** – Implemented lazy driver factories, optional imports, and clear ImportErrors for missing packages. +2. **Default Encoder Registration** – Bootstraps register encoders/entities automatically and the CLI invokes them on startup. +3. **OpenAI SDK Compatibility** – Extraction and embedding adapters align with the Responses API; remaining polish tracked in + `ISSUES.md`. +4. **Configuration Clarity** – `README.md`, `ENVIRONMENT_NEEDS.md`, and the new `docs/` pages document environment variables and service setup. + +## Phase 2 – Restore Promised API Surface ✅ +1. **Entity & Predicate Registry Wiring** – `MeshMind` now boots registries and storage persists predicates automatically. +2. **CRUD & Triplet Support** – CRUD helpers and triplet APIs live on `MeshMind` and `MemoryManager`, storing relationships via + `GraphDriver.upsert_edge`. +3. **Relationship-Aware Examples** – Updated example script demonstrates triplet creation and retrieval flows. + +## Phase 3 – Retrieval & Maintenance Enhancements (In Progress) +1. **Search Coverage** – Hybrid, vector-only, regex, exact-match, fuzzy, and LLM rerank helpers are implemented and exposed. + Graph-backed wrappers now rely on driver-side filtering, pagination, and aggregation before in-memory scoring. Next: push + similarity computation into Memgraph/Neo4j so vector rankings can execute server-side without Python hydration. +2. **Maintenance Tasks** – Tasks emit telemetry and persist consolidation/compression results. Consolidation planning enforces + batch/backoff thresholds and surfaces skipped groups. Next: validate heuristics on larger datasets and tune the thresholds with + real data. +3. **Importance Scoring Improvements** – Heuristic scoring is live and now records distribution metrics via telemetry. Next: + design data-driven evaluation loops or LLM-assisted ranking to tune weights over time. +4. **LLM Provider Flexibility** – Direct `openai` usage has been consolidated behind `meshmind.llm_client`. The wrapper + honors `LLM_*` environment variables, CLI flags, and REST/gRPC payload overrides (`use_llm_rerank`, `llm_models`, + `llm_base_urls`, `llm_api_key`, `rerank_model`). Timestamp utilities have been standardized on timezone-aware UTC defaults. + Next: validate live provider integrations once external credentials and network routing are provisioned. + +## Phase 4 – Developer Experience & Tooling (In Progress) +1. **Testing Overhaul** – Pytest suites rely on local fixtures, compatibility shims, and Celery workflow coverage. Graph-backed + retrieval, Neo4j connectivity shims, CLI admin helpers, and the documentation guard script now have dedicated tests; continue + adding cross-backend integration coverage and mirror shim retirement progress in `DUMMIES.md` so integration suites can + replace them incrementally. +2. **Automation & CI** – Makefile provides lint/format/type/test/docs-guard targets and CI runs fmt-check, docs guard, and + pytest. Add caching and matrix builds when dependencies stabilize. +3. **Environment Provisioning** – Docker Compose now provisions Memgraph, Neo4j, and Redis (with targeted stacks for tests). + Track multi-backend examples, document the new `SETUP.md`, keep docs current, and distribute the new `run/install_setup.sh` + and `run/maintenance_setup.sh` automation scripts for environment bootstrap. + +## Phase 5 – Strategic Enhancements (Planned) +1. **Graph-Backed Retrieval** – Extend the new driver-side filtering/pagination to full vector/lexical execution using backend-native indexes to avoid round-tripping candidate embeddings. +2. **Operational Observability** – Export telemetry to Prometheus/OpenTelemetry and surface dashboards/alerts. +3. **Celery Hardening** – Stress test consolidation/compression heuristics at scale and codify retry/backoff policies. +4. **Model Fidelity** – Replace compatibility shims with production-ready Pydantic models once dependency support catches up. diff --git a/PROJECT.md b/PROJECT.md new file mode 100644 index 0000000..5b153fb --- /dev/null +++ b/PROJECT.md @@ -0,0 +1,92 @@ +# MeshMind Project Overview + +## Vision and Scope +- Transform unstructured text into graph-backed `Memory` records enriched with embeddings and metadata. +- Offer pipelines for extraction, preprocessing, storage, and retrieval that can be orchestrated from CLI tools or bespoke agents. +- Enable background maintenance workflows (expiry, consolidation, compression) once supporting services are provisioned. + +## Current Architecture Snapshot +- **Client façade**: `meshmind.client.MeshMind` composes a provider-agnostic LLM client, configurable embedding model, registry + bootstrap, and a lazily created graph driver selected via `GRAPH_BACKEND` (memory, SQLite, Memgraph, Neo4j). Retrieval helpers expose + hybrid, vector, regex, exact, and reranked flows. +- **Pipelines**: Extraction (LLM + function calling), preprocessing (deduplicate, score, compress), and storage utilities live in + `meshmind.pipeline`. Maintenance helpers consolidate duplicates and expire stale memories. +- **Graph layer**: `meshmind.db` defines `GraphDriver` and implements in-memory, SQLite, Memgraph, and optional Neo4j drivers + with a shared factory for local testing and production use. +- **Retrieval helpers**: `meshmind.retrieval` now covers BM25, fuzzy, hybrid, vector-only, regex, exact-match, and LLM rerank + workflows with shared filters, reranker utilities, and driver-side filtering/pagination to minimise Python hydration. +- **Task runners**: `meshmind.tasks` configures Celery beat to run expiry, consolidation, and compression. Drivers/managers + initialize lazily so import-time failures are avoided. +- **Support code**: `meshmind.core` provides configuration, data models, embeddings, similarity math, and optional dependency + guards around tokenization. +- **Service adapters**: `meshmind.api.rest` and `.grpc` expose REST/gRPC entry points (with lightweight stubs for tests) so + ingestion and retrieval can run as services, including `/memories/counts` for namespace/label summaries. +- **Observability**: `meshmind.core.observability` collects metrics, gauges, and structured log events across pipelines and + scheduled tasks. +- **Tooling**: The CLI ingest command (`meshmind ingest`), updated example script, Makefile automation, CI workflow, and Docker + Compose file illustrate extraction → preprocessing → storage → retrieval locally. +- **Compatibility & fakes**: `_compat/pydantic` keeps models working when Pydantic is absent, while `meshmind/testing` provides fake Memgraph, Redis, and embedding drivers for offline test runs. + +## Implemented Capabilities +- Serialize knowledge as `Memory` (nodes) and `Triplet` (relationships) Pydantic models with namespaces, metadata, embeddings, + timezone-aware timestamps, TTL, and importance fields. +- Extract structured memories from text via the provider-agnostic `LLMClient` (Responses API compatible), with encoder registration handled during bootstrap. +- Deduplicate memories by name and cosine similarity, score importance heuristically (token diversity, recency, metadata, embedding magnitude), and compress metadata when `tiktoken` is installed. +- Persist memory nodes and triplet relationships through the storage pipeline and `MemoryManager` CRUD helpers. +- Configure LLM providers globally via `LLM_*` environment variables while supporting per-request overrides through CLI flags, + REST/gRPC payloads, and the shared `LLMClient` wrapper. +- Perform hybrid, vector-only, regex, exact-match, fuzzy, and BM25 retrieval with optional metadata filters and LLM reranking, leveraging driver-side filtering/pagination to shrink result sets before scoring. +- Summarize stored memories by namespace/entity label via the CLI (`meshmind admin counts`) and REST `/memories/counts` route. +- Provide CRUD surfaces on `MeshMind` for creating, updating, deleting, and listing memories and triplets. +- Run Celery maintenance tasks (expiry, consolidation, compression) that tolerate missing graph drivers until runtime and persist consolidated/compressed memories back to the selected backend. +- Demonstrate ingestion, relationship creation, and retrieval in `examples/extract_preprocess_store_example.py`. +- Automate linting, formatting, type checking, and testing through the Makefile and GitHub Actions. + +## Partially Implemented or Fragile Areas +- The LLM-backed embedding wrapper still assumes dictionary-style responses; adjust once SDK models are fully adopted. +- Neo4j driver support is import-guarded; the new CLI connectivity check still needs validation against a live cluster. +- Maintenance tasks rely on in-process heuristics for consolidation summaries; long-term storage and conflict resolution rules need validation. +- Importance scoring now records telemetry but still relies on heuristics; richer scoring logic or LLM-assisted ranking is pending. +- SQLite driver currently stores JSON blobs; future work may normalize columns for structured querying. + +## Missing or Broken Capabilities +- Graph-backed retrieval still hydrates namespace/entity-label filtered candidates client-side; pushing ranking into the graph store is future work. +- Predicate management remains internal to the bootstrap process; external administration APIs are still missing. +- Metrics remain in-memory; external exporters (Prometheus/OpenTelemetry) are not wired up. +- gRPC wiring currently relies on stubs; production-ready servers are still future work. +- Compatibility shims provide minimal validation and should be replaced with real Pydantic models in production builds; see + `DUMMIES.md` for a complete inventory and retirement plan. + +## External Services & Dependencies +- **Graph backend**: Choose via `GRAPH_BACKEND`. In-memory and SQLite require no external services. Memgraph needs the `pymgclient` package (which exposes the `mgclient` module); + Neo4j requires the official driver and a live instance. +- **LLM providers**: Install the OpenAI SDK (or compatible fork) for extraction, embeddings, and reranking; configure `LLM_API_KEY` (or fallback `OPENAI_API_KEY`) and the `LLM_*` model/base URL overrides as needed. +- **tiktoken**: Optional but necessary for compression/token budgeting. +- **RapidFuzz, scikit-learn, numpy**: Support fuzzy and lexical retrieval. +- **Celery + Redis**: Optional but necessary for scheduled maintenance jobs. +- **sentence-transformers**: Optional embedding backend for offline models. +- **ruff, pyright, typeguard, toml-sort, yamllint**: Development tooling invoked by the Makefile and CI workflow. + +## Tooling and Operational State +- `Makefile` exposes `install`, `lint`, `fmt`, `fmt-check`, `typecheck`, `test`, `check`, `docs-guard`, `docker`, and `clean` + targets. `make install` installs the `.[dev,docs,testing]` extras so optional dependencies are present. +- `.github/workflows/ci.yml` runs formatting/linting checks, the documentation guard, and pytest on push and pull requests. +- Tests rely on fixtures (`memory_factory`, `dummy_encoder`, in-memory drivers) and compatibility shims so they pass without external services, though installing optional dependencies improves fidelity. +- Developer-facing documentation now lives in `docs/` alongside the canonical `README.md`; the docs guard (`make docs-guard`) enforces synchronized updates when modules change. +- Docker Compose now provisions Memgraph, Neo4j, and Redis; integration-specific stacks (including the Celery worker) live under + `meshmind/tests/docker/`. See `ENVIRONMENT_NEEDS.md` and `SETUP.md` for enabling optional services locally. + +## Roadmap Highlights +- Push graph-backed retrieval deeper into the drivers (vector similarity, structured filters) so the new server-side filtering/pagination evolves into full backend-native ranking. +- Export observability metrics to external sinks (Prometheus/OpenTelemetry) and surface dashboards. +- Enhance importance scoring with data-driven heuristics or LLM evaluation. +- Validate consolidation heuristics and conflict-resolution rules against real datasets. +- Validate Neo4j driver behaviour against a live cluster and ship official test doubles for Memgraph/Redis/encoders. +- Continue refining documentation to reflect setup, troubleshooting, and architectural decisions. +- Reintroduce full Pydantic models once dependency availability is guaranteed in target environments. + +## Future Potential Extensions +- Plugin-based encoder and retriever registration for runtime extensibility. +- Streaming ingestion workers (queues, webhooks) beyond batch CLI workflows. +- UI or agent-facing dashboards for curation, monitoring, and analytics. +- Automated CI pipelines for release packaging, schema migrations, and integration tests. diff --git a/README.md b/README.md index 04882e7..7b72da1 100644 --- a/README.md +++ b/README.md @@ -1,244 +1,255 @@ # MeshMind -MeshMind is a knowledge management system that uses LLMs and graph databases to store and retrieve information. -Adding, Searching, Updating, and Deleting memories is supported. -Retrieval of memories using LLMs and graph databases by comparing embeddings and graph database metadata. - -## Features - -- Adding memories -- Searching memories -- Updating memories -- Deleting memories -- Extracting memories from content -- Expiring memories -- Memory Importance Ranking -- Memory Deduplication -- Memory Consolidation -- Memory Compression - -## Retrieval Methods - -- Embedding Vector Search -- BM25 Retrieval -- ReRanking with LLM -- Fuzzy Search -- Exact Comparison Search -- Regex Search -- Search Filters -- Hybrid Search Methods - -## Components -- OpenAI API -- MemGraphDB (Alternative to Neo4j) -- Embedding Model - -# Types of Memory - -- Long-Term Memory - Persistent Memory - - Explicit Memory - Conscious Memory - Active Hotpath Memory (Triggered in Response to Input) - - Declarative Memory - Conscious Memory - - Semantic Memory - What is known - - Eposodic Memory - What has been experienced - - Procedural Memory - How to perform tasks - - - Implicit Memory - Subconscious Memory - Background Process Memory (Triggered in Intervals) - - Non-Declarative Memory - Subconscious Memory - - Semantic Memory - Implicitly acquired - - Eposodic Memory - Implicitly acquired - - Procedural Memory - Implicitly acquired - -- Short-Term Memory - Transient Memory - - Working Memory (Processing) [reasoning messages, scratchpad, etc] - - Sensory Memory (Input) [user input, system input, etc] - - Log Storage (Output) [assistant responses, tool logs, etc] - -# Methods -- Extract Memory -- Add Memory -- Add Triplet -- Search Memory -- Update Memory -- Delete Memory - -## Usage - +MeshMind is an experimental memory orchestration service that pairs large language models with a property graph. It extracts +structured `Memory` records from unstructured text, enriches them with embeddings and metadata, and stores both nodes and +relationships via a Memgraph driver. Retrieval helpers operate on in-memory collections today, offering hybrid, vector-only, +regex, exact-match, fuzzy, and BM25 scoring with optional LLM reranking. + +## Status at a Glance +- ✅ `meshmind.client.MeshMind` orchestrates extraction, preprocessing, storage, CRUD helpers, and retrieval wrappers. +- ✅ Pipelines deduplicate memories, score importance with token/recency/metadata heuristics, compress metadata, and persist nodes and triplets. +- ✅ Retrieval helpers expose hybrid, vector-only, regex, exact-match, BM25, fuzzy, and rerank workflows with namespace/entity + filters. +- ✅ Celery tasks for expiry, consolidation, and compression initialize lazily and run when Redis and Memgraph are configured. +- ✅ Makefile and GitHub Actions provide linting, formatting, type checking, and pytest automation. +- ✅ Docker Compose provisions Memgraph, Neo4j, and Redis for local orchestration, with + integration-specific stacks under `meshmind/tests/docker/` for Celery workers. +- ✅ Built-in observability surfaces structured events and in-memory metrics for pipelines and scheduled tasks while Celery consolidation/compression flows now persist their updates. +- ✅ Compatibility shims and fake drivers let the suite run without Pydantic, scikit-learn, rapidfuzz, Redis, or live Memgraph instances. +- ✅ Graph-backed retrieval wrappers load memories directly from the configured driver when collections are omitted. +- ✅ Graph drivers filter by namespace and entity label before hydrating candidates, keeping hybrid searches efficient on large graphs. + +## Requirements +- Python 3.11 or 3.12 recommended (`pyproject.toml` pins `>=3.11,<3.13` while third-party packages catch up). +- Configurable graph backend via `GRAPH_BACKEND` (`memory`, `sqlite`, `memgraph`, `neo4j`). +- Memgraph instance reachable via Bolt and the `pymgclient` Python package (which exposes the `mgclient` module for + `GRAPH_BACKEND=memgraph`). +- Optional Neo4j instance with the official Python driver (when `GRAPH_BACKEND=neo4j`). +- OpenAI-compatible API key (set `LLM_API_KEY` or `OPENAI_API_KEY`) for extraction, embeddings, and optional reranking. +- Optional: Redis and Celery for scheduled maintenance tasks. +- Optional: `numpy`, `scikit-learn`, and `rapidfuzz` improve similarity and lexical matching but pure-Python fallbacks are bundled. +- Install project dependencies with `pip install -e .[dev,docs,testing]`; see `SETUP.md` for a detailed walkthrough. + +## Installation +> **Tip:** For automated provisioning with internet access, run `./run/install_setup.sh` from the project root (requires sudo). + +1. Create and activate a virtual environment using Python 3.11/3.12 (e.g., `uv venv`, `python -m venv .venv`). +2. Upgrade `pip` and install MeshMind with all optional extras: + ```bash + python -m pip install --upgrade pip + pip install uv + uv pip install --system -e .[dev,docs,testing] # drop --system if you're inside a virtualenv + ``` +3. Export required environment variables (or populate `.env`; see `SETUP.md`): + ```bash + export OPENAI_API_KEY=sk-... + export LLM_API_KEY=${LLM_API_KEY:-$OPENAI_API_KEY} + export LLM_DEFAULT_MODEL=gpt-5-nano + export LLM_DEFAULT_BASE_URL=https://api.openai.com/v1 + export LLM_EXTRACTION_MODEL=gpt-5-nano + export LLM_EXTRACTION_BASE_URL= + export LLM_EMBEDDING_MODEL=text-embedding-3-small + export LLM_EMBEDDING_BASE_URL= + export LLM_RERANK_MODEL=gpt-5-nano + export LLM_RERANK_BASE_URL= + export GRAPH_BACKEND=memory # or memgraph/sqlite/neo4j + export MEMGRAPH_URI=bolt://localhost:7687 + export MEMGRAPH_USERNAME= # optional; Memgraph defaults to anonymous auth + export MEMGRAPH_PASSWORD= + export SQLITE_PATH=/tmp/meshmind.db + export NEO4J_URI=bolt://localhost:7688 + export NEO4J_USERNAME=neo4j + export NEO4J_PASSWORD=meshminD123 + export REDIS_URL=redis://localhost:6379/0 + export EMBEDDING_MODEL=${LLM_EMBEDDING_MODEL} + ``` + +4. Provision Redis, Memgraph, and Neo4j with Docker Compose when you need external + services: + ```bash + docker compose up -d + ``` + Alternate topologies live in `meshmind/tests/docker/`; see `SETUP.md` for guidance on + targeted stacks and teardown commands. + +## Encoder Registration +`MeshMind` bootstraps encoders and entities during initialization, but custom scripts can register additional encoders: ```python -from meshmind import MeshMind -from pydantic import BaseModel, Field - -# Pydantic model's name will be used as the node label in the graph database. -# This defines the attributes of the entity. -# Attributes of the entity are stored in entity metadata['attributes'] -class Person(BaseModel): - first_name: str | None = Field(..., description="First name of the person") - last_name: str | None = Field(None, description="Last name of the person") - description: str | None = Field(None, description="Description of the person") - job_title: str | None = Field(None, description="Job title of the person") - -# Initialize MeshMind -mesh_mind = MeshMind() - -# Register pydantic model as entity -mesh_mind.register_entity(Person) - -# Register allowed relationship predicates labels. -mesh_mind.register_allowed_predicates([ - "employee_of", - "on_team", - "on_project", -]) - -# Add edge - This will create an edge in the graph database. (Alternative to `register_allowed_types`) -mesh_mind.add_predicate("has_skill") - -# Extract memories - [High Level API] - This will create nodes and edges based on the content provided. Extracts memories from the content. -extracted_memories = mesh_mind.extract_memories( - instructions="Extract all memories from the database.", - namespace="Company Employees", - entity_types=[Person], - content=["John Doe, Software Engineer 10 years of experience.", "Jane Doe, Software Engineer 5 years of experience."] - ) +from meshmind.core.embeddings import EncoderRegistry, OpenAIEmbeddingEncoder -for memory in extracted_memories: - # Store memory - This will perform deduplication, add uuid, add timestamps, format memory and store the memory in the graph database using `add_triplet`. - mesh_mind.store_memory(memory) - -# Add memory - [Mid Level API] - This will perform all preprocessing steps i.e. deduplication, add uuid, add timestamps, format memory, etc. and store the memory in the graph database using `add_triplet`. ( Skips extraction and automatically adds memory to the graph database ) **Useful for adding custom memories** -mesh_mind.add_memory( - namespace="Company Employees", - name="John Doe", - entity_label="Person", - entity=Person( - first_name="John", - last_name="Doe", - description="John Doe", - job_title="Software Engineer", - ), - metadata={ - "source": "John Doe Employee Record", - "source_type": "text", - } -) +if not EncoderRegistry.is_registered("text-embedding-3-small"): + EncoderRegistry.register("text-embedding-3-small", OpenAIEmbeddingEncoder("text-embedding-3-small")) +``` +You may register deterministic or local encoders (e.g., sentence-transformers) for offline testing. The +`OpenAIEmbeddingEncoder` now delegates to the provider-agnostic `meshmind.llm_client.LLMClient`, so any +OpenAI-compatible endpoint can serve embeddings once configured via environment variables or CLI overrides. -# `add_triplet` - [Low Level API] -# -# Add a [node, edge, node] triplet - This will create a pair of nodes and a connecting edge in the graph database using db driver. -# This bypasses deduplication and other preprocessing steps and directly adds the data to the graph database. -# This is useful for adding data that is not in the format of a memory. -# -# subject: The subject of the triplet. ( Source Entity Node Label Name ) -# predicate: The predicate of the triplet. ( Relationship between the subject and object, registered using `register_allowed_predicates` ) -# object: The object of the triplet. ( Target Entity Node Label Name ) -# namespace: The namespace of the triplet. ( Group of related nodes and edges ) -# entity_label: The label of the entity. ( Type of node, registered using `register_entity` ) -# metadata: The metadata of the triplet. ( Additional information about the triplet ) -# reference_time: The time at which the triplet was created. ( Optional ) -# -# If the subject, predicate, object, namespace, or entity_label does not exist, it will be created. -# -# Example: -mesh_mind.add_triplet( - subject="John Doe", - predicate="on_project", - object="Project X", - namespace="Company Employees", - entity_label="Person", - metadata={ - "source": "John Doe Project Record", - "source_type": "text", - "summary": "John Doe is on project X.", - "attributes": { - "first_name": "John", - "last_name": "Doe", - "description": "John Doe", - "job_title": "Software Engineer", - } - }, - reference_time="2025-05-09T23:31:51-04:00" +## Quick Start +```python +from meshmind.client import MeshMind +from meshmind.core.types import Memory, Triplet + +mm = MeshMind() # Uses LLM defaults from LLM_* environment variables +texts = ["Python is a programming language created by Guido van Rossum."] +memories = mm.extract_memories( + instructions="Extract key facts as Memory objects.", + namespace="demo", + entity_types=[Memory], + content=texts, ) - -# Search - [High Level API] - This will search the graph database for nodes and edges based on the query. -search_results = mesh_mind.search( - query="John Doe", - namespace="Company Employees", - entity_types=[Person], +memories = mm.deduplicate(memories) +memories = mm.score_importance(memories) +memories = mm.compress(memories) +mm.store_memories(memories) + +if len(memories) >= 2: + relation = Triplet( + subject=str(memories[0].uuid), + predicate="RELATED_TO", + object=str(memories[1].uuid), + namespace="demo", + entity_label="Knowledge", ) + mm.store_triplets([relation]) +``` -for search_result in search_results: - print(search_result) - -# Search - [Mid Level API] - This will search the graph database for nodes and edges based on the query. -search_results = mesh_mind.search_facts( - query="John Doe", - namespace="Company Employees", - entity_types=[Person], - config=SearchConfig( - encoder="text-embedding-3-small", - ) - ) +## Retrieval +`MeshMind` exposes multiple retrieval helpers that operate on lists of `Memory` objects (e.g., fetched via +`mm.list_memories(namespace="demo")`). When you omit the `memories` argument, the client fetches candidates directly from the +active graph backend configured through `GRAPH_BACKEND` or the driver supplied to `MeshMind`. Driver-backed searches now use +server-side filtering (`query`, `entity_labels`) and pagination (`offset`, `limit`) before in-memory scoring to avoid loading +entire namespaces. +```python +from meshmind.core.types import SearchConfig -for search_result in search_results: - print(search_result) - -# Search - [Low Level API] - This will search the graph database for nodes and edges based on the query. -search_results = mesh_mind.search_procedures( - query="John Doe", - namespace="Company Employees", - entity_types=[Person], - config=SearchConfig( - encoder="text-embedding-3-small", - ) - ) +memories = mm.list_memories(namespace="demo", entity_labels=["Knowledge"], limit=25) +config = SearchConfig(encoder=mm.embedding_model, top_k=5, rerank_model="gpt-5-nano") -# Update Memory - Same as `add_memory` but updates an existing memory. -mesh_mind.update_memory( - uuid="12345678-1234-1234-1234-123456789012", - namespace="Company Employees", - name="John Doe", - entity_label="Person", - entity=Person( - first_name="John", - last_name="Doe", - description="John Doe", - job_title="Software Engineer", - ), - metadata={ - "source": "John Doe Employee Record", - "source_type": "text", - } -) +hybrid = mm.search("Python", namespace="demo", entity_labels=["Knowledge"], config=config, use_llm_rerank=True) +vector_only = mm.search_vector("programming", namespace="demo", entity_labels=["Knowledge"]) +regex_hits = mm.search_regex(r"Guido", namespace="demo", entity_labels=["Knowledge"]) +exact_hits = mm.search_exact("Python", namespace="demo", entity_labels=["Knowledge"]) +``` +The in-memory and graph-backed search helpers support namespace/entity filters and optional reranking via the +provider-agnostic `LLMClient`. Entity-label filters are applied at the driver, avoiding unnecessary hydration when graphs +contain heterogeneous nodes. You can still pass explicit lists (e.g., during testing) to bypass graph access when desired. -# Delete Memory -mesh_mind.delete_memory( - uuid="12345678-1234-1234-1234-123456789012" -) +## Command-Line Operations +```bash +meshmind ingest \ + --namespace demo \ + --instructions "Extract key facts as Memory objects." \ + ./path/to/text/files +``` +The CLI bootstraps encoders/entities automatically. Ensure environment variables are set and Memgraph is reachable. +LLM connectivity is configurable per operation: -for search_result in search_results: - print(search_result) +- `--llm-api-key` and `--llm-base-url` override the provider credentials for the entire run. +- `--extraction-model` / `--extraction-endpoint` adjust the Responses API call that performs entity extraction. +- `--embedding-model` / `--embedding-endpoint` override encoder generation (defaults to `LLM_EMBEDDING_MODEL`). +- `--rerank-model` / `--rerank-endpoint` control reranking when `use_llm_rerank=True` is provided to `MeshMind.search`. -``` +### LLM Override Precedence -## Command-Line Interface (CLI) +The `meshmind.llm_client.LLMClient` merges configuration from multiple sources before issuing +Requests API/Embeddings calls. The precedence order is: -MeshMind includes a `meshmind` CLI tool for ingesting content via the extract → preprocess → store pipeline. +1. **Explicit payload overrides** – REST/gRPC requests may include `llm_models`, `llm_base_urls`, and `llm_api_key` + dictionaries; CLI commands surface equivalent `--extraction-*`, `--embedding-*`, and `--rerank-*` flags. These values + apply to the single request being processed. +2. **Environment variables** – `LLM_*` variables (and the legacy `OPENAI_API_KEY`) define the default provider, model, and + endpoint when no per-request overrides are supplied. +3. **Built-in defaults** – The client falls back to `gpt-5-nano` for Responses calls and `text-embedding-3-small` for + embeddings when neither payload nor environment values exist. -Usage: -```bash -meshmind --help -``` -Primary command: +This layering allows operators to set safe defaults globally while giving automation and human workflows the ability to +experiment with alternative providers on demand. + +Administrative helpers expose predicate registry and telemetry insight: ```bash -meshmind ingest \ - -n \ - [-e ] \ - [-i ""] \ - [ ...] +meshmind admin predicates --list +meshmind admin predicates --add RELATED_TO +meshmind admin maintenance +meshmind admin graph --backend neo4j +meshmind admin counts --namespace demo ``` -Example: + +## Maintenance Tasks +Celery tasks in `meshmind.tasks.scheduled` provide expiry, consolidation, and compression maintenance with persistence. ```bash -meshmind ingest -n demo --embedding-model text-embedding-3-small ./data/articles +celery -A meshmind.tasks.celery_app.app worker -B ``` -This reads text files under `./data/articles`, extracts memories, deduplicates, scores, compresses, -and stores them in your Memgraph database under the `demo` namespace. - +Tasks instantiate the driver lazily, emit structured logs/metrics, and persist consolidated or compressed memories back to the selected graph driver. Provide valid environment variables and ensure Memgraph/Redis are running when using external backends. + +## Tooling +- **Makefile** – `make fmt`, `make lint`, `make typecheck`, `make test`, `make check`, `make docker`, `make clean`, + `make docs-guard`. +- **CI** – `.github/workflows/ci.yml` runs formatting checks (ruff, toml-sort, yamllint), pytest, and the documentation guard + to ensure code changes keep the wiki up to date. +- **Examples** – `examples/extract_preprocess_store_example.py` demonstrates ingestion, triplet creation, and multiple retrieval + strategies. +- **Dockerfile / docker-compose** – Container definition and orchestration files that provision Memgraph, Neo4j, Redis, and the + Celery worker stacks documented in `SETUP.md` and `meshmind/tests/docker/`. +- **Provisioning scripts** – `run/install_setup.sh` and `run/maintenance_setup.sh` validate that optional packages (`fastapi`, + `neo4j`, `pymgclient`, `uvicorn`) are present and respect `MESH_SKIP_SYSTEM_PACKAGES=1` / `MESH_SKIP_PYTHON_SYNC=1` when you + need a dry run without network access. + +## Service Interfaces +- **REST** – `meshmind.api.rest.create_app` returns a FastAPI app (or lightweight stub) that exposes `/memories`, `/triplets`, + and `/search` endpoints. Search payloads accept: + - `use_llm_rerank` to toggle LLM-based reranking, + - `llm_models`, `llm_base_urls`, and `llm_api_key` dictionaries for per-request overrides, + - `rerank_model` when you need to pin the reranker explicitly. + Example: + ```json + { + "query": "architecture", + "namespace": "demo", + "entity_labels": ["Knowledge"], + "top_k": 5, + "use_llm_rerank": true, + "llm_models": {"rerank": "openrouter/reranker-v1"}, + "llm_base_urls": {"rerank": "https://openrouter.ai/api/v1"} + } + ``` +- **gRPC** – `meshmind.api.grpc.GrpcServiceStub` mirrors the ingestion and retrieval RPC surface for integration tests and + future server wiring. `SearchRequest` now carries the same LLM override fields as the REST payload so clients can experiment + with alternative endpoints or models without diverging code paths. + +## Developer Documentation +- `docs/overview.md` – high-level module map. +- `docs/persistence.md` – graph driver behaviours and configuration. +- `docs/retrieval.md` – search strategies and entity-label filtering semantics. +- `docs/pipelines.md` – ingestion lifecycle. +- `docs/api.md` – REST/gRPC payloads and CLI expectations. +- `docs/testing.md` – pytest layout and fake drivers. +- `docs/configuration.md` – environment variables and defaults. +- `docs/operations.md` & `docs/telemetry.md` – operational workflows and observability guidance. +- `docs/development.md` – contribution workflow and coding standards. +- `DUMMIES.md` – inventory of temporary shims, fakes, and compatibility layers to retire now that full dependencies are + available. + +## Observability +- `meshmind.core.observability.telemetry` collects counters, gauges, and durations for pipelines and Celery tasks. +- `meshmind.core.observability.log_event` emits structured log messages that annotate pipeline progress. +- Metrics remain in-memory today; export hooks (Prometheus, OpenTelemetry) are future enhancements. + +## Compatibility & Test Doubles +- `meshmind/_compat/pydantic.py` provides a lightweight `BaseModel` implementation so the codebase functions without installing Pydantic. +- `meshmind/retrieval/bm25.py`, `meshmind/retrieval/fuzzy.py`, and `meshmind/core/similarity.py` include pure-Python fallbacks for scikit-learn, rapidfuzz, and numpy. +- `meshmind/testing` exports fake Memgraph, Redis, and embedding drivers that power the pytest suite and examples without external infrastructure. +- `DUMMIES.md` lists every remaining stub (REST/gRPC service adapters, Celery fallbacks, compatibility layers) with guidance + on whether to remove or preserve them once external services are provisioned. + +## Testing +- Run `pytest` to execute the suite; tests rely on fixtures, fake drivers, and compatibility shims so they do not require external services or optional libraries. +- `make typecheck` invokes `pyright` and `typeguard`; install the tooling listed above beforehand. +- See `ENVIRONMENT_NEEDS.md` for environment requirements and known blockers (Docker/Memgraph/Redis availability). + +## Known Limitations +- Graph-backed retrieval still hydrates candidates client-side (now filtered by namespace and entity label); server-side vector search remains future work. +- Metrics remain in-memory; no external exporter is wired up yet. +- Importance scoring uses heuristics and telemetry but does not yet incorporate feedback loops or LLM-assisted ranking. + +## Roadmap Snapshot +Consult `PROJECT.md`, `PLAN.md`, and `RECOMMENDATIONS.md` for prioritized enhancements: graph-backed retrieval, metrics exporters, richer importance scoring, and production-ready service deployments. diff --git a/README_OLD.md b/README_OLD.md new file mode 100644 index 0000000..e0f86dc --- /dev/null +++ b/README_OLD.md @@ -0,0 +1,170 @@ +# MeshMind + +MeshMind is an experimental memory orchestration service that pairs large language models with a property graph. It extracts +structured `Memory` records from unstructured text, enriches them with embeddings and metadata, and stores both nodes and +relationships via a Memgraph driver. Retrieval helpers operate on in-memory collections today, offering hybrid, vector-only, +regex, exact-match, fuzzy, and BM25 scoring with optional LLM reranking. + +## Status at a Glance +- ✅ `meshmind.client.MeshMind` orchestrates extraction, preprocessing, storage, CRUD helpers, and retrieval wrappers. +- ✅ Pipelines deduplicate memories, score importance with token/recency/metadata heuristics, compress metadata, and persist nodes and triplets. +- ✅ Retrieval helpers expose hybrid, vector-only, regex, exact-match, BM25, fuzzy, and rerank workflows with namespace/entity + filters. +- ✅ Celery tasks for expiry, consolidation, and compression initialize lazily and run when Redis and Memgraph are configured. +- ✅ Makefile and GitHub Actions provide linting, formatting, type checking, and pytest automation. +- ✅ Docker Compose provisions Memgraph, Redis, and a Celery worker for local orchestration. +- ✅ Built-in observability surfaces structured events and in-memory metrics for pipelines and scheduled tasks while Celery consolidation/compression flows now persist their updates. +- ✅ Compatibility shims and fake drivers let the suite run without Pydantic, scikit-learn, rapidfuzz, Redis, or live Memgraph instances. +- ✅ Graph-backed retrieval wrappers load memories directly from the configured driver when collections are omitted. + +## Requirements +- Python 3.11 or 3.12 recommended (`pyproject.toml` pins `>=3.11,<3.13` while third-party packages catch up). +- Configurable graph backend via `GRAPH_BACKEND` (`memory`, `sqlite`, `memgraph`, `neo4j`). +- Memgraph instance reachable via Bolt and the `pymgclient` Python package (which provides the runtime `mgclient` module when + `GRAPH_BACKEND=memgraph`). +- Optional Neo4j instance with the official Python driver (when `GRAPH_BACKEND=neo4j`). +- OpenAI API key for extraction, embeddings, and optional reranking. +- Optional: Redis and Celery for scheduled maintenance tasks. +- Optional: `numpy`, `scikit-learn`, and `rapidfuzz` improve similarity and lexical matching but pure-Python fallbacks are bundled. +- Install project dependencies with `pip install -e .`; see `pyproject.toml` for the full list. + +## Installation +1. Create and activate a virtual environment using Python 3.11/3.12 (e.g., `uv venv`, `python -m venv .venv`). +2. Install MeshMind: + ```bash + pip install -e . + ``` +3. Install optional dependencies as needed: + ```bash + pip install pymgclient tiktoken sentence-transformers celery[redis] ruff pyright typeguard toml-sort yamllint + ``` +4. Export required environment variables: + ```bash + export OPENAI_API_KEY=sk-... + export GRAPH_BACKEND=memory # or memgraph/sqlite/neo4j + export MEMGRAPH_URI=bolt://localhost:7687 + export MEMGRAPH_USERNAME=neo4j + export MEMGRAPH_PASSWORD=secret + export SQLITE_PATH=/tmp/meshmind.db + export NEO4J_URI=bolt://localhost:7687 + export NEO4J_USERNAME=neo4j + export NEO4J_PASSWORD=secret + export REDIS_URL=redis://localhost:6379/0 + export EMBEDDING_MODEL=text-embedding-3-small + ``` + +## Encoder Registration +`MeshMind` bootstraps encoders and entities during initialization, but custom scripts can register additional encoders: +```python +from meshmind.core.embeddings import EncoderRegistry, OpenAIEmbeddingEncoder + +if not EncoderRegistry.is_registered("text-embedding-3-small"): + EncoderRegistry.register("text-embedding-3-small", OpenAIEmbeddingEncoder("text-embedding-3-small")) +``` +You may register deterministic or local encoders (e.g., sentence-transformers) for offline testing. + +## Quick Start +```python +from meshmind.client import MeshMind +from meshmind.core.types import Memory, Triplet + +mm = MeshMind() +texts = ["Python is a programming language created by Guido van Rossum."] +memories = mm.extract_memories( + instructions="Extract key facts as Memory objects.", + namespace="demo", + entity_types=[Memory], + content=texts, +) +memories = mm.deduplicate(memories) +memories = mm.score_importance(memories) +memories = mm.compress(memories) +mm.store_memories(memories) + +if len(memories) >= 2: + relation = Triplet( + subject=str(memories[0].uuid), + predicate="RELATED_TO", + object=str(memories[1].uuid), + namespace="demo", + entity_label="Knowledge", + ) + mm.store_triplets([relation]) +``` + +## Retrieval +`MeshMind` exposes multiple retrieval helpers that operate on lists of `Memory` objects (e.g., fetched via +`mm.list_memories(namespace="demo")`). When you omit the `memories` argument, the client fetches candidates directly from the +active graph backend configured through `GRAPH_BACKEND` or the driver supplied to `MeshMind`. +```python +from meshmind.core.types import SearchConfig + +memories = mm.list_memories(namespace="demo") +config = SearchConfig(encoder=mm.embedding_model, top_k=5, rerank_model="gpt-4o-mini") + +hybrid = mm.search("Python", namespace="demo", config=config, use_llm_rerank=True) +vector_only = mm.search_vector("programming", namespace="demo") +regex_hits = mm.search_regex(r"Guido", namespace="demo") +exact_hits = mm.search_exact("Python", namespace="demo") +``` +The in-memory and graph-backed search helpers support namespace/entity filters and optional reranking via the OpenAI Responses +API. You can still pass explicit lists (e.g., during testing) to bypass graph access when desired. + +## Command-Line Operations +```bash +meshmind ingest \ + --namespace demo \ + --instructions "Extract key facts as Memory objects." \ + ./path/to/text/files +``` +The CLI bootstraps encoders/entities automatically. Ensure environment variables are set and Memgraph is reachable. + +Administrative helpers expose predicate registry and telemetry insight: +```bash +meshmind admin predicates --list +meshmind admin predicates --add RELATED_TO +meshmind admin maintenance +meshmind admin graph --backend neo4j +``` + +## Maintenance Tasks +Celery tasks in `meshmind.tasks.scheduled` provide expiry, consolidation, and compression maintenance with persistence. +```bash +celery -A meshmind.tasks.celery_app.app worker -B +``` +Tasks instantiate the driver lazily, emit structured logs/metrics, and persist consolidated or compressed memories back to the selected graph driver. Provide valid environment variables and ensure Memgraph/Redis are running when using external backends. + +## Tooling +- **Makefile** – `make fmt`, `make lint`, `make typecheck`, `make test`, `make check`, `make docker`, `make clean`. +- **CI** – `.github/workflows/ci.yml` runs formatting checks (ruff, toml-sort, yamllint) and pytest on push/PR. +- **Examples** – `examples/extract_preprocess_store_example.py` demonstrates ingestion, triplet creation, and multiple retrieval + strategies. + +## Service Interfaces +- **REST** – `meshmind.api.rest.create_app` returns a FastAPI app (or lightweight stub) that exposes `/memories`, `/triplets`, + and `/search` endpoints. +- **gRPC** – `meshmind.api.grpc.GrpcServiceStub` mirrors the ingestion and retrieval RPC surface for integration tests and + future server wiring. + +## Observability +- `meshmind.core.observability.telemetry` collects counters, gauges, and durations for pipelines and Celery tasks. +- `meshmind.core.observability.log_event` emits structured log messages that annotate pipeline progress. +- Metrics remain in-memory today; export hooks (Prometheus, OpenTelemetry) are future enhancements. + +## Compatibility & Test Doubles +- `meshmind/_compat/pydantic.py` provides a lightweight `BaseModel` implementation so the codebase functions without installing Pydantic. +- `meshmind/retrieval/bm25.py`, `meshmind/retrieval/fuzzy.py`, and `meshmind/core/similarity.py` include pure-Python fallbacks for scikit-learn, rapidfuzz, and numpy. +- `meshmind/testing` exports fake Memgraph, Redis, and embedding drivers that power the pytest suite and examples without external infrastructure. + +## Testing +- Run `pytest` to execute the suite; tests rely on fixtures, fake drivers, and compatibility shims so they do not require external services or optional libraries. +- `make typecheck` invokes `pyright` and `typeguard`; install the tooling listed above beforehand. +- See `NEEDED_FOR_TESTING.md` for environment requirements and known blockers (Docker/Memgraph/Redis availability). + +## Known Limitations +- Graph-backed retrieval loads namespace-scoped candidates into memory before scoring; server-side vector search remains future work. +- Metrics remain in-memory; no external exporter is wired up yet. +- Importance scoring uses heuristics and telemetry but does not yet incorporate feedback loops or LLM-assisted ranking. + +## Roadmap Snapshot +Consult `PROJECT.md`, `PLAN.md`, and `RECOMMENDATIONS.md` for prioritized enhancements: graph-backed retrieval, metrics exporters, richer importance scoring, and production-ready service deployments. diff --git a/RECOMMENDATIONS.md b/RECOMMENDATIONS.md new file mode 100644 index 0000000..2a7c484 --- /dev/null +++ b/RECOMMENDATIONS.md @@ -0,0 +1,38 @@ +# Recommendations + +## Stabilize the Foundation +- Maintain lazy initialization for optional dependencies and continue testing environments without Memgraph or LLM provider access. +- Maintain declared Python support at `>=3.11,<3.13` and monitor dependency releases before widening the range. +- Harden the LLM-backed embedding adapter to consume SDK response objects directly and surface actionable errors for rate limits. +- Add automated smoke tests for the new SQLite/Neo4j drivers to ensure regressions are caught early. +- Use `DUMMIES.md` to track compatibility layers and schedule their removal once real dependencies are part of the default + bootstrap path. + +## Restore and Extend Functionality +- Extend the new server-side filtering and pagination work by pushing similarity ranking into Memgraph/Neo4j so vector scoring runs without loading namespaces in Python. +- Validate consolidation heuristics at scale and tune the new batch/backoff thresholds before enabling automated writes in production. +- Introduce evaluation loops for the new importance heuristic (e.g., LLM-assisted ranking or analytics-driven weights) to tune thresholds over time, leveraging the telemetry stats now emitted. +- Exercise the new `llm_client` overrides via REST/gRPC integration smoke tests (once credentials are available) to confirm per-request models/endpoints behave consistently outside unit tests. +- Expand predicate/registry management APIs beyond the CLI helper so services can manage vocabularies programmatically. +- Plan for reintroducing full Pydantic models once packaging support is aligned with target Python versions. + +## Improve Developer Experience +- Document usage patterns for each graph backend (memory/sqlite/memgraph/neo4j) inside `docs/` and keep the docs-guard mapping current so contributors know which pages to update when modules change. +- Add Makefile targets for running Celery workers and seeding demo data once infrastructure is provisioned (potentially reusing + the new Docker Compose stacks). +- Broaden pytest coverage with cross-backend integration tests (Memgraph/Neo4j) and failure injection to complement the new graph retrieval, CLI admin, and docs guard unit tests. +- Cache dependencies and split lint/test jobs in CI for faster feedback once the dependency stack stabilizes. +- Maintain the new `run/install_setup.sh` and `run/maintenance_setup.sh` automation scripts alongside provisioning docs so environment bootstrap stays reproducible, and document timezone-aware timestamp expectations when integrating with downstream stores. + +## Documentation & Onboarding +- Keep `README.md`, `SOT.md`, `docs/`, and onboarding guides synchronized with each release; document rerank, retrieval, and + registry flows with diagrams when possible. +- Maintain the troubleshooting section for optional tooling (ruff, pyright, typeguard, toml-sort, yamllint) now referenced in + the Makefile and expand it as new developer utilities are introduced. Keep `SETUP.md` synchronized when dependencies change. +- Provide walkthroughs for configuring LLM reranking, including sample prompts and response expectations. +- Add onboarding notes for the REST/gRPC service layers with sample payloads and curl/grpcurl snippets. + +## Future Enhancements +- Export telemetry to Prometheus/OpenTelemetry and wire alerts/dashboards around ingestion and maintenance. +- Explore streaming ingestion pipelines (queues, webhooks) for near-real-time updates. +- Investigate lightweight web UI tooling for inspecting memories, triplets, and telemetry snapshots. diff --git a/RESUME_NOTES.md b/RESUME_NOTES.md new file mode 100644 index 0000000..fc30d97 --- /dev/null +++ b/RESUME_NOTES.md @@ -0,0 +1,50 @@ +# Resume Notes + +## Current Context + +- Branch: `work` (target PR branch: `integration`). +- Optional dependencies are bundled in the `.[dev,docs,testing]` extras, covering REST (`fastapi`, `uvicorn`), graph drivers + (`neo4j`, `pymgclient`, `redis`), Celery, LLM tooling, and developer linters. The provisioning scripts under `run/` + synchronize from `uv.lock` and verify these extras before attempting installs. +- Docker orchestration (root `docker-compose.yml` and the files in `meshmind/tests/docker/`) provisions Memgraph, Neo4j, Redis, + and optional Celery workers. Keep these references handy for live integration testing once container access is available. +- Documentation guard tooling requires that code edits touching modules with wiki pages also update the relevant files in + `docs/`. Planning artifacts (`PLAN.md`, `PROJECT.md`, `SOT.md`, etc.) are refreshed every iteration per agent instructions. + +## Latest Changes + +- Documented the LLM override cascade across `README.md`, `docs/api.md`, and `docs/configuration.md`, including precedence + notes and example payloads/CLI flags. +- Updated operations guides (`SETUP.md`, `docs/operations.md`) to describe the provisioning script validation flow and skip + flags; highlighted the pytest smoke tests that cover these scripts. +- Refreshed planning/backlog documents (`SOT.md`, `PLAN.md`, `PROJECT.md`, `RECOMMENDATIONS.md`, `FINDINGS.md`, `ISSUES.md`) + with timezone-aware timestamp notes and the new per-request override workflow. +- Extended `DUMMIES.md` and `docs/testing.md` to capture the `FakeLLMClient` behaviour and the setup script smoke-test + coverage; updated `ENVIRONMENT_NEEDS.md` and `NEEDED_FOR_TESTING.md` to acknowledge that optional packages now install with + network access. + +## Environment State + +- External graph services (Neo4j, Memgraph) and Redis are still unavailable inside this sandbox; integration tests depend on + fakes or SQLite until infrastructure is provisioned. +- Internet access is currently available; optional packages (`neo4j`, `pymgclient`, `fastapi`, `uvicorn`, etc.) are installed + via `uv pip`. Maintain this access so `uv.lock` can be regenerated and provisioning scripts remain effective. +- Timezone defaults across pipeline utilities now emit timezone-aware UTC timestamps; existing persisted data should be + reviewed once live backends are in play. + +## Next Session Starting Points + +1. Work through the remaining `TODO.md` priority items that are unblocked by missing infrastructure (e.g., research tasks may + remain pending until live services exist). +2. Validate Neo4j connectivity end-to-end once a reachable instance is available, using `meshmind admin graph --backend neo4j`. +3. Plan integration tests for the LLM override payloads against a real provider when credentials are provisioned; update + `docs/testing.md` accordingly. +4. Continue chipping away at shim retirements documented in `DUMMIES.md`, starting with replacing the Pydantic compatibility + layer when production targets allow the real dependency. + +## Helpful References + +- `docs/overview.md` for the module map and terminology. +- `docs/configuration.md` and `docs/operations.md` for environment variables and provisioning details. +- `SETUP.md`, `ENVIRONMENT_NEEDS.md`, and `NEEDED_FOR_TESTING.md` for onboarding and infrastructure expectations. +- `TODO.md` and `ISSUES.md` for the prioritized backlog and outstanding blockers. diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..de69bfc --- /dev/null +++ b/SETUP.md @@ -0,0 +1,125 @@ +# Setup Guide + +This guide walks through preparing a MeshMind development machine, provisioning the +external graph/cache services, and validating that the environment is ready for local +execution and automated testing. + +## 1. Prerequisites + +- **Operating system**: Linux or macOS with Docker Engine ≥ 24 and Docker Compose v2. +- **Python**: CPython 3.11.x (the project currently supports 3.11 and 3.12). +- **System packages** (only required if you intend to install optional graph drivers): + - Build tooling: `build-essential`, `cmake`, `git`. +- Crypto/Kerberos headers: `libssl-dev`, `libkrb5-dev` (needed to compile `pymgclient`, which provides the `mgclient` module). + - Optional: `libopenblas-dev` for faster `numpy`/`scikit-learn` builds on Debian/Ubuntu. +- **Automation scripts**: `run/install_setup.sh` bootstraps a fresh environment; `run/maintenance_setup.sh` refreshes a cached + workspace. Each script checks that the optional extras (`fastapi`, `neo4j`, `pymgclient`, `uvicorn`) are declared in + `pyproject.toml`, installs `uv` if necessary, and either synchronizes from `uv.lock` or performs a validation-only dry run + when `MESH_SKIP_SYSTEM_PACKAGES=1` and/or `MESH_SKIP_PYTHON_SYNC=1` are set. The pytest suite exercises this behaviour in + `meshmind/tests/test_setup_scripts.py`. + +## 2. Bootstrap the Python environment + +1. Create and activate a virtual environment: + + ```bash + python3.11 -m venv .venv + source .venv/bin/activate + ``` + +2. Install MeshMind and all optional extras required for full local coverage: + + ```bash + python -m pip install --upgrade pip + pip install uv + uv pip install --system -e .[dev,docs,testing] # omit --system when inside an active virtualenv + ``` + + The editable install pulls in the optional dependencies used by the REST service + (`fastapi`, `uvicorn`), the graph drivers (`neo4j`, `pymgclient`, `redis`), LLM tooling + (`openai`, `tiktoken`, `sentence-transformers`), and developer utilities (ruff, + pyright, typeguard, docs tooling, pytest plugins). + +3. Copy the sample environment file and adjust values as needed: + + ```bash + cp .env.example .env + ``` + +## 3. Provision external services + +The repository ships with a root `docker-compose.yml` that starts Redis, Memgraph, and +Neo4j with sane defaults. Run the stack in the background: + +```bash +docker compose up -d +``` + +> The default Neo4j credentials are `neo4j` / `meshminD123`. Memgraph exposes Bolt on +> `bolt://localhost:7687`, the Memgraph Lab UI on `http://localhost:3000`, and an +> additional monitoring endpoint on `http://localhost:7444`. + +### 3.1 Alternate topologies for tests + +For integration scenarios or CI jobs, use the compose files in +`meshmind/tests/docker/`: + +| File | Purpose | +| --- | --- | +| `memgraph.yml` | Runs only Memgraph with local port mapping. | +| `neo4j.yml` | Runs only Neo4j with APOC enabled. | +| `redis.yml` | Runs only Redis with persistence. | +| `full-stack.yml` | Spins up all services plus an optional Celery worker. | + +Example (Memgraph only): + +```bash +docker compose -f meshmind/tests/docker/memgraph.yml up -d +``` + +### 3.2 Cleaning up + +```bash +docker compose down -v +``` + +## 4. Configure environment variables + +Update `.env` (or export variables in your shell) with the credentials for each +service: + +| Variable | Description | Default | +| --- | --- | --- | +| `MEMGRAPH_URI` | Bolt URI for Memgraph. | `bolt://localhost:7687` | +| `NEO4J_URI` | Bolt URI for Neo4j. | `bolt://localhost:7688` | +| `NEO4J_USERNAME` / `NEO4J_PASSWORD` | Neo4j auth values. | `neo4j` / `meshminD123` | +| `REDIS_URL` | Redis connection string. | `redis://localhost:6379/0` | +| `LLM_API_KEY` / `OPENAI_API_KEY` | API key for OpenAI-compatible providers (OpenAI, OpenRouter, Azure, Google). | _none_ | +| `LLM_DEFAULT_MODEL` | Default Responses model for extraction/rerank. | `gpt-5-nano` | +| `LLM_DEFAULT_BASE_URL` | Default base URL for OpenAI-compatible SDKs. | `https://api.openai.com/v1` | +| `LLM_EXTRACTION_MODEL` / `LLM_EXTRACTION_BASE_URL` | Overrides for extraction requests. | inherit defaults | +| `LLM_EMBEDDING_MODEL` / `LLM_EMBEDDING_BASE_URL` | Overrides for embedding requests. | `text-embedding-3-small` / empty | +| `LLM_RERANK_MODEL` / `LLM_RERANK_BASE_URL` | Overrides for reranking requests. | inherit defaults | +| `SENTENCE_TRANSFORMERS_MODEL` | HuggingFace model ID used for local embeddings. | `all-MiniLM-L6-v2` | +| `DEFAULT_ENCODER` | Preferred encoder alias (`openai` or `sentence-transformers`). | `sentence-transformers` | + +Optional variables for experimentation: + +- `SQLITE_PATH` – override the path for the SQLite prototype driver. +- `GRAPH_BACKEND` – choose between `memory`, `sqlite`, `memgraph`, and `neo4j`. +- `CELERY_BROKER_URL` – override the broker used by Celery (defaults to `REDIS_URL`). +- `LLM_*` variables – adjust endpoint/model overrides per operation when targeting non-default LLM providers. + +## 5. Smoke tests + +After installing dependencies and starting the services: + +```bash +make test +meshmind admin graph --backend memgraph +meshmind admin graph --backend neo4j +meshmind admin counts --backend sqlite +``` + +You should see passing pytest output and successful graph connectivity checks. Refer to +`docs/troubleshooting.md` if any of the services fail health checks. diff --git a/SOT.md b/SOT.md new file mode 100644 index 0000000..aae1829 --- /dev/null +++ b/SOT.md @@ -0,0 +1,155 @@ +# MeshMind Source of Truth + +This document summarizes the current architecture, supporting assets, and operational expectations for MeshMind. Update it +whenever workflows or modules change so new contributors can find accurate information in one place. + +## Repository Layout +``` +meshmind/ +├── api/ # MemoryManager CRUD adapter plus REST/gRPC service layers +├── cli/ # CLI entry point, ingest command, and admin utilities +├── client.py # High-level MeshMind façade and orchestration helpers +├── core/ # Configuration, embeddings, types, similarity, shared utilities +├── db/ # Graph driver abstractions plus in-memory, SQLite, Memgraph, and Neo4j implementations +├── models/ # Entity and predicate registries shared across the pipeline +├── pipeline/ # Extract, preprocess, compression, storage, consolidation, expiry stages with telemetry hooks +├── retrieval/ # Search strategies (hybrid, lexical, fuzzy, vector, regex, rerank helpers) +├── tasks/ # Celery beat schedules and maintenance jobs +├── testing/ # Fake drivers (Memgraph, Redis, embedding, LLM client) for offline tests +├── _compat/ # Compatibility shims (e.g., fallback Pydantic base classes) +├── tests/ # Pytest suites with local fixtures (no external services required) +└── examples/ # Scripts and notebooks showing ingestion and retrieval flows +``` +Supporting assets: +- `Makefile`: Development automation (linting, formatting, type checks, docs guard, docker compose). +- `docker-compose.yml`: Provisions Memgraph, Neo4j, and Redis for local orchestration; targeted stacks for tests live in + `meshmind/tests/docker/` (Memgraph-only, Neo4j-only, Redis-only, and full integration). +- `SETUP.md`: End-to-end provisioning instructions covering Python deps, environment variables, and Compose workflows. +- `run/install_setup.sh`, `run/maintenance_setup.sh`: Automation scripts for provisioning fresh environments and refreshing cached workspaces. +- `.github/workflows/ci.yml`: GitHub Actions workflow running linting/formatting checks and pytest. +- `pyproject.toml`: Project metadata and dependency list (pins Python `>=3.11,<3.13`; see compatibility notes in `ISSUES.md`). +- Documentation (`PROJECT.md`, `PLAN.md`, `SOT.md`, `README.md`, etc.) describing the system and roadmap. +- `DUMMIES.md`: Catalog of temporary shims (Pydantic fallback, REST/gRPC stubs, Celery dummies, fake drivers) with removal + guidance now that dependencies can be installed. + +## Configuration (`meshmind/core/config.py`) +- Loads environment variables for the active graph backend (`GRAPH_BACKEND`), Memgraph (`MEMGRAPH_URI`, `MEMGRAPH_USERNAME`, + `MEMGRAPH_PASSWORD`), SQLite (`SQLITE_PATH`), optional Neo4j (`NEO4J_URI`, `NEO4J_USERNAME`, `NEO4J_PASSWORD`), Redis + (`REDIS_URL`), and provider-agnostic LLM settings (`LLM_API_KEY`, `LLM_*_MODEL`, `LLM_*_BASE_URL`, legacy `OPENAI_API_KEY`). +- Uses `python-dotenv` when available to hydrate values from a `.env` file automatically. +- Provides a module-level `settings` instance used across the client, drivers, CLI, and Celery tasks. + +## Core Data Models (`meshmind/core/types.py`) +- `Memory`: Pydantic model (or compatibility fallback) that represents a knowledge record, including embeddings, metadata, and optional TTL. Timestamp fields now default to timezone-aware UTC values to avoid naive datetime bugs. +- `Triplet`: Subject–predicate–object edge connecting two memory UUIDs with namespace and metadata. +- `SearchConfig`: Retrieval configuration (encoder name, `top_k`, `rerank_k`, optional rerank model, metadata filters, + hybrid weights). + +## Client (`meshmind/client.py`) +- `MeshMind` bootstraps: + - Provider-agnostic LLM client constructed from `LLMConfig` (defaults derived from `LLM_*` environment variables) with support + for injectable custom clients during testing. + - Embedding model from configuration with encoder bootstrap that registers available adapters. + - Graph driver factory that creates the configured backend (memory, SQLite, Memgraph, Neo4j) lazily when persistence is required. +- Provides convenience helpers: + - Pipelines: `extract_memories`, `deduplicate`, `score_importance`, `compress`, `store_memories`, `store_triplets`. + - CRUD: `create_memory`, `update_memory`, `delete_memory`, `get_memory`, + `list_memories(namespace=None, entity_labels=None, offset=0, limit=None, query=None, use_search=None)`, + `memory_counts`, `list_triplets`. + - Retrieval: `search` (hybrid + optional LLM rerank), `search_vector`, `search_regex`, `search_exact` with automatic graph-backed loading when `memories` is omitted. +- Exposes `graph_driver`/`driver` properties that surface the active graph driver instance on demand. + +## LLM Client (`meshmind/llm_client.py`) +- `LLMConfig` normalizes environment variables and CLI overrides into per-operation settings for extraction, embeddings, and rerank workflows. +- `LLMClient` caches OpenAI-compatible client instances per base URL and exposes `responses.create` / `embeddings.create` + proxies that inject configured models/endpoints automatically. +- `build_llm_config_from_settings` and `build_default_llm_client` helpers construct instances for use across pipelines, + encoders, and the high-level client. + +## Embeddings & Utilities (`meshmind/core/embeddings.py`, `meshmind/core/utils.py`) +- `EncoderRegistry` manages encoder instances (LLM-backed embeddings, sentence-transformers, custom fixtures). +- The LLM-backed embedding adapter delegates to the shared `LLMClient`, retrying on rate limits and respecting environment/CLI overrides. +- Utility functions provide UUIDs, timestamps, hashing, and token counting guarded behind optional `tiktoken` imports. + +## Database Layer (`meshmind/db`) +- `GraphDriver` defines the persistence contract (entity/relationship upserts, querying, deletions, triplet listing) and now standardises pagination (`offset`, `limit`), server-side search (`search_entities`), and aggregated counts (`count_entities`). +- `InMemoryGraphDriver` and `SQLiteGraphDriver` power local development/testing without external services while supporting the extended contract. +- `MemgraphDriver` wraps the `mgclient` module shipped with the `pymgclient` package, handles URI parsing, executes Cypher statements, and exposes Cypher-based filtering, + pagination, and aggregation helpers alongside the Python-side vector search fallback when database-native similarity is + unavailable. +- `Neo4jGraphDriver` mirrors the Memgraph contract using the official driver (optional dependency) and now exposes server-side search and counts. +- `factory.py` exposes helpers (`create_graph_driver`, `graph_driver_factory`) to instantiate backends based on configuration. + +## Pipeline Modules (`meshmind/pipeline`) +1. **Extraction (`extract.py`)** – Orchestrates LLM Responses calls against the `Memory` schema, enforces entity label filters, and + populates embeddings via registered encoders using the configured `LLMClient`. +2. **Preprocess (`preprocess.py`)** – Deduplicates by name/embedding similarity, ensures memories have importance scores while + recording telemetry statistics, and delegates to compression when available. +3. **Compress (`compress.py`)** – Truncates metadata payloads to configurable token budgets when `tiktoken` is installed and records telemetry counters/durations. +4. **Store (`store.py`)** – Persists memories and triplets using the configured `GraphDriver`, registering predicates as needed and emitting observability events. +5. **Consolidate & Expire (`consolidate.py`, `expire.py`)** – Maintenance utilities triggered by Celery tasks to group memories, + apply batch/backoff settings, surface skipped groups, and remove stale entries. + +## Service Layers (`meshmind/api`) +- `memory_manager.py`: CRUD façade over the active graph driver that forwards namespace/entity-label filters, pagination hints, search strings, and exposes aggregate counts alongside triplet listings. +- `service.py`: Pydantic payloads and orchestration helpers shared by REST/gRPC surfaces. `MemoryService.search` leans on driver-side filtering before ranking, handles per-request LLM overrides (`use_llm_rerank`, `llm_models`, `llm_base_urls`, `llm_api_key`, `rerank_model`), and exposes `memory_counts` for CLI/HTTP usage. +- `rest.py`: `create_app` returns a FastAPI application when available or a `RestAPIStub` for tests. Routes support pagination parameters and include `/memories/counts` for namespace/label summaries. +- `grpc.py`: `GrpcServiceStub` plus simple request/response dataclasses mirroring planned RPCs. + +## Retrieval (`meshmind/retrieval`) +- `filters.py`: Namespace, entity label, and metadata filtering helpers. +- `bm25.py`, `fuzzy.py`: Lexical and fuzzy scorers using scikit-learn TF-IDF + cosine and RapidFuzz WRatio, with pure-Python fallbacks when optional dependencies are unavailable. +- `vector.py`: Vector-only search utilities with cosine similarity and optional precomputed query embeddings. +- `hybrid.py`: Combines vector and BM25 scores with configurable weights defined in `SearchConfig`. +- `search.py`: Dispatchers for hybrid, BM25, fuzzy, vector, regex, and exact-match search modes plus metadata filters. +- `rerank.py`: Generic reranker interface and helper routed through the shared `LLMClient` for OpenAI-compatible providers. +- `graph.py`: Wrappers that pull candidates from the active graph driver before delegating to the existing search strategies. + +## CLI (`meshmind/cli`) +- `meshmind.cli.__main__`: Entry point exposing `ingest` and `admin` subcommands for local pipelines and maintenance tooling. +- CLI bootstraps encoder and entity registries, validates configuration early, surfaces actionable errors when optional + dependencies are missing, and routes predicate maintenance, telemetry inspection, and graph connectivity checks through + `meshmind.cli.admin`. + +## Tasks (`meshmind/tasks`) +- `celery_app.py`: Creates the Celery application lazily, returning a shim when Celery is not installed. +- `scheduled.py`: Defines periodic consolidation, compression, and expiry jobs that initialize drivers and managers lazily, + emit observability events, persist updated memories, surface skipped consolidation groups, and tolerate missing dependencies + during import. + +## API Adapter (`meshmind/api/memory_manager.py`) +- Manages CRUD operations against the graph driver, including triplet persistence and deletion helpers. +- Returns Pydantic models for list/get operations and gracefully handles missing records. + +## Models (`meshmind/models/registry.py`) +- `EntityRegistry` and `PredicateRegistry` store class metadata and permitted predicates. +- Registries are populated during bootstrap and extended as new entity/predicate types are defined. + +## Examples & Tests +- `examples/extract_preprocess_store_example.py`: Demonstrates extraction, preprocessing, triplet creation, and multiple + retrieval strategies. +- `meshmind/tests`: Pytest suites rely on fixtures (`memory_factory`, `dummy_encoder`, in-memory drivers, service stubs) and + pure-Python doubles (compatibility BaseModel, fake Memgraph/Redis/embedding/LLM clients), allowing the suite to run without Memgraph, OpenAI, or Redis dependencies. `test_setup_scripts.py` exercises the provisioning scripts in validation mode. + +- Required: `openai`, `pydantic`, `pydantic-settings`, `python-dotenv`. Install `pymgclient` (for the runtime `mgclient` module) when using Memgraph and install the `neo4j` + driver when targeting Neo4j. Pure-Python fallbacks exist for Pydantic, numpy, scikit-learn, and rapidfuzz but production deployments + should install the real packages. +- Optional but supported: `tiktoken`, `sentence-transformers`, `celery[redis]`, `fastapi`, `uvicorn[standard]`, `redis`, `httpx`, + `pytest-cov`. +- Development tooling introduced in the Makefile/CI expects `ruff`, `pyright`, `typeguard`, `toml-sort`, `yamllint`, `mkdocs`, and + `mkdocs-material` (now bundled in the extras). + +## Operational Notes +- Graph persistence requires a configured backend: in-memory/SQLite need no services; Memgraph requires a running instance + reachable via `settings.MEMGRAPH_URI` and the `mgclient` module provided by `pymgclient`; Neo4j requires the official driver and credentials. Use `meshmind admin graph --backend ` to sanity check connectivity or run the compose stacks. +- Encoder registration occurs during bootstrap; ensure at least one embedding encoder is available before extraction/search. +- LLM reranking flows through the shared `LLMClient`. Provide `LLM_API_KEY` (or fallback `OPENAI_API_KEY`) and confirm the + selected `SearchConfig.rerank_model` / `LLM_RERANK_MODEL` is deployed to your account or supported by the configured + `LLM_RERANK_BASE_URL`. REST/gRPC requests may override models/endpoints per call via `use_llm_rerank`, `llm_models`, + `llm_base_urls`, `llm_api_key`, and `rerank_model`. +- Provisioning scripts (`run/install_setup.sh`, `run/maintenance_setup.sh`) validate optional packages (`fastapi`, `neo4j`, + `pymgclient`, `uvicorn`) and respect `MESH_SKIP_SYSTEM_PACKAGES=1` / `MESH_SKIP_PYTHON_SYNC=1` when run in validation mode. +- Local development commands rely on external tooling (ruff, pyright, typeguard, toml-sort, yamllint); install them via the + Makefile or the CI workflow instructions in `README.md`. +- Docker Compose can be used to run Memgraph/Neo4j/Redis locally; ensure container tooling is available or provision services + manually. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..6ced283 --- /dev/null +++ b/TODO.md @@ -0,0 +1,74 @@ +# TODO + +## Completed + +- [x] Implement dependency guards and lazy imports for optional packages (`pymgclient`, `tiktoken`, `celery`, `sentence-transformers`). +- [x] Add bootstrap helper for default encoder registration and call it from the CLI. +- [x] Update OpenAI encoder implementation to align with latest SDK responses and retry semantics. +- [x] Improve configuration guidance and automation for environment variables and service setup. +- [x] Wire `EntityRegistry` and `PredicateRegistry` into the storage pipeline and client. +- [x] Implement CRUD and triplet methods on `MeshMind`, including relationship persistence in `GraphDriver`. +- [x] Refresh examples to cover relationship-aware ingestion and retrieval flows. +- [x] Extend retrieval module with vector-only, regex, exact-match, and optional LLM rerank search helpers. +- [x] Modernize pytest suites and add fixtures to run without external services. +- [x] Expand Makefile and add CI workflows for linting, testing, and type checks. +- [x] Document or provision local Memgraph and Redis services (e.g., via docker-compose) for onboarding. +- [x] Abstract `GraphDriver` to support alternative storage backends (Neo4j, in-memory, SQLite prototype). +- [x] Add service interfaces (REST/gRPC) for ingestion and retrieval. +- [x] Introduce observability (logging, metrics) for ingestion and maintenance pipelines. +- [x] Promote the new README, archive the legacy version, and keep SOT diagrams/maps in sync. +- [x] Harden Celery maintenance tasks to initialize drivers lazily and persist consolidation results. +- [x] Replace constant importance scoring with a heuristic driven by token diversity, recency, metadata richness, and embedding magnitude. +- [x] Create fake Memgraph, Redis, and embedding drivers for testing purposes. +- [x] Expand `GraphDriver.list_entities` to support namespace/entity-label filters and propagate the behaviour through `MemoryManager`, graph retrieval wrappers, and the MeshMind client. +- [x] Extend REST/gRPC payloads, CLI helpers, and pytest coverage to exercise the new entity-label filtering semantics. +- [x] Stand up `docs/` wiki pages, `ENVIRONMENT_NEEDS.md`, and `RESUME_NOTES.md` so documentation and session hand-off stay current. +- [x] Add unit tests covering namespace/entity-label filtering for the SQLite driver and fake drivers. +- [x] Update the example pipeline (`examples/extract_preprocess_store_example.py`) to demonstrate entity-label restricted retrieval via the MeshMind client. +- [x] Document REST/gRPC request samples that include `entity_labels` in `docs/api.md`. +- [x] Add a regression test confirming `MeshMind.list_memories` forwards `entity_labels` to the memory manager. +- [x] Push graph-backed retrieval queries deeper into Memgraph/Neo4j backends so search executes without materializing entire namespaces. +- [x] Implement pagination/streaming options in `MemoryManager.list_memories` to avoid loading entire namespaces into memory. +- [x] Add CLI/admin command to report memory counts grouped by namespace and entity label for quick health checks. +- [x] Create developer tooling (pre-commit or CI check) that ensures `docs/` pages are touched when code under corresponding modules changes. +- [x] Draft a troubleshooting section documenting optional tooling installation failures (ruff, pyright, typeguard, toml-sort, yamllint) and recommended fallbacks. +- [x] Expose `memory_counts` via the gRPC stub to keep service interfaces aligned. + +- [x] Extend the docs guard mapping/tests so Docker, setup, and environment guides are enforced when related modules change. +- [x] Draft `CLEANUP.md` outlining post-restriction cleanups for files that were temporarily modified to satisfy sandbox limitations. +- [x] Audit the repository for direct `import openai` usage to scope the `llm_client` refactor. +- [x] Implement a provider-agnostic `meshmind/llm_client.py` wrapper that routes requests via configurable endpoint URLs. +- [x] Replace all direct OpenAI client interactions in the codebase with the new `llm_client` abstraction. +- [x] Update unit tests and documentation to reflect the `llm_client` usage pattern. +- [x] Extend configuration models to support per-operation LLM endpoint and model overrides with a default of `gpt-5-nano`. +- [x] Add CLI flags that override LLM endpoint/model settings when provided. +- [x] Document the cascading LLM override behaviour across README and SETUP guides. +- [x] Expose LLM override fields via REST/gRPC payloads and verify they integrate with the `llm_client` abstraction. +- [x] Add API and service-level tests covering the new LLM override payloads once implemented. +- [x] Replace `datetime.utcnow()` usage in `meshmind/_compat/pydantic.py` with timezone-aware alternatives and update any tests relying on naive timestamps. +- [x] Add a smoke test or script check that `run/install_setup.sh` and `run/maintenance_setup.sh` install key optional packages (`neo4j`, `pymgclient`, `fastapi`) when internet access is present, documenting skip behaviour when offline. +- [x] Regenerate `uv.lock` to align with the updated dependency set (`fastapi`, `uvicorn`, `neo4j`, `pymgclient`, extras) once package downloads are possible (blocked: pip cannot access PyPI from this environment). +- [x] Document per-request LLM override payloads and CLI flags across `README.md`, `docs/api.md`, and `docs/configuration.md`. +- [x] Update `SETUP.md` and `docs/operations.md` to describe the provisioning scripts' validation step and skip environment variables. +- [x] Refresh `SOT.md`, `PLAN.md`, `PROJECT.md`, and `RECOMMENDATIONS.md` to capture the LLM override workflow and timezone-aware timestamp changes. +- [x] Extend `DUMMIES.md` and `docs/testing.md` with details about `FakeLLMClient` and the new setup script smoke test. +- [x] Update `ENVIRONMENT_NEEDS.md` and `NEEDED_FOR_TESTING.md` to reflect the availability of optional packages (`fastapi`, `neo4j`, `pymgclient`, `uvicorn`). +## Priority Tasks + +- [ ] Validate Neo4j driver requirements and connectivity against a live cluster (exercise CLI admin checks end-to-end). +- [ ] Validate consolidation heuristics on larger datasets to confirm accuracy and stability under load. +- [ ] Define and document conflict-resolution/backoff strategies for maintenance writes after heuristics are validated. +- [ ] Establish evaluation loops (analytics or LLM-assisted) to tune the new importance heuristic over time. +- [ ] Replace the compatibility shim with production Pydantic models once upstream packaging supports the target Python versions. +- [ ] Verify curl/grpcurl snippets against running REST/gRPC services once infrastructure is available. +- [ ] Create automated smoke tests for REST `/memories/counts` and `meshmind admin counts` against a live backend when infrastructure is ready. +- [ ] Add gRPC proto definitions and generated clients so the Python stubs align with production servers (including `MemoryCounts`). +- [ ] Benchmark driver-side pagination/filtering on large datasets to tune default candidate limits and document recommended overrides. +- [ ] Implement backend-native vector similarity queries for Memgraph/Neo4j to eliminate Python-side scoring when embeddings are present. + +## Recommended Waiting for Approval Tasks + +- [ ] Provision Neo4j, Memgraph, and Redis instances accessible from the development environment to unblock live integration tests (requires infrastructure approval). +- [ ] Approve installation of optional dependencies (`neo4j`, `pymgclient`, `redis`, `celery`, `tiktoken`, `sentence-transformers`) across CI and developer machines to exercise full workflows. +- [ ] Source or generate large synthetic datasets for consolidation and retrieval benchmarking to validate heuristics under load. +- [ ] Define a policy for reintroducing Pydantic models (version targets, rollout timeline) so compatibility shims can be retired once approved. diff --git a/docker-compose.yml b/docker-compose.yml index 96be0b4..cdf9fca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,73 @@ -version: "3.8" +version: "3.9" + services: memgraph: image: memgraph/memgraph-platform:latest + container_name: meshmind-memgraph + restart: unless-stopped ports: - "7687:7687" + - "7444:7444" - "3000:3000" + healthcheck: + test: ["CMD", "bash", "-c", "cypher-shell --version || exit 1"] + interval: 15s + timeout: 10s + retries: 5 + environment: + MEMGRAPH_MEMORY_LIMIT: 2GB + volumes: + - memgraph-data:/var/lib/memgraph + networks: + - meshmind + + neo4j: + image: neo4j:5.26 + container_name: meshmind-neo4j + restart: unless-stopped + ports: + - "7688:7687" + - "7474:7474" + environment: + NEO4J_AUTH: neo4j/meshminD123 + NEO4J_PLUGINS: '["apoc"]' + NEO4J_apoc_export_file_enabled: "true" + NEO4J_apoc_import_file_enabled: "true" + NEO4J_dbms_security_procedures_unrestricted: apoc.* + healthcheck: + test: ["CMD", "cypher-shell", "-u", "neo4j", "-p", "meshminD123", "RETURN 1"] + interval: 15s + timeout: 10s + retries: 5 + volumes: + - neo4j-data:/data + - neo4j-logs:/logs + networks: + - meshmind + redis: image: redis:7-alpine - worker: - build: . - command: celery -A meshmind.tasks.celery_app worker -B -l info - depends_on: - - memgraph - - redis \ No newline at end of file + container_name: meshmind-redis + restart: unless-stopped + command: redis-server --save 60 1000 --loglevel warning + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - redis-data:/data + networks: + - meshmind + +networks: + meshmind: + name: meshmind + +volumes: + memgraph-data: + neo4j-data: + neo4j-logs: + redis-data: diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..3ea41c1 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,86 @@ +# Service Interfaces & CLI + +MeshMind exposes multiple integration points for ingestion and retrieval workflows. + +## Memory Service (`meshmind.api.service`) + +- `MemoryService` encapsulates ingestion (`ingest_memories`, `ingest_triplets`), search, and CRUD operations. +- `SearchPayload` accepts `entity_labels`, allowing clients to restrict search to specific entity types before hybrid + ranking occurs. It also exposes `use_llm_rerank`, `llm_models`, `llm_base_urls`, `llm_api_key`, and `rerank_model` so API + callers can override the shared `LLMClient` on a per-request basis. When these dictionaries are omitted, the service falls + back to `LLM_*` environment variables and then the built-in defaults (`gpt-5-nano`, `text-embedding-3-small`). +- `MemoryPayload` and `TripletPayload` are Pydantic-compatible models shared by REST and gRPC stubs. + +## REST API (`meshmind.api.rest`) + +- `create_app` dynamically returns a FastAPI application if FastAPI is installed, otherwise a lightweight stub. +- Routes: + - `POST /memories`: ingest a batch of memories. + - `POST /triplets`: ingest relationships. + - `POST /search`: execute hybrid search. + - `GET /memories`: list memories (accepts `namespace`, `entity_labels`, `offset`, `limit`, `query`, `use_search`). + - `GET /triplets`: list triplets. + - `GET /memories/counts`: summarize memory counts grouped by namespace and entity label. +- Sample request bodies: + ```json + { + "query": "python", + "namespace": "demo", + "entity_labels": ["Memory"], + "top_k": 5, + "encoder": "text-embedding-3-small" + } + ``` + ```json + { + "query": "architecture", + "namespace": "demo", + "top_k": 5, + "use_llm_rerank": true, + "llm_models": {"rerank": "openrouter/reranker-v1"}, + "llm_base_urls": {"rerank": "https://openrouter.ai/api/v1"} + } + ``` + ```json + { + "namespace": "demo", + "entity_labels": ["Memory"], + "limit": 20, + "query": "python" + } + ``` + ```json + { + "namespace": "demo" + } + ``` +- The `RestAPIStub` mirrors these routes for tests without requiring FastAPI. + +## gRPC Stub (`meshmind.api.grpc`) + +- Provides dataclasses representing typical RPC messages. +- `GrpcServiceStub` implements the service interface in pure Python for unit tests and demos. +- `SearchRequest` mirrors the REST payload including `entity_labels` to ensure consistent filtering semantics. It also carries + `use_llm_rerank`, `llm_models`, `llm_base_urls`, `llm_api_key`, and `rerank_model` so RPC callers can override LLM behaviour + per request. Overrides remain scoped to the single RPC and never mutate the global client configuration. +- `MemoryCountsRequest` returns namespace/entity-label aggregates so service parity with REST/CLI is preserved. + +## CLI (`meshmind/cli`) + +- `meshmind.cli.__main__` bootstraps default encoders, validates configuration, and exposes ingestion commands. +- `meshmind.cli.admin` contains administrative tasks for registry inspection, backend connectivity checks, memory counts, + and maintenance triggers. + +## Client (`meshmind/client.py`) + +- `MeshMind` client wraps all pipelines and persistence methods. +- CRUD and search helpers delegate to the memory manager while respecting namespace/entity label filters. +- Search helpers automatically fetch candidates from the graph when `memories` is omitted, ensuring consistent + behaviour across service surfaces. + +## Example Workflow + +1. Instantiate the client: `mm = MeshMind()` (ensure environment variables are set for your backend). +2. Ingest data using CLI (`meshmind ingest ...`) or the REST API. +3. Retrieve memories via REST (`POST /search`) or the Python client (`mm.search(...)`). +4. Monitor metrics/logs through the observability utilities (`docs/telemetry.md`). diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..794d7c9 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,62 @@ +# Architecture + +MeshMind is organized around a layered architecture that separates extraction, persistence, and retrieval concerns while +keeping the graph storage pluggable. + +## High-Level Flow + +1. **Extraction** (`meshmind.pipeline.extract`) + - Uses instructions plus LLM client and embeddings to convert raw content into `Memory` objects and `Triplet` + relationships. +2. **Preprocessing** (`meshmind.pipeline.preprocess`) + - Deduplicates, scores importance, compresses content, and prepares payloads for persistence. +3. **Storage** (`meshmind.pipeline.store`) + - Persists memories and triplets through the active graph driver, registering entity/predicate schemas along the way. +4. **Retrieval** (`meshmind.retrieval`) + - Provides hybrid search, vector-only, regex, exact, fuzzy, BM25, and optional LLM rerank flows across stored + memories. +5. **Maintenance** (`meshmind.pipeline.consolidate`, `meshmind.tasks.scheduled`) + - Consolidates duplicates, expires stale items, and runs periodic graph hygiene tasks. + +## Key Components + +- **MeshMind Client (`meshmind/client.py`)** + - High-level façade that wires pipelines, storage, and retrieval helpers. Lazily loads a graph driver via the factory + configured by `meshmind.core.config.settings`. + - Provides CRUD utilities, search helpers, and bootstrap logic for registries and encoders. +- **Memory Service Layer (`meshmind/api/service.py`)** + - Encapsulates ingestion/search/triplet operations, enabling REST/gRPC adapters and CLI commands to share behaviour. + - Applies configuration (e.g., `SearchConfig`) and now filters `list_memories` calls by namespace and entity labels to + minimize driver loads. +- **Graph Drivers (`meshmind/db/*`)** + - `InMemoryGraphDriver`: simple dictionaries for tests and demos. + - `SQLiteGraphDriver`: lightweight relational persistence using JSON columns. + - `Neo4jGraphDriver` and `MemgraphDriver`: Bolt-based implementations using Cypher. + - All drivers implement the expanded `GraphDriver.list_entities(namespace, entity_labels)` contract for efficient + filtering at the storage layer. +- **Observability (`meshmind/core/observability.py`)** + - Tracks metrics, counters, and structured logs for pipeline operations. + +## Data Model + +Memories capture: + +- `uuid`: unique identifier. +- `namespace`: logical partition to isolate tenants/workloads. +- `entity_label`: semantic label for type filtering. +- `embedding`: vector representation for similarity search. +- `metadata`: arbitrary JSON payload with content, source, annotations. +- `importance`, `ttl_seconds`, `reference_time`: scheduling and scoring hints. + +Triplets connect memories with: + +- `subject`, `predicate`, `object`: relationship endpoints. +- `entity_label`: stored predicate label to support filtering. +- `metadata`: additional context for the relationship. + +## Extensibility + +- Register new entity or predicate schemas via `EntityRegistry` and `PredicateRegistry` before persistence. +- Provide alternate graph drivers by subclassing `GraphDriver` and implementing the CRUD/search primitives. +- Extend retrieval strategies by adding new modules under `meshmind/retrieval` and wiring them through the client and + service layers. diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..e1dbe71 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,48 @@ +# Configuration Reference + +MeshMind configuration is derived from environment variables and optional `.env` files (loaded when `python-dotenv` is +installed). + +## Core Settings + +| Variable | Description | Default | +| -------- | ----------- | ------- | +| `GRAPH_BACKEND` | Storage backend (`memory`, `sqlite`, `neo4j`, `memgraph`). | `memory` | +| `MEMGRAPH_URI` | Bolt URI for Memgraph. | `bolt://localhost:7687` | +| `MEMGRAPH_USERNAME` / `MEMGRAPH_PASSWORD` | Credentials for Memgraph. | empty | +| `NEO4J_URI` | Bolt URI for Neo4j. | `bolt://localhost:7688` | +| `NEO4J_USERNAME` / `NEO4J_PASSWORD` | Credentials for Neo4j. | `neo4j` / `meshminD123` | +| `SQLITE_PATH` | File path for SQLite backend. | `:memory:` | +| `REDIS_URL` | Redis connection string for caching/background tasks. | `redis://localhost:6379/0` | +| `OPENAI_API_KEY` | Legacy API key fallback used when `LLM_API_KEY` is unset. | empty | +| `LLM_API_KEY` | Preferred API key for any OpenAI-compatible provider. | inherits `OPENAI_API_KEY` | +| `LLM_DEFAULT_MODEL` | Fallback Responses model for extraction/reranking. | `gpt-5-nano` | +| `LLM_DEFAULT_BASE_URL` | Base URL for providers using the OpenAI SDK. | empty | +| `LLM_EXTRACTION_MODEL` / `LLM_EXTRACTION_BASE_URL` | Overrides for extraction runs. | inherit default model/base | +| `LLM_EMBEDDING_MODEL` / `LLM_EMBEDDING_BASE_URL` | Overrides for embedding requests. | `text-embedding-3-small` / empty | +| `LLM_RERANK_MODEL` / `LLM_RERANK_BASE_URL` | Overrides for reranking requests. | inherit default model/base | +| `EMBEDDING_MODEL` | Backwards-compatible alias for `LLM_EMBEDDING_MODEL`. | `text-embedding-3-small` | + +## Derived Behaviour + +- `Settings.missing()` reports missing variables based on the selected backend (e.g., Memgraph credentials ignored when + not using Memgraph). +- CLI commands (`meshmind.cli.__main__`) surface missing configuration early so ingestion fails fast. +- The MeshMind client reads settings during initialization to configure the default `LLMClient` and graph driver factory. +- CLI arguments (`--llm-api-key`, `--llm-base-url`, `--extraction-model`, etc.) override environment values for a single run, + and API payloads may pass explicit models/endpoints where supported. The precedence order is per-request override (CLI flag + or payload) > environment variables > built-in defaults described above. +- REST/gRPC search requests accept `use_llm_rerank`, `llm_models`, `llm_base_urls`, `llm_api_key`, and `rerank_model` so callers + can experiment with alternative providers without mutating global configuration. + +## Recommended Secrets Management + +- Store secrets in a `.env` file for local development (loaded automatically when `python-dotenv` is present). +- Use environment-specific secret managers (Vault, AWS Secrets Manager, etc.) for production deployments. +- Rotate API keys/credentials regularly and update the environment variables accordingly. + +## Extending Configuration + +- Add new settings to `meshmind/core/config.py` with sensible defaults. +- Update `Settings.REQUIRED_GROUPS` when additional capabilities require environment validation. +- Document the new variables here, in `README.md`, and in `ENVIRONMENT_NEEDS.md` if they require provisioning support. diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..194749c --- /dev/null +++ b/docs/development.md @@ -0,0 +1,44 @@ +# Development Workflow + +This guide summarizes expectations when contributing to MeshMind. + +## Prerequisites + +- Python 3.11 or 3.12 is recommended (see `pyproject.toml`). +- Install dependencies with `pip install -e .[dev,docs,testing]` (or `uv pip install --system -e .[dev,docs,testing]`; drop + `--system` when using an activated virtualenv). +- Optional extras: none—`.[dev,docs,testing]` pulls in REST tooling (`fastapi`, `uvicorn`), graph drivers (`neo4j`, `pymgclient`, + `redis`), LLM tooling (`openai`, `tiktoken`, `sentence-transformers`), and developer utilities. Refer to `SETUP.md` for + service provisioning steps. + +## Coding Standards + +- Follow the style rules documented in `AGENTS.md` (120-character lines, meaningful headings, bullet lists for + enumerations). +- Keep code modular and dependency-light to maintain deterministic tests. +- Avoid wrapping imports in `try/except` unless explicitly handling optional dependencies. + +## Documentation + +- Update `README.md`, `CHANGELOG.md`, `docs/`, `SOT.md`, and other root markdown files whenever behaviour changes. +- Each change batch must append a timestamped entry to `CHANGELOG.md` describing modules, functions, and rationale. +- Maintain `RESUME_NOTES.md` at the end of every turn to capture context for future sessions. +- Run `make docs-guard` (optionally with `BASE_REF=`) before pushing to ensure code changes have matching documentation updates. + +## Testing + +- Run `pytest` before committing. +- Add unit tests for new features, especially when touching drivers, pipelines, or retrieval logic. +- Use the fake drivers and fixtures to avoid requiring external services. + +## Git Workflow + +- Work on feature branches derived from the current branch (e.g., `work`). +- Commit logically grouped changes with descriptive messages. +- Generate PR summaries via the automated tooling once commits are ready. + +## Continuous Integration + +- `.github/workflows/ci.yml` runs linting and tests; ensure new code paths are covered. +- The Makefile includes shortcuts (`make test`, `make lint`, `make format`) for local validation. +- CI also executes `make docs-guard` so missing documentation updates will fail the pipeline. diff --git a/docs/operations.md b/docs/operations.md new file mode 100644 index 0000000..90dcef8 --- /dev/null +++ b/docs/operations.md @@ -0,0 +1,57 @@ +# Operations & Maintenance + +This guide covers operational tasks for MeshMind deployments. + +## Configuration + +- Environment variables are read via `meshmind.core.config.Settings`. +- Critical variables: + - `GRAPH_BACKEND`: `memory`, `sqlite`, `neo4j`, or `memgraph`. + - Connection URIs/usernames/passwords for Neo4j/Memgraph. + - `SQLITE_PATH` when using the SQLite backend. + - `REDIS_URL`, `LLM_API_KEY`/`OPENAI_API_KEY`, and the `LLM_*` model/base URL overrides for optional LLM integrations. +- `settings.missing()` groups missing variables by capability to simplify diagnostics. + +## Provisioning Scripts + +- `run/install_setup.sh` installs system packages, ensures `uv` is available, validates that optional dependencies + (`fastapi`, `neo4j`, `pymgclient`, `uvicorn`) are declared in `pyproject.toml`, and syncs Python requirements from + `uv.lock` when present. Use `MESH_SKIP_SYSTEM_PACKAGES=1` and/or `MESH_SKIP_PYTHON_SYNC=1` to perform a validation-only run + when network access is unavailable. +- `run/maintenance_setup.sh` refreshes cached environments with the same validation logic and respects the same skip flags so + CI can dry-run provisioning. + +## Bootstrap & Registries + +- `meshmind.core.bootstrap.bootstrap_entities` ensures the `Memory` model is registered. +- `meshmind.core.bootstrap.bootstrap_encoders` primes default encoders via the provider-agnostic `LLMClient` when available. +- Registry state (entities/predicates) persists in process memory; configure the CLI/admin tasks to inspect/refresh when + adding new schema types. + +## Scheduled Tasks + +- `meshmind.tasks.scheduled.expire_task`: prunes expired memories based on TTL metadata. +- `meshmind.tasks.scheduled.consolidate_task`: merges duplicates, persists consolidation results, and emits telemetry. +- `meshmind.tasks.scheduled.compress_task`: re-compresses long memories and persists updates to the configured backend. +- All tasks rely on `MemoryManager.list_memories` and respect namespace/label filters for efficiency. + +## Observability + +- `meshmind.core.observability.telemetry` exposes counters and gauges with `snapshot()` for inspection. +- The `docs/telemetry.md` page lists the available metrics and logging patterns. + +## Admin CLI + +- `meshmind.cli.admin` provides commands to: + - Validate backend connectivity (Neo4j/Memgraph/SQLite). + - Summarize stored memories via `mesh admin counts --namespace ` grouped by entity label. + - Run consolidation plans manually. + - Inspect registry contents. + - Summarize configuration with sensitive values masked. + +## Deployment Considerations + +- Provision graph databases externally (Docker, managed service) and expose Bolt endpoints reachable from the runtime. +- Ensure optional dependencies (`neo4j`, `pymgclient`, `redis`, `celery`, `fastapi`, `uvicorn`, `tiktoken`) are installed where required (or install `.[dev,docs,testing]`). +- Configure logging/metrics sinks to capture telemetry emitted by pipeline stages. +- Use `docker-compose.yml` as a reference for local orchestration (Memgraph, Neo4j, Redis) and the targeted stacks in `meshmind/tests/docker/` for integration testing. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..560311b --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,34 @@ +# MeshMind Overview + +MeshMind is a modular framework for extracting, storing, and retrieving "memories"—structured knowledge units that +combine embeddings, metadata, and graph relationships. The platform orchestrates LLM-powered extraction pipelines, +pluggable persistence backends, and multiple retrieval strategies so applications can ingest heterogeneous content and +query it with hybrid search techniques. + +## Core Concepts + +- **Memories** (`meshmind.core.types.Memory`): normalized documents with embeddings, importance scores, TTLs, and + metadata. +- **Triplets** (`meshmind.core.types.Triplet`): relationship edges connecting memories by subject/predicate/object. +- **Graph Drivers** (`meshmind.db`): persistence adapters for in-memory usage, SQLite prototyping, and Bolt-compatible + services (Neo4j, Memgraph). +- **Pipelines** (`meshmind.pipeline`): modular stages for extraction, preprocessing, compression, and storage. +- **Retrieval** (`meshmind.retrieval`): text, vector, graph, and rerank utilities that operate on in-memory or driver- + backed memories. +- **Service Interfaces** (`meshmind.api`): REST/gRPC-compatible surfaces and CLI tooling to automate ingestion and + maintenance workflows. + +## Project Layout + +- `meshmind/core`: fundamental types, configuration, bootstrap helpers, encoders, similarity metrics, observability, + and shared utilities. +- `meshmind/pipeline`: ingestion lifecycle components (`extract`, `preprocess`, `store`, `compress`, `consolidate`, + `expire`). +- `meshmind/db`: graph drivers plus the factory that selects the active backend based on environment configuration. +- `meshmind/retrieval`: hybrid search primitives, graph-aware wrappers, and optional LLM rerank integration. +- `meshmind/api`: Memory service layer, REST/gRPC adapters, CLI entry points, and admin tooling. +- `meshmind/tests`: unit and integration-style tests that exercise pipelines, drivers, API stubs, and search flows + using in-memory or fake dependencies. +- `examples/`: runnable demos showing end-to-end extraction, preprocessing, and storage pipelines. + +Consult the other `docs/*.md` pages for deep dives into each subsystem. diff --git a/docs/persistence.md b/docs/persistence.md new file mode 100644 index 0000000..e4a6fa4 --- /dev/null +++ b/docs/persistence.md @@ -0,0 +1,62 @@ +# Persistence Layer + +MeshMind persists memories and triplets through interchangeable graph drivers. Each driver implements the +`meshmind.db.base_driver.GraphDriver` interface, which now accepts both namespace and entity-label filters for +`list_entities`. + +## Driver Capabilities + +| Driver | Module | Use Case | Notes | +| ------ | ------ | -------- | ----- | +| In-memory | `meshmind.db.in_memory_driver.InMemoryGraphDriver` | Tests and demos | Stores nodes/edges in Python dicts with UUID auto-generation helpers, plus in-process search/pagination implementations. | +| SQLite | `meshmind.db.sqlite_driver.SQLiteGraphDriver` | Lightweight persistence without external services | Uses two tables (`entities`, `triplets`) with JSON columns; supports namespace + label filtering, pagination, and SQL-based search. | +| Neo4j | `meshmind.db.neo4j_driver.Neo4jGraphDriver` | Production Bolt cluster | Requires the `neo4j` Python driver and connectivity to a running instance. Provides `verify_connectivity()` for health checks, server-side search, and aggregated counts. | +| Memgraph | `meshmind.db.memgraph_driver.MemgraphDriver` | Memgraph (Bolt-compatible) | Requires the `pymgclient` package (provides the `mgclient` module). Filters results using Cypher with optional namespace/label predicates, driver-side search, and count aggregation. | +| Fake drivers | `meshmind.testing.fakes` | Offline testing | Provide stubbed implementations for Redis, embedding models, and Memgraph. | + +## Driver Factory + +`meshmind.db.factory.graph_driver_factory` inspects `meshmind.core.config.settings.GRAPH_BACKEND` and constructs the +corresponding driver. Supported values: + +- `memory` +- `sqlite` +- `neo4j` +- `memgraph` + +Environment variables (see `README.md` and `ENVIRONMENT_NEEDS.md`) provide connection URIs and credentials. + +## MemoryManager + +`meshmind.api.memory_manager.MemoryManager` wraps driver operations and now exposes: + +```python +list_memories( + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + *, + offset: int = 0, + limit: int | None = None, + query: str | None = None, + use_search: bool | None = None, +) -> List[Memory] + +count_memories(namespace: str | None = None) -> Dict[str, Dict[str, int]] +``` + +Filtering and pagination at this layer keep retrieval queries efficient when large graphs are present. The manager defers +to driver-provided `search_entities` and `count_entities` helpers when available, then handles `add_memory`, +`update_memory`, `delete_memory`, and triplet equivalents by normalizing payloads and delegating to the driver. + +## Maintenance & Consolidation + +Scheduled tasks in `meshmind.tasks.scheduled` use the memory manager to fetch entities before running consolidation or +expiration strategies. Because `list_memories` accepts entity labels, maintenance jobs can focus on specific entity +classes without scanning the full graph. + +## Adding a New Driver + +1. Subclass `GraphDriver` and implement the abstract methods, including the namespace/label-aware `list_entities`. +2. Register the driver in `meshmind/db/factory.py` under a new backend key. +3. Provide fake/test doubles if the backend cannot run inside CI. +4. Update documentation (`docs/persistence.md`, `SOT.md`, `README.md`) and add tests validating CRUD behaviours. diff --git a/docs/pipelines.md b/docs/pipelines.md new file mode 100644 index 0000000..37a90ac --- /dev/null +++ b/docs/pipelines.md @@ -0,0 +1,47 @@ +# Pipelines + +MeshMind pipelines orchestrate the transformation of raw content into stored memories and relationships. + +## Extract (`meshmind.pipeline.extract`) + +- `extract_memories` uses the configured LLM client to parse unstructured content into structured `Memory` objects. +- Supports configurable entity types, instructions, and embedding models. +- Defaults are sourced from `LLM_*` environment variables; pass a custom `LLMConfig` or CLI overrides (`meshmind ingest`) to + target different providers or models for extraction and embeddings. +- Output includes candidate triplets emitted by extraction heuristics. + +## Preprocess (`meshmind.pipeline.preprocess`) + +- `deduplicate`: removes near-duplicate memories based on embedding cosine similarity and metadata hashes. +- `score_importance`: applies heuristic scoring that considers recency, metadata richness, token diversity, and embedding + magnitude. +- `compress`: performs optional content compression if tiktoken is installed; otherwise acts as a no-op while recording + telemetry. + +## Store (`meshmind.pipeline.store`) + +- `store_memories`: upserts each `Memory` through the graph driver after ensuring the entity type is registered. +- `store_triplets`: persists relationships while registering predicates via `PredicateRegistry`. + +## Consolidate (`meshmind.pipeline.consolidate`) + +- `consolidate_memories`: groups similar memories, merges metadata, and produces a plan enumerating consolidation + outcomes for later application. + +## Maintenance (`meshmind.tasks.scheduled`) + +- Provides task stubs for Celery/cron that run consolidation plans, expire TTL-bound memories, and emit importance + telemetry snapshots. + +## Expiration (`meshmind.pipeline.expire`) + +- Contains helpers to remove expired memories (leveraged by maintenance tasks and CLI commands). + +## Usage Patterns + +1. Use the MeshMind client (`MeshMind.extract_memories`) or pipeline functions directly to generate memories, supplying a custom + `LLMConfig` when provider-specific overrides are required. +2. Preprocess the output with `deduplicate`, `score_importance`, and `compress` depending on your quality requirements. +3. Persist via `store_memories` / `store_triplets` using your configured graph backend. +4. Retrieve with hybrid or specialized searches (see `docs/retrieval.md`). +5. Schedule maintenance tasks to keep the graph clean and heuristics calibrated. diff --git a/docs/retrieval.md b/docs/retrieval.md new file mode 100644 index 0000000..ecfed61 --- /dev/null +++ b/docs/retrieval.md @@ -0,0 +1,59 @@ +# Retrieval Strategies + +MeshMind ships with multiple retrieval strategies that operate on `Memory` collections. Each strategy can run purely in +memory (provided via argument) or load candidates directly from the configured graph driver. + +## Hybrid Search + +`meshmind.retrieval.search.search` + +- Combines BM25 (text), vector similarity, regex, exact-match, and fuzzy signals. +- Accepts `SearchConfig` to tune weights, top-k counts, embedding encoder, and rerank parameters. +- Optional rerank step calls `meshmind.retrieval.llm_rerank.llm_rerank` using the active LLM client. Per-operation models and + endpoints cascade from `LLM_*` environment variables but can be overridden via `SearchConfig` or CLI flags. + +Graph wrapper: `graph_hybrid_search(query, driver, namespace=None, entity_labels=None, config=None, reranker=None)` +leans on `MemoryManager.list_memories(..., query=query, use_search=True)` so Memgraph/Neo4j filter and paginate on the +server. The helper automatically expands the candidate window (`top_k * 5`, `rerank_k * 2`) to balance recall with +efficiency. + +## Vector Search + +`meshmind.retrieval.vector.search_vector` + +- Uses cosine similarity between query embedding and stored embeddings. +- Graph wrapper `graph_vector_search` now requests driver-side filtering (`query=`) and pagination, reducing + the number of memories materialised before vector scoring. + +## Textual Search + +- **Regex** (`search_regex` / `graph_regex_search`): applies compiled regular expressions against string fields. +- **Exact** (`search_exact` / `graph_exact_search`): equality comparisons with optional case sensitivity, field + selection, and top-k truncation. +- **Fuzzy** (`search_fuzzy` / `graph_fuzzy_search`): Levenshtein distance using `rapidfuzz` if installed. +- **BM25** (`search_bm25` / `graph_bm25_search`): rank text fields using BM25 weighting. + +## Entity Label Filtering & Pagination + +All graph-backed helpers accept `entity_labels`. The labels and pagination hints (`offset`, `limit`) are forwarded to +`MemoryManager` and ultimately to the graph driver so only relevant entity types are hydrated into Python. This prevents +unnecessary deserialization when a graph contains many heterogeneous memory types and unlocks efficient infinite-scroll or +batch processing patterns. + +## Search Configuration + +`SearchConfig` (`meshmind.core.types.SearchConfig`) controls: + +- `top_k`: maximum results to return. +- `encoder`: embedding model name (default comes from settings and the MeshMind client). +- `rerank_k`: number of documents to rerank with LLM. +- `rerank_model` / `rerank_endpoint`: explicit overrides that take precedence over environment defaults when reranking. +- `fields`: optional mapping for textual searches (regex, exact, fuzzy) to target metadata keys. + +## Extending Retrieval + +1. Add a new module under `meshmind/retrieval` with a function that accepts `(query, memories, **kwargs)`. +2. Update `meshmind/retrieval/__init__.py` and the MeshMind client to expose the helper. +3. Create graph wrappers if the strategy benefits from driver-backed loading. +4. Add unit tests in `meshmind/tests/test_retrieval.py` or a dedicated module to ensure determinism. +5. Document the feature here and in `README.md`. diff --git a/docs/telemetry.md b/docs/telemetry.md new file mode 100644 index 0000000..e751a40 --- /dev/null +++ b/docs/telemetry.md @@ -0,0 +1,31 @@ +# Telemetry & Observability + +MeshMind provides lightweight observability utilities under `meshmind.core.observability` to trace pipeline behaviour. + +## Telemetry API + +- `telemetry.reset()`: clear counters/gauges (used in tests). +- `telemetry.increment(name, value=1.0, tags=None)`: bump a counter. +- `telemetry.gauge(name, value, tags=None)`: record the latest gauge value. +- `telemetry.timer(name, tags=None)`: context manager to measure elapsed time. +- `telemetry.snapshot()`: return a dictionary of counters/gauges/timers for inspection. + +## Instrumented Components + +- `meshmind.pipeline.preprocess.score_importance`: records importance distribution metrics. +- `meshmind.pipeline.compress.compress`: wraps compression attempts to measure latency and failure rates. +- `meshmind.pipeline.store` and `meshmind.tasks.scheduled`: emit counters for stored items and maintenance plans. +- Observability hooks are intentionally dependency-free to keep tests deterministic. + +## Integrations + +- For production use, wrap the telemetry API with adapters that forward metrics to Prometheus, StatsD, or logging + systems. +- Configure logging handlers (see `logging` usage throughout the pipeline modules) to integrate with your preferred log + aggregation stack. + +## Best Practices + +- Tag metrics with `namespace` when running multi-tenant workloads. +- Reset telemetry within tests to avoid cross-test contamination. +- Extend the telemetry module with thread-safe transports if moving beyond single-process deployments. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..8fc3369 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,47 @@ +# Testing & Quality + +MeshMind includes an extensive pytest suite that runs without external services by relying on fake drivers and in-memory +backends. + +## Test Topology + +- `meshmind/tests/test_pipeline_*`: validate extraction, preprocessing, storage, and maintenance flows with dummy + drivers. +- `meshmind/tests/test_db_*`: ensure graph drivers implement CRUD semantics (with fakes for optional dependencies). +- `meshmind/tests/test_retrieval.py` and `test_graph_retrieval.py`: exercise hybrid, textual, vector, and graph-based + searches. +- `meshmind/tests/test_service_interfaces.py`: cover REST and gRPC stubs, including entity-label filtering, pagination hints, and memory count routes with LLM override payloads. +- `meshmind/tests/test_setup_scripts.py`: run the provisioning scripts in validation mode to ensure optional dependencies are declared in `pyproject.toml`, that `uv` is bootstrapped, and that skip flags behave as expected. +- `meshmind/tests/test_cli_admin.py`: verify administrative CLI commands use the correct driver factory, settings, and counts reporting. +- `meshmind/tests/test_docs_guard.py`: ensure the documentation guard script enforces wiki updates when code modules change. +- `meshmind/tests/test_observability.py`: confirm telemetry metrics/counters update during preprocessing steps. + +## Fakes & Fixtures + +- `meshmind.testing.fakes`: supplies fake Memgraph/Redis/embedding drivers and a provider-agnostic `FakeLLMClient` that records + override dictionaries (`llm_models`, `llm_base_urls`, `llm_api_key`) so service tests can assert cascading behaviour without + real SDK calls. +- `meshmind/tests/conftest.py`: defines reusable fixtures such as `dummy_encoder`, `memory_service`, and telemetry reset + helpers. +- `DUMMIES.md`: outlines every compatibility shim and stub so test suites can migrate toward real dependencies over time. + +## Running Tests + +```bash +pip install -e .[dev,docs,testing] +pytest +``` + +Optional extras: + +- `PYTHONPATH=.` ensures imports resolve when running tests manually. +- To test the Neo4j/Memgraph drivers, set `GRAPH_BACKEND` appropriately, point `MEMGRAPH_URI` / `NEO4J_URI` at the Docker + services, and export credentials as described in `SETUP.md` and `ENVIRONMENT_NEEDS.md`. + +## Adding Tests + +1. Prefer deterministic unit tests using the in-memory driver or fakes. +2. When touching new modules, add coverage in the closest existing test file or create a dedicated module under + `meshmind/tests`. +3. Mock optional dependencies (`LLMClient` providers, Redis, Celery) unless integration coverage is explicitly required. +4. Update documentation (`docs/testing.md`, `README.md`) whenever the testing workflow changes. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..fe4e5a9 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,32 @@ +# Troubleshooting + +## Optional Tooling Installation Failures + +- **Ruff, Pyright, Typeguard** + - Install via `uv pip install ruff pyright typeguard` to match CI tooling. + - If Pyright is unavailable, run `make lint` to exercise Ruff only and document the gap in `ENVIRONMENT_NEEDS.md`. +- **toml-sort / yamllint** + - Provided through `uv pip install toml-sort yamllint`. + - When installing globally, ensure the binaries are on the `PATH` or invoke via `python -m toml_sort` and `python -m yamllint`. + +## Graph Backends + +- **Memgraph / Neo4j connectivity** + - Verify credentials are exported (`MEMGRAPH_URI`, `NEO4J_URI`, etc.) before running `mesh admin graph --backend neo4j`. + - When the native drivers are unavailable, fall back to the in-memory or SQLite backends and note the blocker in `ENVIRONMENT_NEEDS.md`. + +## External Services + +- **Redis** + - Use `docker-compose up redis` to start the local stack, or swap in the `FakeRedisBroker` for unit tests. +- **Embedding providers** + - When `LLM_API_KEY`/`OPENAI_API_KEY` are missing, configure `FAKE_EMBEDDINGS=1` to rely on the deterministic fake encoder + or point `LLM_EMBEDDING_MODEL`/`LLM_EMBEDDING_BASE_URL` at a provider accessible in your environment. + +## Common Runtime Symptoms + +- **Search returns empty results** + - Confirm that the namespace and `entity_labels` filters align with persisted memories. + - For graph-backed searches, run `mesh admin counts` to validate stored entity counts. +- **Pagination behaves unexpectedly** + - Inspect driver-specific logs (enable via `MESH_LOG_LEVEL=DEBUG`) to confirm the offset and limit values passed downstream. diff --git a/examples/extract_preprocess_store_example.py b/examples/extract_preprocess_store_example.py index 74ac65d..ba803d1 100644 --- a/examples/extract_preprocess_store_example.py +++ b/examples/extract_preprocess_store_example.py @@ -1,40 +1,73 @@ -""" -Example flow: extract → preprocess → store using Meshmind pipeline. -Requires a running Memgraph instance and a valid OPENAI_API_KEY. -""" -from meshmind.core.types import Memory +"""End-to-end MeshMind example covering extraction, storage, and retrieval.""" +from __future__ import annotations + from meshmind.client import MeshMind +from meshmind.core.types import Memory, Triplet + -def main(): - # Initialize MeshMind client (uses OpenAI and default MemgraphDriver) +def main() -> None: mm = MeshMind() - driver = mm.driver - # Sample content for extraction texts = [ "The Eiffel Tower is located in Paris and was built in 1889.", - "Python is a programming language created by Guido van Rossum." + "Python is a programming language created by Guido van Rossum.", ] - # Extract memories via LLM memories = mm.extract_memories( instructions="Extract key facts as Memory objects.", namespace="demo", - entity_types=[Memory], + entity_types=[Memory], content=texts, ) - print(f"Extracted {len(memories)} memories:") - for m in memories: - print(m.json()) - - # Preprocess: deduplicate, score importance, compress - memories = mm.deduplicate(memories, threshold=0.9) + memories = mm.deduplicate(memories) memories = mm.score_importance(memories) memories = mm.compress(memories) - - # Store into graph mm.store_memories(memories) - print("Memories stored to graph.") + print(f"Stored {len(memories)} memories.") + + if len(memories) >= 2: + relation = Triplet( + subject=str(memories[0].uuid), + predicate="RELATED_TO", + object=str(memories[1].uuid), + namespace="demo", + entity_label="Knowledge", + metadata={"confidence": 0.9}, + ) + mm.store_triplets([relation]) + print("Stored relationship between first two memories.") + + stored = mm.list_memories(namespace="demo", entity_labels=["Memory"], limit=10) + + hits = mm.search("Eiffel Tower", namespace="demo", entity_labels=["Memory"]) + print("Hybrid search results:") + for mem in hits: + print(f"- {mem.name} (importance={mem.importance})") + + vector_hits = mm.search_vector("programming", namespace="demo", entity_labels=["Memory"]) + print("Vector-only search results:") + for mem in vector_hits: + print(f"- {mem.name}") + + regex_hits = mm.search_regex(r"Paris", namespace="demo", entity_labels=["Memory"]) + print("Regex search results:") + for mem in regex_hits: + print(f"- {mem.name}") + + exact_hits = mm.search_exact( + "Python", + namespace="demo", + entity_labels=["Memory"], + fields=["name"], + ) + print("Exact match search results:") + for mem in exact_hits: + print(f"- {mem.name}") + + counts = mm.memory_counts(namespace="demo") + print(f"Filtered view returned {len(stored)} memories from the driver.") + print(f"Namespace counts: {counts}") + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/meshmind/_compat/pydantic.py b/meshmind/_compat/pydantic.py new file mode 100644 index 0000000..d3435cb --- /dev/null +++ b/meshmind/_compat/pydantic.py @@ -0,0 +1,152 @@ +"""Compatibility helpers for environments without :mod:`pydantic`.""" +from __future__ import annotations + +from dataclasses import MISSING +from datetime import datetime, timezone +from typing import Any, Dict, List, Optional, TypeVar, Union, get_args, get_origin, get_type_hints +from uuid import UUID + +try: # pragma: no cover - prefer real pydantic when available + from pydantic import BaseModel as _RealBaseModel # type: ignore + from pydantic import Field # type: ignore + from pydantic import ValidationError # type: ignore + + BaseModel = _RealBaseModel +except ImportError: # pragma: no cover - exercised in constrained sandboxes + Field = None # type: ignore + + class ValidationError(Exception): + """Fallback validation error raised by the compatibility layer.""" + + class _FieldInfo: + __slots__ = ("default", "default_factory") + + def __init__(self, default: Any = MISSING, default_factory: Optional[Any] = None) -> None: + self.default = default + self.default_factory = default_factory + + def default_value(self) -> Any: + if self.default_factory is not None: + return self.default_factory() + if self.default is MISSING: + return None + return self.default + + def _aware_utcnow() -> datetime: + return datetime.now(timezone.utc) + + def Field(*, default: Any = MISSING, default_factory: Optional[Any] = None) -> _FieldInfo: # type: ignore[override] + if default_factory is datetime.utcnow: + default_factory = _aware_utcnow + return _FieldInfo(default=default, default_factory=default_factory) + + _T = TypeVar("_T", bound="BaseModel") + + class BaseModel: + """Tiny subset of :class:`pydantic.BaseModel` used in the project.""" + + def __init__(self, **data: Any) -> None: + hints = get_type_hints(self.__class__) + for name, annotation in hints.items(): + value = data.pop(name, MISSING) + if value is MISSING: + default_obj = getattr(self.__class__, name, MISSING) + if isinstance(default_obj, _FieldInfo): + value = default_obj.default_value() + elif default_obj is not MISSING: + value = default_obj + else: + value = None + value = self._coerce(annotation, value) + setattr(self, name, value) + for key, value in data.items(): + setattr(self, key, value) + + @classmethod + def _coerce(cls, annotation: Any, value: Any) -> Any: + if value is None: + return None + + origin = get_origin(annotation) + if origin is Union: + for candidate in get_args(annotation): + try: + return cls._coerce(candidate, value) + except Exception: + continue + return value + + if annotation in (str, int, float, bool): + return annotation(value) + + if annotation is UUID: + if isinstance(value, UUID): + return value + return UUID(str(value)) + + if annotation is datetime: + if isinstance(value, datetime): + return value if value.tzinfo else value.replace(tzinfo=timezone.utc) + parsed = datetime.fromisoformat(str(value)) + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=timezone.utc) + return parsed + + if origin is list: + inner = get_args(annotation)[0] if get_args(annotation) else Any + return [cls._coerce(inner, item) for item in value] + + if origin is dict: + key_type, val_type = (Any, Any) + args = get_args(annotation) + if len(args) == 2: + key_type, val_type = args + return { + cls._coerce(key_type, k): cls._coerce(val_type, v) + for k, v in value.items() + } + + return value + + def dict(self, *, exclude_none: bool = False) -> Dict[str, Any]: + data = dict(self.__dict__) + if exclude_none: + data = {k: v for k, v in data.items() if v is not None} + return data + + def model_dump(self, *, exclude_none: bool = False) -> Dict[str, Any]: + return self.dict(exclude_none=exclude_none) + + def model_copy(self: _T, *, update: Optional[Dict[str, Any]] = None) -> _T: + payload = self.dict() + if update: + payload.update(update) + return self.__class__(**payload) + + @classmethod + def schema(cls) -> Dict[str, Any]: + hints = get_type_hints(cls) + properties: Dict[str, Any] = {} + required: List[str] = [] + for name, annotation in hints.items(): + properties[name] = {"type": _schema_type(annotation)} + required.append(name) + return {"type": "object", "properties": properties, "required": required} + + def __repr__(self) -> str: # pragma: no cover - debug helper + params = ", ".join(f"{k}={v!r}" for k, v in self.dict().items()) + return f"{self.__class__.__name__}({params})" + + def _schema_type(annotation: Any) -> str: + origin = get_origin(annotation) + if origin is list: + return "array" + if origin is dict: + return "object" + if annotation in (int, float): + return "number" + if annotation is bool: + return "boolean" + return "string" + +__all__ = ["BaseModel", "Field", "ValidationError"] diff --git a/meshmind/api/grpc.py b/meshmind/api/grpc.py new file mode 100644 index 0000000..dc7498e --- /dev/null +++ b/meshmind/api/grpc.py @@ -0,0 +1,108 @@ +"""gRPC-style adapters for MeshMind.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Dict, Iterable, List, Optional + +from meshmind.api.service import MemoryPayload, MemoryService, SearchPayload, TripletPayload + + +@dataclass +class IngestMemoriesRequest: + memories: List[dict] = field(default_factory=list) + + +@dataclass +class IngestMemoriesResponse: + uuids: List[str] = field(default_factory=list) + + +@dataclass +class IngestTripletsRequest: + triplets: List[dict] = field(default_factory=list) + + +@dataclass +class IngestTripletsResponse: + stored: int = 0 + + +@dataclass +class SearchRequest: + query: str + namespace: str | None = None + top_k: int = 10 + encoder: str | None = None + rerank_k: int | None = None + entity_labels: List[str] | None = None + rerank_model: str | None = None + use_llm_rerank: bool = False + llm_models: Dict[str, str] | None = None + llm_base_urls: Dict[str, Optional[str]] | None = None + llm_api_key: str | None = None + + +@dataclass +class SearchResponse: + results: List[dict] = field(default_factory=list) + + +@dataclass +class MemoryCountsRequest: + namespace: str | None = None + + +@dataclass +class MemoryCountsResponse: + counts: Dict[str, Dict[str, int]] = field(default_factory=dict) + + +class GrpcServiceStub: + """Simple callable object that mirrors gRPC service behaviour for tests.""" + + def __init__(self, service: MemoryService) -> None: + self.service = service + + def IngestMemories(self, request: IngestMemoriesRequest) -> IngestMemoriesResponse: # noqa: N802 + payloads = [MemoryPayload(**item) for item in request.memories] + uuids = self.service.ingest_memories(payloads) + return IngestMemoriesResponse(uuids=uuids) + + def IngestTriplets(self, request: IngestTripletsRequest) -> IngestTripletsResponse: # noqa: N802 + payloads = [TripletPayload(**item) for item in request.triplets] + stored = self.service.ingest_triplets(payloads) + return IngestTripletsResponse(stored=stored) + + def Search(self, request: SearchRequest) -> SearchResponse: # noqa: N802 + payload = SearchPayload( + query=request.query, + namespace=request.namespace, + top_k=request.top_k, + encoder=request.encoder, + rerank_k=request.rerank_k, + entity_labels=request.entity_labels, + rerank_model=request.rerank_model, + use_llm_rerank=request.use_llm_rerank, + llm_models=request.llm_models, + llm_base_urls=request.llm_base_urls, + llm_api_key=request.llm_api_key, + ) + results = self.service.search(payload) + return SearchResponse(results=[mem.dict() for mem in results]) + + def MemoryCounts(self, request: MemoryCountsRequest) -> MemoryCountsResponse: # noqa: N802 + counts = self.service.memory_counts(namespace=request.namespace) + return MemoryCountsResponse(counts=counts) + + +__all__ = [ + "GrpcServiceStub", + "IngestMemoriesRequest", + "IngestMemoriesResponse", + "IngestTripletsRequest", + "IngestTripletsResponse", + "SearchRequest", + "SearchResponse", + "MemoryCountsRequest", + "MemoryCountsResponse", +] diff --git a/meshmind/api/memory_manager.py b/meshmind/api/memory_manager.py index 935d270..c22f49d 100644 --- a/meshmind/api/memory_manager.py +++ b/meshmind/api/memory_manager.py @@ -1,39 +1,50 @@ -from typing import Any, List, Optional +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Sequence from uuid import UUID +from meshmind._compat.pydantic import BaseModel + +from meshmind.core.types import Memory, Triplet + + class MemoryManager: - """ - Mid-level CRUD interface for Memory objects, delegating to an underlying graph driver. - """ + """Mid-level CRUD interface for ``Memory`` and ``Triplet`` objects.""" + def __init__(self, graph_driver: Any): # pragma: no cover self.driver = graph_driver - def add_memory(self, memory: Any) -> UUID: + @staticmethod + def _props(model: Any) -> Dict[str, Any]: + if isinstance(model, BaseModel): + return model.dict(exclude_none=True) + if hasattr(model, "dict"): + try: + return model.dict(exclude_none=True) # type: ignore[attr-defined] + except TypeError: + pass + if isinstance(model, dict): + return {k: v for k, v in model.items() if v is not None} + return {k: v for k, v in model.__dict__.items() if v is not None} + + def add_memory(self, memory: Memory) -> UUID: """ Add a new Memory object to the graph. :param memory: A Memory-like object to be stored. :return: The UUID of the newly added memory. """ - # Upsert the memory object into the graph - try: - props = memory.dict(exclude_none=True) - except Exception: - props = memory.__dict__ + props = self._props(memory) self.driver.upsert_entity(memory.entity_label, memory.name, props) return memory.uuid - def update_memory(self, memory: Any) -> None: + def update_memory(self, memory: Memory) -> None: """ Update an existing Memory object in the graph. :param memory: A Memory-like object with updated fields. """ - # Update an existing memory via upsert - try: - props = memory.dict(exclude_none=True) - except Exception: - props = memory.__dict__ + props = self._props(memory) self.driver.upsert_entity(memory.entity_label, memory.name, props) def delete_memory(self, memory_id: UUID) -> None: @@ -52,44 +63,104 @@ def get_memory(self, memory_id: UUID) -> Optional[Any]: :param memory_id: UUID of the memory to retrieve. :return: Memory-like object or None if not found. """ - # Retrieve a memory by UUID - from meshmind.core.types import Memory - - cypher = "MATCH (m) WHERE m.uuid = $uuid RETURN m" - params = {"uuid": str(memory_id)} - records = self.driver.find(cypher, params) - if not records: + payload = self.driver.get_entity(str(memory_id)) + if not payload: return None - # Extract node properties - record = records[0] - data = record.get('m', record) try: - return Memory(**data) + return Memory(**payload) except Exception: return None - def list_memories(self, namespace: Optional[str] = None) -> List[Any]: + def list_memories( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + query: Optional[str] = None, + use_search: bool | None = None, + ) -> List[Memory]: """ - List Memory objects, optionally filtered by namespace. + List Memory objects, optionally filtered by namespace and entity label. :param namespace: If provided, only return memories in this namespace. + :param entity_labels: Optional entity labels to restrict the results. + :param offset: Offset into the result set for pagination. + :param limit: Maximum number of memories to return. + :param query: Optional search string to hint server-side filtering. + :param use_search: Force enabling/disabling the driver's search routine. :return: List of Memory-like objects. """ - # List memories, optionally filtered by namespace - from meshmind.core.types import Memory - - if namespace: - cypher = "MATCH (m) WHERE m.namespace = $namespace RETURN m" - params = {"namespace": namespace} + should_search = use_search if use_search is not None else bool(query) + if should_search and hasattr(self.driver, "search_entities"): + search_fn = getattr(self.driver, "search_entities") + records = search_fn( # type: ignore[operator] + query=query, + namespace=namespace, + entity_labels=entity_labels, + offset=offset, + limit=limit, + ) else: - cypher = "MATCH (m) RETURN m" - params = {} - records = self.driver.find(cypher, params) - result: List[Any] = [] - for record in records: - data = record.get('m', record) + records = self.driver.list_entities( + namespace, + entity_labels, + offset=offset, + limit=limit, + ) + result: List[Memory] = [] + for data in records: try: result.append(Memory(**data)) except Exception: continue - return result \ No newline at end of file + return result + + def count_memories(self, namespace: Optional[str] = None) -> Dict[str, Dict[str, int]]: + """Aggregate memory counts grouped by namespace and entity label.""" + + if not hasattr(self.driver, "count_entities"): + raise NotImplementedError("Graph driver does not implement count_entities") + counter = getattr(self.driver, "count_entities") + return counter(namespace) # type: ignore[misc] + + def add_triplet(self, triplet: Triplet) -> None: + """Persist or update a ``Triplet`` relationship.""" + + props = self._props(triplet) + namespace = props.pop("namespace", None) + if namespace is not None: + props["namespace"] = namespace + self.driver.upsert_edge( + triplet.subject, + triplet.predicate, + triplet.object, + props, + ) + + def delete_triplet(self, subj: str, predicate: str, obj: str) -> None: + """Remove a relationship identified by subject/predicate/object.""" + + self.driver.delete_triplet(subj, predicate, obj) + + def list_triplets(self, namespace: Optional[str] = None) -> List[Triplet]: + """Return stored ``Triplet`` objects, optionally filtered by namespace.""" + + records = self.driver.list_triplets(namespace) + result: List[Triplet] = [] + for record in records: + data = { + "subject": record.get("subject"), + "predicate": record.get("predicate"), + "object": record.get("object"), + "namespace": record.get("namespace") or namespace, + "entity_label": record.get("predicate", "Relation"), + "metadata": record.get("metadata") or {}, + "reference_time": record.get("reference_time"), + } + try: + result.append(Triplet(**data)) + except Exception: + continue + return result diff --git a/meshmind/api/rest.py b/meshmind/api/rest.py new file mode 100644 index 0000000..0d2dcd2 --- /dev/null +++ b/meshmind/api/rest.py @@ -0,0 +1,123 @@ +"""REST adapters for the :mod:`meshmind` service layer.""" +from __future__ import annotations + +from typing import Any, Dict, Iterable, List + +from meshmind.api.service import MemoryPayload, MemoryService, SearchPayload, TripletPayload + + +class RestAPIStub: + """Fallback handler that emulates REST routes without FastAPI.""" + + def __init__(self, service: MemoryService) -> None: + self.service = service + + def dispatch(self, method: str, path: str, payload: Dict[str, Any] | None = None) -> Dict[str, Any]: + method = method.upper() + payload = payload or {} + if method == "POST" and path == "/memories": + memories = [MemoryPayload(**item) for item in payload.get("memories", [])] + uuids = self.service.ingest_memories(memories) + return {"uuids": uuids} + if method == "POST" and path == "/triplets": + triplets = [TripletPayload(**item) for item in payload.get("triplets", [])] + count = self.service.ingest_triplets(triplets) + return {"stored": count} + if method == "POST" and path == "/search": + request = SearchPayload(**payload) + results = self.service.search(request) + return {"results": [mem.dict() for mem in results]} + if method == "GET" and path == "/memories": + namespace = payload.get("namespace") + entity_labels = payload.get("entity_labels") + offset = int(payload.get("offset", 0)) + limit_value = payload.get("limit") + limit = int(limit_value) if limit_value is not None else None + query = payload.get("query") + use_search = payload.get("use_search") + memories = self.service.list_memories( + namespace, + entity_labels, + offset=offset, + limit=limit, + query=query, + use_search=use_search, + ) + return {"memories": [mem.dict() for mem in memories]} + if method == "GET" and path == "/memories/counts": + namespace = payload.get("namespace") + counts = self.service.memory_counts(namespace) + return {"counts": counts} + if method == "GET" and path == "/triplets": + namespace = payload.get("namespace") + triplets = self.service.list_triplets(namespace) + return {"triplets": [triplet.dict() for triplet in triplets]} + raise ValueError(f"Unsupported route {method} {path}") + + +def create_app(service: MemoryService) -> Any: + """Create a FastAPI application if FastAPI is installed, otherwise return a stub.""" + + try: # pragma: no cover - optional dependency path + from fastapi import FastAPI, HTTPException + except ImportError: # pragma: no cover - executed in tests without fastapi + return RestAPIStub(service) + + app = FastAPI(title="MeshMind API") + + @app.post("/memories") + def create_memories(payload: Dict[str, Iterable[Dict[str, Any]]]): + try: + items = [MemoryPayload(**item) for item in payload.get("memories", [])] + except Exception as exc: # pragma: no cover - FastAPI handles validation + raise HTTPException(status_code=400, detail=str(exc)) + uuids = service.ingest_memories(items) + return {"uuids": uuids} + + @app.post("/triplets") + def create_triplets(payload: Dict[str, Iterable[Dict[str, Any]]]): + try: + items = [TripletPayload(**item) for item in payload.get("triplets", [])] + except Exception as exc: # pragma: no cover + raise HTTPException(status_code=400, detail=str(exc)) + stored = service.ingest_triplets(items) + return {"stored": stored} + + @app.post("/search") + def search(payload: Dict[str, Any]): + try: + request = SearchPayload(**payload) + except Exception as exc: # pragma: no cover + raise HTTPException(status_code=400, detail=str(exc)) + results = service.search(request) + return {"results": [mem.dict() for mem in results]} + + @app.get("/memories") + def list_memories( + namespace: str | None = None, + entity_labels: List[str] | None = None, + offset: int = 0, + limit: int | None = None, + query: str | None = None, + use_search: bool | None = None, + ): + memories = service.list_memories( + namespace, + entity_labels, + offset=offset, + limit=limit, + query=query, + use_search=use_search, + ) + return {"memories": [mem.dict() for mem in memories]} + + @app.get("/triplets") + def list_triplets(namespace: str | None = None): + triplets = service.list_triplets(namespace) + return {"triplets": [triplet.dict() for triplet in triplets]} + + @app.get("/memories/counts") + def memory_counts(namespace: str | None = None): + return {"counts": service.memory_counts(namespace)} + + return app diff --git a/meshmind/api/service.py b/meshmind/api/service.py new file mode 100644 index 0000000..4b4afd7 --- /dev/null +++ b/meshmind/api/service.py @@ -0,0 +1,211 @@ +"""Service layer abstractions for REST and gRPC adapters.""" +from __future__ import annotations + +from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence + +from meshmind._compat.pydantic import BaseModel, Field + +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.types import Memory, SearchConfig, Triplet +from meshmind.retrieval import llm_rerank, search as retrieval_search + + +class MemoryPayload(BaseModel): + """Serializable payload for creating or updating a memory.""" + + uuid: str | None = None + namespace: str + name: str + entity_label: str = "Memory" + embedding: List[float] | None = None + metadata: dict[str, object] = Field(default_factory=dict) + reference_time: str | None = None + importance: float | None = None + ttl_seconds: int | None = None + + def to_memory(self) -> Memory: + payload = self.model_dump(exclude_none=True) + if self.uuid: + payload["uuid"] = self.uuid + return Memory(**payload) + + +class TripletPayload(BaseModel): + subject: str + predicate: str + object: str + namespace: str + entity_label: str = "Relation" + metadata: dict[str, object] = Field(default_factory=dict) + reference_time: str | None = None + + def to_triplet(self) -> Triplet: + return Triplet(**self.model_dump(exclude_none=True)) + + +class SearchPayload(BaseModel): + query: str + namespace: str | None = None + top_k: int = 10 + encoder: str | None = None + rerank_k: int | None = None + entity_labels: Sequence[str] | None = None + rerank_model: str | None = None + use_llm_rerank: bool = False + llm_models: Dict[str, str] | None = None + llm_base_urls: Dict[str, Optional[str]] | None = None + llm_api_key: str | None = None + + def to_config(self) -> SearchConfig: + config = SearchConfig(top_k=self.top_k) + if self.encoder: + config.encoder = self.encoder + if self.rerank_k is not None: + config.rerank_k = self.rerank_k + if self.rerank_model: + config.rerank_model = self.rerank_model + elif self.llm_models and self.llm_models.get("rerank"): + config.rerank_model = self.llm_models["rerank"] + return config + + def llm_override_kwargs(self) -> Dict[str, Any]: + return { + "models": self.llm_models, + "base_urls": self.llm_base_urls, + "api_key": self.llm_api_key, + } + + +class MemoryService: + """Business logic for ingestion, retrieval, and triplet persistence.""" + + def __init__( + self, + manager: MemoryManager, + *, + llm_client: Any | None = None, + llm_client_factory: Callable[[], Any] | None = None, + ) -> None: + self.manager = manager + self._llm_client: Any | None = llm_client + self._llm_client_factory = llm_client_factory + + @property + def llm_client(self) -> Any | None: + """Expose the cached LLM client (mainly for tests).""" + + return self._llm_client + + def _resolve_llm_client(self) -> Any | None: + if self._llm_client is not None: + return self._llm_client + if self._llm_client_factory is None: + return None + try: + self._llm_client = self._llm_client_factory() + except Exception: + return None + return self._llm_client + + # ------------------------------------------------------------------ + # Ingestion + # ------------------------------------------------------------------ + def ingest_memories(self, payloads: Sequence[MemoryPayload]) -> List[str]: + uuids: List[str] = [] + for payload in payloads: + memory = payload.to_memory() + self.manager.add_memory(memory) + uuids.append(str(memory.uuid)) + return uuids + + def ingest_triplets(self, payloads: Iterable[TripletPayload]) -> int: + count = 0 + for payload in payloads: + self.manager.add_triplet(payload.to_triplet()) + count += 1 + return count + + # ------------------------------------------------------------------ + # Retrieval + # ------------------------------------------------------------------ + def search(self, request: SearchPayload) -> List[Memory]: + config = request.to_config() + candidate_limit = max(config.top_k * 5, (config.rerank_k or 0) * 2) + limit = candidate_limit or None + memories = self.manager.list_memories( + namespace=request.namespace, + entity_labels=request.entity_labels, + limit=limit, + query=request.query, + use_search=True, + ) + reranker = None + if request.use_llm_rerank: + llm_client = self._resolve_llm_client() + if llm_client is not None: + overrides = request.llm_override_kwargs() + if any(overrides.values()): + with_overrides = getattr(llm_client, "with_overrides", None) + if callable(with_overrides): + llm_client = with_overrides(**overrides) + endpoint_override = None + if request.llm_base_urls: + endpoint_override = ( + request.llm_base_urls.get("rerank") + or request.llm_base_urls.get("default") + ) + rerank_model = config.rerank_model + + def _reranker( + query: str, + candidates: Sequence[Memory], + top_k: int, + *, + client: Any = llm_client, + model: str | None = rerank_model, + endpoint: str | None = endpoint_override, + ) -> Sequence[Memory]: + return llm_rerank( + query, + candidates, + client, + top_k, + model=model, + endpoint=endpoint, + ) + + reranker = _reranker + return retrieval_search( + request.query, + memories, + config=config, + reranker=reranker, + ) + + # ------------------------------------------------------------------ + # CRUD proxies + # ------------------------------------------------------------------ + def list_memories( + self, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + *, + offset: int = 0, + limit: int | None = None, + query: str | None = None, + use_search: bool | None = None, + ) -> List[Memory]: + return self.manager.list_memories( + namespace, + entity_labels, + offset=offset, + limit=limit, + query=query, + use_search=use_search, + ) + + def list_triplets(self, namespace: str | None = None) -> List[Triplet]: + return self.manager.list_triplets(namespace) + + def memory_counts(self, namespace: str | None = None) -> Dict[str, Dict[str, int]]: + return self.manager.count_memories(namespace) diff --git a/meshmind/cli/__main__.py b/meshmind/cli/__main__.py index 24dab53..0b39cca 100644 --- a/meshmind/cli/__main__.py +++ b/meshmind/cli/__main__.py @@ -5,7 +5,10 @@ import argparse import sys +from meshmind.cli.admin import register_admin_subcommands from meshmind.cli.ingest import ingest_command +from meshmind.core.bootstrap import bootstrap_encoders, bootstrap_entities +from meshmind.core.config import settings def main(): @@ -25,22 +28,74 @@ def main(): "-e", "--embedding-model", default=None, help="Embedding model to use (default from settings)" ) + ingest_parser.add_argument( + "--embedding-endpoint", + default=None, + help="Override the endpoint URL for embedding requests", + ) ingest_parser.add_argument( "-i", "--instructions", default="Extract key facts as Memory objects.", help="Instructions for the extraction LLM prompt" ) + ingest_parser.add_argument( + "--llm-base-url", + default=None, + help="Override the base URL for the LLM provider", + ) + ingest_parser.add_argument( + "--llm-api-key", + default=None, + help="Override the API key used for LLM calls", + ) + ingest_parser.add_argument( + "--extraction-model", + default=None, + help="Model identifier for the extraction step", + ) + ingest_parser.add_argument( + "--extraction-endpoint", + default=None, + help="Endpoint URL for extraction requests", + ) + ingest_parser.add_argument( + "--rerank-model", + default=None, + help="Model identifier for reranking", + ) + ingest_parser.add_argument( + "--rerank-endpoint", + default=None, + help="Endpoint URL for reranking requests", + ) ingest_parser.add_argument( "paths", nargs="+", help="Paths to files or directories to ingest" ) + ingest_parser.set_defaults(func=ingest_command) + + register_admin_subcommands(subparsers) args = parser.parse_args() - if args.command == "ingest": - ingest_command(args) - else: - parser.print_help() - sys.exit(1) + # Ensure default encoders and entities are registered before executing commands + bootstrap_entities() + bootstrap_encoders() + + missing = settings.missing() + if missing: + for group, keys in missing.items(): + print( + f"Warning: missing configuration for {group}: {', '.join(keys)}", + file=sys.stderr, + ) + + func = getattr(args, "func", None) + if callable(func): + result = func(args) + return result + + parser.print_help() + sys.exit(1) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/meshmind/cli/admin.py b/meshmind/cli/admin.py new file mode 100644 index 0000000..41b1cd7 --- /dev/null +++ b/meshmind/cli/admin.py @@ -0,0 +1,147 @@ +"""Administrative CLI helpers for MeshMind.""" +from __future__ import annotations + +import argparse +import json +import sys +from typing import TextIO + +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.config import settings +from meshmind.core.observability import telemetry +from meshmind.db.factory import create_graph_driver +from meshmind.models.registry import PredicateRegistry + + +def register_admin_subcommands(subparsers: argparse._SubParsersAction) -> None: + """Attach admin subcommands to the CLI parser.""" + + admin_parser = subparsers.add_parser("admin", help="Administrative commands") + admin_sub = admin_parser.add_subparsers(dest="admin_command") + + predicate_parser = admin_sub.add_parser( + "predicates", help="Manage the predicate registry" + ) + predicate_parser.add_argument("--list", action="store_true", help="List predicates") + predicate_parser.add_argument("--add", metavar="LABEL", help="Add a predicate label") + predicate_parser.add_argument( + "--remove", metavar="LABEL", help="Remove a predicate label if present" + ) + predicate_parser.set_defaults(func=handle_predicates) + + maintenance_parser = admin_sub.add_parser( + "maintenance", help="Inspect maintenance telemetry" + ) + maintenance_parser.add_argument( + "--reset", action="store_true", help="Reset telemetry after printing" + ) + maintenance_parser.set_defaults(func=handle_maintenance) + + graph_parser = admin_sub.add_parser( + "graph", help="Validate graph backend connectivity" + ) + graph_parser.add_argument( + "--backend", + default=settings.GRAPH_BACKEND, + help="Backend to test (memory, sqlite, memgraph, neo4j)", + ) + graph_parser.set_defaults(func=handle_graph_check) + + counts_parser = admin_sub.add_parser( + "counts", help="Summarise memory counts by namespace and entity label" + ) + counts_parser.add_argument( + "--backend", + default=settings.GRAPH_BACKEND, + help="Graph backend to inspect", + ) + counts_parser.add_argument( + "--namespace", + default=None, + help="Optional namespace filter", + ) + counts_parser.set_defaults(func=handle_counts) + + +def handle_predicates(args: argparse.Namespace, stream: TextIO | None = None) -> None: + """Apply predicate registry operations based on CLI flags.""" + + stream = stream or sys.stdout + updated = False + if args.add: + PredicateRegistry.add(args.add) + print(f"Registered predicate: {args.add}", file=stream) + updated = True + if args.remove: + removed = PredicateRegistry.remove(args.remove) + status = "Removed" if removed else "Not present" + print(f"{status} predicate: {args.remove}", file=stream) + updated = True + if args.list or not updated: + labels = sorted(PredicateRegistry.all()) + print(json.dumps({"predicates": labels}, indent=2), file=stream) + + +def handle_maintenance(args: argparse.Namespace, stream: TextIO | None = None) -> None: + """Print maintenance telemetry and optionally reset it.""" + + stream = stream or sys.stdout + snapshot = telemetry.snapshot() + print(json.dumps(snapshot, indent=2, sort_keys=True), file=stream) + if args.reset: + telemetry.reset() + print("Telemetry reset", file=stream) + + +def handle_graph_check(args: argparse.Namespace, stream: TextIO | None = None) -> int: + """Try to instantiate the requested graph driver and verify connectivity.""" + + stream = stream or sys.stdout + try: + driver = create_graph_driver(backend=args.backend) + except Exception as exc: # pragma: no cover - best effort logging + print(f"Failed to create driver: {exc}", file=sys.stderr) + return 1 + + verify = getattr(driver, "verify_connectivity", None) + if callable(verify): + try: + ok = bool(verify()) + except Exception as exc: # pragma: no cover - depends on backend state + print(f"Connectivity check failed: {exc}", file=sys.stderr) + return 2 + else: + result = "ok" if ok else "unknown" + print(json.dumps({"backend": args.backend, "status": result}), file=stream) + return 0 + + print( + json.dumps({"backend": args.backend, "status": "unsupported"}), + file=stream, + ) + return 0 + + +def handle_counts(args: argparse.Namespace, stream: TextIO | None = None) -> int: + """Print aggregated memory counts grouped by namespace and label.""" + + stream = stream or sys.stdout + try: + driver = create_graph_driver(backend=args.backend) + except Exception as exc: # pragma: no cover - backend instantiation failures + print(f"Failed to create driver: {exc}", file=sys.stderr) + return 1 + + manager = MemoryManager(driver) + try: + counts = manager.count_memories(namespace=args.namespace) + except NotImplementedError as exc: + print(str(exc), file=sys.stderr) + return 2 + finally: + closer = getattr(driver, "close", None) + if callable(closer): # pragma: no cover - depends on backend + closer() + + print(json.dumps(counts, indent=2, sort_keys=True), file=stream) + return 0 diff --git a/meshmind/cli/ingest.py b/meshmind/cli/ingest.py index bfedfa9..60b5113 100644 --- a/meshmind/cli/ingest.py +++ b/meshmind/cli/ingest.py @@ -1,11 +1,14 @@ """ CLI ingest command: load files/folders → extract → preprocess → store. """ +from __future__ import annotations import os import sys from meshmind.client import MeshMind +from meshmind.core.config import settings from meshmind.core.types import Memory +from meshmind.llm_client import build_llm_config_from_settings def ingest_command(args): """ @@ -35,8 +38,35 @@ def ingest_command(args): print("No content found to ingest.", file=sys.stderr) sys.exit(1) + models_override: dict[str, str] = {} + base_urls_override: dict[str, str] = {} + if getattr(args, "embedding_model", None): + models_override["embedding"] = args.embedding_model + if getattr(args, "extraction_model", None): + models_override["extraction"] = args.extraction_model + if getattr(args, "rerank_model", None): + models_override["rerank"] = args.rerank_model + + if getattr(args, "llm_base_url", None): + base_urls_override["default"] = args.llm_base_url + if getattr(args, "embedding_endpoint", None): + base_urls_override["embedding"] = args.embedding_endpoint + if getattr(args, "extraction_endpoint", None): + base_urls_override["extraction"] = args.extraction_endpoint + if getattr(args, "rerank_endpoint", None): + base_urls_override["rerank"] = args.rerank_endpoint + + overrides_models = models_override or None + overrides_base = base_urls_override or None + api_key_override = getattr(args, "llm_api_key", None) or None + llm_config = build_llm_config_from_settings(settings).override( + models=overrides_models, + base_urls=overrides_base, + api_key=api_key_override, + ) + # Extraction - mm = MeshMind() + mm = MeshMind(llm_config=llm_config) memories = mm.extract_memories( instructions=args.instructions, namespace=args.namespace, diff --git a/meshmind/client.py b/meshmind/client.py index 365eac0..8feb1fb 100644 --- a/meshmind/client.py +++ b/meshmind/client.py @@ -1,80 +1,335 @@ -""" -MeshMind client combining LLM, embedding, and graph driver. -""" -from openai import OpenAI -from typing import Any, List, Type -from meshmind.db.memgraph_driver import MemgraphDriver +"""High-level MeshMind client orchestrating ingestion and storage flows.""" +from __future__ import annotations + +from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Type +from uuid import UUID + +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.bootstrap import bootstrap_entities, bootstrap_encoders from meshmind.core.config import settings +from meshmind.core.types import Memory, Triplet, SearchConfig +from meshmind.db.base_driver import GraphDriver +from meshmind.db.factory import graph_driver_factory as make_graph_driver_factory +from meshmind.models.registry import EntityRegistry, PredicateRegistry +from meshmind.llm_client import LLMClient, LLMConfig, build_llm_config_from_settings class MeshMind: - """ - High-level client to manage extraction, preprocessing, and storage of memories. - """ + """High-level orchestration client for extraction, preprocessing, and persistence.""" + def __init__( self, llm_client: Any = None, + llm_config: LLMConfig | None = None, embedding_model: str | None = None, - graph_driver: Any = None, + graph_driver: Optional[GraphDriver] = None, + graph_driver_factory: Callable[[], GraphDriver] | None = None, ): - # Initialize LLM client - self.llm_client = llm_client or OpenAI() - # Set embedding model name - self.embedding_model = embedding_model or settings.EMBEDDING_MODEL - # Initialize graph driver - self.driver = graph_driver or MemgraphDriver( - settings.MEMGRAPH_URI, - settings.MEMGRAPH_USERNAME, - settings.MEMGRAPH_PASSWORD, + config = llm_config or build_llm_config_from_settings(settings) + if embedding_model: + config = config.override(models={"embedding": embedding_model}) + self.llm_config = config + + if llm_client is None: + llm_client = LLMClient(config) + + self.llm_client = llm_client + self.embedding_model = config.model_for("embedding", fallback=settings.EMBEDDING_MODEL) + + self._graph_driver: Optional[GraphDriver] = graph_driver + self._graph_driver_factory = graph_driver_factory + if self._graph_driver is None and self._graph_driver_factory is None: + self._graph_driver_factory = make_graph_driver_factory() + + self._memory_manager: Optional[MemoryManager] = ( + MemoryManager(self._graph_driver) if self._graph_driver else None ) + self.entity_registry = EntityRegistry + self.predicate_registry = PredicateRegistry + bootstrap_entities([Memory]) + bootstrap_encoders() + @property + def graph_driver(self) -> GraphDriver: + """Expose the active graph driver, creating it on demand.""" + return self._ensure_driver() + + @property + def driver(self) -> GraphDriver: + """Backward compatible alias for :attr:`graph_driver`.""" + return self.graph_driver + + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + def _ensure_driver(self) -> GraphDriver: + if self._graph_driver is None: + if self._graph_driver_factory is None: + raise RuntimeError("No graph driver factory available for MeshMind") + self._graph_driver = self._graph_driver_factory() + return self._graph_driver + + def _ensure_manager(self) -> MemoryManager: + if self._memory_manager is None: + self._memory_manager = MemoryManager(self._ensure_driver()) + return self._memory_manager + + # ------------------------------------------------------------------ + # Pipelines + # ------------------------------------------------------------------ def extract_memories( self, instructions: str, namespace: str, - entity_types: List[Type[Any]], - content: List[str], + entity_types: Sequence[Type[Any]], + content: Sequence[str], ) -> List[Any]: from meshmind.pipeline.extract import extract_memories return extract_memories( instructions=instructions, namespace=namespace, - entity_types=entity_types, + entity_types=list(entity_types), embedding_model=self.embedding_model, - content=content, + content=list(content), llm_client=self.llm_client, ) def deduplicate( self, - memories: List[Any], + memories: Sequence[Any], threshold: float = 0.95, ) -> List[Any]: from meshmind.pipeline.preprocess import deduplicate - return deduplicate(memories, threshold) + return deduplicate(list(memories), threshold) def score_importance( self, - memories: List[Any], + memories: Sequence[Any], ) -> List[Any]: from meshmind.pipeline.preprocess import score_importance - return score_importance(memories) + return score_importance(list(memories)) def compress( self, - memories: List[Any], + memories: Sequence[Any], ) -> List[Any]: from meshmind.pipeline.preprocess import compress - return compress(memories) + return compress(list(memories)) def store_memories( self, - memories: List[Any], + memories: Iterable[Any], ) -> None: from meshmind.pipeline.store import store_memories - store_memories(memories, self.driver) + store_memories( + memories, + self._ensure_driver(), + entity_registry=self.entity_registry, + ) + + def store_triplets( + self, + triplets: Iterable[Triplet], + ) -> None: + from meshmind.pipeline.store import store_triplets + + store_triplets( + triplets, + self._ensure_driver(), + predicate_registry=self.predicate_registry, + ) + + # ------------------------------------------------------------------ + # CRUD helpers + # ------------------------------------------------------------------ + def create_memory(self, memory: Memory) -> UUID: + return self._ensure_manager().add_memory(memory) + + def update_memory(self, memory: Memory) -> None: + self._ensure_manager().update_memory(memory) + + def delete_memory(self, memory_id: UUID) -> None: + self._ensure_manager().delete_memory(memory_id) + + def get_memory(self, memory_id: UUID) -> Optional[Memory]: + return self._ensure_manager().get_memory(memory_id) + + def list_memories( + self, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + *, + offset: int = 0, + limit: int | None = None, + query: str | None = None, + use_search: bool | None = None, + ) -> List[Memory]: + return self._ensure_manager().list_memories( + namespace, + entity_labels, + offset=offset, + limit=limit, + query=query, + use_search=use_search, + ) + + def memory_counts(self, namespace: str | None = None) -> Dict[str, Dict[str, int]]: + return self._ensure_manager().count_memories(namespace) + + def create_triplet(self, triplet: Triplet) -> None: + self.predicate_registry.add(triplet.predicate) + self._ensure_manager().add_triplet(triplet) + + def delete_triplet(self, triplet: Triplet) -> None: + self._ensure_manager().delete_triplet( + triplet.subject, triplet.predicate, triplet.object + ) + + def list_triplets(self, namespace: str | None = None) -> List[Triplet]: + return self._ensure_manager().list_triplets(namespace) + + # ------------------------------------------------------------------ + # Retrieval helpers + # ------------------------------------------------------------------ + def search( + self, + query: str, + memories: Sequence[Memory] | None = None, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + config: SearchConfig | None = None, + use_llm_rerank: bool = False, + reranker: Callable[[str, Sequence[Memory], int], Sequence[Memory]] | None = None, + ) -> List[Memory]: + from meshmind.retrieval import llm_rerank, search as hybrid_search + from meshmind.retrieval.graph import graph_hybrid_search + + cfg = config or SearchConfig(encoder=self.embedding_model) + if cfg.rerank_model is None: + cfg.rerank_model = self.llm_config.model_for("rerank", fallback=None) + active_reranker = reranker + if use_llm_rerank: + active_reranker = lambda q, c, k: llm_rerank( + q, + c, + self.llm_client, + k, + model=cfg.rerank_model, + endpoint=self.llm_config.base_url_for("rerank"), + ) + + if memories is None: + return graph_hybrid_search( + query, + self._ensure_driver(), + namespace=namespace, + entity_labels=entity_labels, + config=cfg, + reranker=active_reranker, + ) + + return hybrid_search( + query, + list(memories), + namespace=namespace, + entity_labels=list(entity_labels) if entity_labels else None, + config=cfg, + reranker=active_reranker, + ) + + def search_vector( + self, + query: str, + memories: Sequence[Memory] | None = None, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + config: SearchConfig | None = None, + ) -> List[Memory]: + from meshmind.retrieval import search_vector + from meshmind.retrieval.graph import graph_vector_search + + cfg = config or SearchConfig(encoder=self.embedding_model) + if memories is None: + return graph_vector_search( + query, + self._ensure_driver(), + namespace=namespace, + entity_labels=entity_labels, + config=cfg, + ) + return search_vector( + query, + list(memories), + namespace=namespace, + entity_labels=list(entity_labels) if entity_labels else None, + config=cfg, + ) + + def search_regex( + self, + pattern: str, + memories: Sequence[Memory] | None = None, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + flags: int | None = None, + top_k: int = 10, + ) -> List[Memory]: + from meshmind.retrieval import search_regex + from meshmind.retrieval.graph import graph_regex_search + + if memories is None: + return graph_regex_search( + pattern, + self._ensure_driver(), + namespace=namespace, + entity_labels=entity_labels, + flags=flags, + top_k=top_k, + ) + return search_regex( + pattern, + list(memories), + namespace=namespace, + entity_labels=list(entity_labels) if entity_labels else None, + flags=flags, + top_k=top_k, + ) + + def search_exact( + self, + query: str, + memories: Sequence[Memory] | None = None, + namespace: str | None = None, + entity_labels: Sequence[str] | None = None, + fields: Sequence[str] | None = None, + case_sensitive: bool = False, + top_k: int = 10, + ) -> List[Memory]: + from meshmind.retrieval import search_exact + from meshmind.retrieval.graph import graph_exact_search + + if memories is None: + return graph_exact_search( + query, + self._ensure_driver(), + namespace=namespace, + entity_labels=entity_labels, + fields=fields, + case_sensitive=case_sensitive, + top_k=top_k, + ) + return search_exact( + query, + list(memories), + namespace=namespace, + entity_labels=list(entity_labels) if entity_labels else None, + fields=list(fields) if fields else None, + case_sensitive=case_sensitive, + top_k=top_k, + ) + diff --git a/meshmind/core/bootstrap.py b/meshmind/core/bootstrap.py new file mode 100644 index 0000000..1c1b243 --- /dev/null +++ b/meshmind/core/bootstrap.py @@ -0,0 +1,45 @@ +"""Bootstrap helpers for wiring encoders and registries.""" +from __future__ import annotations + +import warnings +from typing import Iterable, Sequence, Type + +from meshmind._compat.pydantic import BaseModel + +from meshmind.core.config import settings +from meshmind.core.embeddings import EncoderRegistry, OpenAIEmbeddingEncoder +from meshmind.core.types import Memory +from meshmind.models.registry import EntityRegistry, PredicateRegistry + + +def bootstrap_encoders(default_models: Sequence[str] | None = None) -> None: + """Ensure a default set of embedding encoders are registered.""" + + models = list(default_models) if default_models else [settings.EMBEDDING_MODEL] + for model_name in models: + if EncoderRegistry.is_registered(model_name): + continue + try: + EncoderRegistry.register(model_name, OpenAIEmbeddingEncoder(model_name)) + except ImportError as exc: + warnings.warn( + f"Skipping registration of OpenAI encoder '{model_name}': {exc}", + RuntimeWarning, + stacklevel=2, + ) + + +def bootstrap_entities(entity_models: Iterable[Type[BaseModel]] | None = None) -> None: + """Register default entity models used throughout the application.""" + + models = list(entity_models) if entity_models else [Memory] + for model in models: + EntityRegistry.register(model) + + +def register_predicates(predicates: Iterable[str]) -> None: + """Register predicate labels in the global predicate registry.""" + + for predicate in predicates: + PredicateRegistry.add(predicate) + diff --git a/meshmind/core/config.py b/meshmind/core/config.py index 9b14ffd..d4611b2 100644 --- a/meshmind/core/config.py +++ b/meshmind/core/config.py @@ -13,20 +13,92 @@ class Settings: """Application settings loaded from environment variables.""" + GRAPH_BACKEND: str = os.getenv("GRAPH_BACKEND", "memory") MEMGRAPH_URI: str = os.getenv("MEMGRAPH_URI", "bolt://localhost:7687") MEMGRAPH_USERNAME: str = os.getenv("MEMGRAPH_USERNAME", "") MEMGRAPH_PASSWORD: str = os.getenv("MEMGRAPH_PASSWORD", "") + NEO4J_URI: str = os.getenv("NEO4J_URI", "bolt://localhost:7687") + NEO4J_USERNAME: str = os.getenv("NEO4J_USERNAME", "neo4j") + NEO4J_PASSWORD: str = os.getenv("NEO4J_PASSWORD", "") + SQLITE_PATH: str = os.getenv("SQLITE_PATH", ":memory:") REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0") OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") - EMBEDDING_MODEL: str = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") + LLM_API_KEY: str = os.getenv("LLM_API_KEY", OPENAI_API_KEY) + LLM_DEFAULT_MODEL: str = os.getenv("LLM_DEFAULT_MODEL", "gpt-5-nano") + LLM_DEFAULT_BASE_URL: str = os.getenv("LLM_DEFAULT_BASE_URL", "") + LLM_EXTRACTION_MODEL: str = os.getenv("LLM_EXTRACTION_MODEL", "") + LLM_EXTRACTION_BASE_URL: str = os.getenv("LLM_EXTRACTION_BASE_URL", "") + _DEFAULT_EMBEDDING = os.getenv("EMBEDDING_MODEL", "text-embedding-3-small") + LLM_EMBEDDING_MODEL: str = os.getenv("LLM_EMBEDDING_MODEL", _DEFAULT_EMBEDDING) + LLM_EMBEDDING_BASE_URL: str = os.getenv("LLM_EMBEDDING_BASE_URL", "") + LLM_RERANK_MODEL: str = os.getenv("LLM_RERANK_MODEL", "") + LLM_RERANK_BASE_URL: str = os.getenv("LLM_RERANK_BASE_URL", "") + EMBEDDING_MODEL: str = LLM_EMBEDDING_MODEL + + REQUIRED_GROUPS = { + "memgraph": ("MEMGRAPH_URI",), + "neo4j": ("NEO4J_URI",), + "openai": ("OPENAI_API_KEY",), + "redis": ("REDIS_URL",), + } def __repr__(self) -> str: return ( - f"Settings(MEMGRAPH_URI={self.MEMGRAPH_URI}, " + f"Settings(GRAPH_BACKEND={self.GRAPH_BACKEND}, " + f"MEMGRAPH_URI={self.MEMGRAPH_URI}, " f"MEMGRAPH_USERNAME={self.MEMGRAPH_USERNAME}, " + f"NEO4J_URI={self.NEO4J_URI}, " f"REDIS_URL={self.REDIS_URL}, " f"EMBEDDING_MODEL={self.EMBEDDING_MODEL})" ) + @staticmethod + def _mask(value: str) -> str: + if not value: + return "" + if len(value) <= 4: + return "*" * len(value) + return f"{value[:2]}***{value[-2:]}" + + def missing(self) -> dict[str, list[str]]: + """Return missing environment variables grouped by capability.""" + + missing: dict[str, list[str]] = {} + for group, keys in self.REQUIRED_GROUPS.items(): + if group == "memgraph" and self.GRAPH_BACKEND != "memgraph": + continue + if group == "neo4j" and self.GRAPH_BACKEND != "neo4j": + continue + absent = [key for key in keys if not getattr(self, key)] + if absent: + missing[group] = absent + return missing + + def summary(self) -> dict[str, str]: + """Return a sanitized summary of active configuration values.""" + + return { + "MEMGRAPH_URI": self.MEMGRAPH_URI, + "MEMGRAPH_USERNAME": self.MEMGRAPH_USERNAME, + "MEMGRAPH_PASSWORD": self._mask(self.MEMGRAPH_PASSWORD), + "NEO4J_URI": self.NEO4J_URI, + "NEO4J_USERNAME": self.NEO4J_USERNAME, + "NEO4J_PASSWORD": self._mask(self.NEO4J_PASSWORD), + "GRAPH_BACKEND": self.GRAPH_BACKEND, + "SQLITE_PATH": self.SQLITE_PATH, + "REDIS_URL": self.REDIS_URL, + "OPENAI_API_KEY": self._mask(self.OPENAI_API_KEY), + "LLM_API_KEY": self._mask(self.LLM_API_KEY), + "LLM_DEFAULT_MODEL": self.LLM_DEFAULT_MODEL, + "LLM_DEFAULT_BASE_URL": self.LLM_DEFAULT_BASE_URL or None, + "LLM_EXTRACTION_MODEL": self.LLM_EXTRACTION_MODEL or None, + "LLM_EXTRACTION_BASE_URL": self.LLM_EXTRACTION_BASE_URL or None, + "LLM_EMBEDDING_MODEL": self.LLM_EMBEDDING_MODEL, + "LLM_EMBEDDING_BASE_URL": self.LLM_EMBEDDING_BASE_URL or None, + "LLM_RERANK_MODEL": self.LLM_RERANK_MODEL or None, + "LLM_RERANK_BASE_URL": self.LLM_RERANK_BASE_URL or None, + "EMBEDDING_MODEL": self.EMBEDDING_MODEL, + } + -settings = Settings() \ No newline at end of file +settings = Settings() diff --git a/meshmind/core/embeddings.py b/meshmind/core/embeddings.py index 84a67e8..a32716f 100644 --- a/meshmind/core/embeddings.py +++ b/meshmind/core/embeddings.py @@ -1,18 +1,15 @@ -""" -Embedding encoders and registry for MeshMind. -""" -from typing import List, Dict, Any +"""Embedding encoder implementations and registry utilities.""" +from __future__ import annotations + import time +from typing import Any, Dict, List -_OPENAI_AVAILABLE = True -try: - from openai import OpenAI - from openai.error import RateLimitError -except ImportError: - _OPENAI_AVAILABLE = False - openai = None # type: ignore - class RateLimitError(Exception): # type: ignore - pass +from meshmind.llm_client import ( + LLMClient, + LLMConfig, + RateLimitError, + build_llm_config_from_settings, +) from .config import settings @@ -26,19 +23,16 @@ def __init__( model_name: str = settings.EMBEDDING_MODEL, max_retries: int = 5, backoff_factor: float = 1.0, + llm_client: LLMClient | None = None, + llm_config: LLMConfig | None = None, ): - if not _OPENAI_AVAILABLE: - raise ImportError( - "openai package is required for OpenAIEmbeddingEncoder" - ) - try: - openai.api_key = settings.OPENAI_API_KEY - except Exception: - pass - - self.llm_client = OpenAI() + config = llm_config or build_llm_config_from_settings(settings) + if model_name: + config = config.override(models={"embedding": model_name}) + resolved_model = config.model_for("embedding", fallback=model_name) + self.llm_client = llm_client or LLMClient(config) self.RateLimitError = RateLimitError - self.model_name = model_name + self.model_name = resolved_model self.max_retries = max_retries self.backoff_factor = backoff_factor @@ -49,14 +43,24 @@ def encode(self, texts: List[str] | str) -> List[List[float]]: """ if isinstance(texts, str): texts = [texts] - + for attempt in range(self.max_retries): try: response = self.llm_client.embeddings.create( + operation="embedding", model=self.model_name, input=texts, ) - return [item['embedding'] for item in response['data']] + data = getattr(response, "data", None) + if data is None: + data = response.get("data", []) # type: ignore[assignment] + embeddings: List[List[float]] = [] + for item in data: + if hasattr(item, "embedding"): + embeddings.append(list(getattr(item, "embedding"))) + else: + embeddings.append(list(item["embedding"])) + return embeddings except self.RateLimitError: time.sleep(self.backoff_factor * (2 ** attempt)) except Exception: @@ -71,7 +75,13 @@ class SentenceTransformerEncoder: Encoder that uses a local SentenceTransformer model. """ def __init__(self, model_name: str): - from sentence_transformers import SentenceTransformer + try: # pragma: no cover - optional dependency + from sentence_transformers import SentenceTransformer + except ImportError as exc: + raise ImportError( + "sentence-transformers is required for SentenceTransformerEncoder." + " Install the optional 'sentence-transformers' extra to enable this encoder." + ) from exc self.model = SentenceTransformer(model_name) @@ -106,4 +116,22 @@ def get(cls, name: str) -> Any: encoder = cls._encoders.get(name) if encoder is None: raise KeyError(f"Encoder '{name}' not found in registry") - return encoder \ No newline at end of file + return encoder + + @classmethod + def is_registered(cls, name: str) -> bool: + """Return True if an encoder ``name`` has been registered.""" + + return name in cls._encoders + + @classmethod + def available(cls) -> List[str]: + """Return the list of registered encoder identifiers.""" + + return list(cls._encoders.keys()) + + @classmethod + def clear(cls) -> None: + """Remove all registered encoders. Intended for testing.""" + + cls._encoders.clear() diff --git a/meshmind/core/observability.py b/meshmind/core/observability.py new file mode 100644 index 0000000..6c36fe1 --- /dev/null +++ b/meshmind/core/observability.py @@ -0,0 +1,80 @@ +"""Shared logging and metrics utilities for MeshMind pipelines.""" +from __future__ import annotations + +import logging +from collections import defaultdict +from contextlib import contextmanager +from time import perf_counter +from typing import Any, Dict, Iterable + +_LOGGER_NAME = "meshmind" +logger = logging.getLogger(_LOGGER_NAME) +if not logger.handlers: # pragma: no cover - avoid duplicate handlers in tests + handler = logging.StreamHandler() + formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) +logger.setLevel(logging.INFO) + + +class Telemetry: + """Lightweight in-memory metrics collector.""" + + def __init__(self) -> None: + self._counters: Dict[str, int] = defaultdict(int) + self._durations: Dict[str, list[float]] = defaultdict(list) + self._gauges: Dict[str, float] = {} + + # ------------------------------------------------------------------ + # Counter helpers + # ------------------------------------------------------------------ + def increment(self, metric: str, value: int = 1) -> None: + self._counters[metric] += value + + def gauge(self, metric: str, value: float) -> None: + self._gauges[metric] = value + + def observe(self, metric: str, value: float) -> None: + self._durations[metric].append(value) + + @contextmanager + def track_duration(self, metric: str): + start = perf_counter() + try: + yield + finally: + elapsed = perf_counter() - start + self.observe(metric, elapsed) + + def extend_counter(self, metric: str, values: Iterable[Any]) -> None: + count = sum(1 for _ in values) + self.increment(metric, count) + + # ------------------------------------------------------------------ + # Snapshot helpers + # ------------------------------------------------------------------ + def snapshot(self) -> Dict[str, Any]: + return { + "counters": dict(self._counters), + "durations": {k: list(v) for k, v in self._durations.items()}, + "gauges": dict(self._gauges), + } + + def reset(self) -> None: + self._counters.clear() + self._durations.clear() + self._gauges.clear() + + +telemetry = Telemetry() + + +def log_event(event: str, **fields: Any) -> None: + """Emit a structured log entry and update a counter for the event.""" + + telemetry.increment(f"events.{event}") + if fields: + formatted = " ".join(f"{key}={value}" for key, value in fields.items()) + logger.info("event=%s %s", event, formatted) + else: + logger.info("event=%s", event) diff --git a/meshmind/core/similarity.py b/meshmind/core/similarity.py index 78fa6c9..7e47130 100644 --- a/meshmind/core/similarity.py +++ b/meshmind/core/similarity.py @@ -1,8 +1,13 @@ -""" -Similarity and distance metrics for MeshMind. -""" +"""Similarity and distance metrics for MeshMind.""" +from __future__ import annotations + +from math import sqrt from typing import Sequence -import numpy as np + +try: # pragma: no cover - optional dependency + import numpy as np +except ImportError: # pragma: no cover - fallback path for test sandboxes + np = None # type: ignore def cosine_similarity(vec1: Sequence[float], vec2: Sequence[float]) -> float: @@ -10,23 +15,38 @@ def cosine_similarity(vec1: Sequence[float], vec2: Sequence[float]) -> float: Compute the cosine similarity between two vectors. Returns 0.0 if either vector has zero magnitude. """ - a = np.array(vec1, dtype=float) - b = np.array(vec2, dtype=float) - if a.shape != b.shape: + if np is not None: + a = np.array(vec1, dtype=float) + b = np.array(vec2, dtype=float) + if a.shape != b.shape: + raise ValueError("Vectors must be the same length") + norm_a = np.linalg.norm(a) + norm_b = np.linalg.norm(b) + if norm_a == 0.0 or norm_b == 0.0: + return 0.0 + return float(np.dot(a, b) / (norm_a * norm_b)) + + if len(vec1) != len(vec2): raise ValueError("Vectors must be the same length") - norm_a = np.linalg.norm(a) - norm_b = np.linalg.norm(b) + dot = sum(float(a) * float(b) for a, b in zip(vec1, vec2)) + norm_a = sqrt(sum(float(a) ** 2 for a in vec1)) + norm_b = sqrt(sum(float(b) ** 2 for b in vec2)) if norm_a == 0.0 or norm_b == 0.0: return 0.0 - return float(np.dot(a, b) / (norm_a * norm_b)) + return float(dot / (norm_a * norm_b)) def euclidean_distance(vec1: Sequence[float], vec2: Sequence[float]) -> float: """ Compute the Euclidean distance between two vectors. """ - a = np.array(vec1, dtype=float) - b = np.array(vec2, dtype=float) - if a.shape != b.shape: + if np is not None: + a = np.array(vec1, dtype=float) + b = np.array(vec2, dtype=float) + if a.shape != b.shape: + raise ValueError("Vectors must be the same length") + return float(np.linalg.norm(a - b)) + + if len(vec1) != len(vec2): raise ValueError("Vectors must be the same length") - return float(np.linalg.norm(a - b)) \ No newline at end of file + return float(sqrt(sum((float(a) - float(b)) ** 2 for a, b in zip(vec1, vec2)))) \ No newline at end of file diff --git a/meshmind/core/types.py b/meshmind/core/types.py index 112d31e..25919c1 100644 --- a/meshmind/core/types.py +++ b/meshmind/core/types.py @@ -1,9 +1,15 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime, timezone from typing import Any, Optional, Tuple from uuid import UUID, uuid4 -from pydantic import BaseModel, Field +from meshmind._compat.pydantic import BaseModel, Field + + +def _utcnow() -> datetime: + """Return the current UTC time with timezone information.""" + + return datetime.now(timezone.utc) class Memory(BaseModel): @@ -17,7 +23,7 @@ class Memory(BaseModel): embedding: Optional[list[float]] = None metadata: dict[str, Any] = Field(default_factory=dict) reference_time: Optional[datetime] = None - created_at: datetime = Field(default_factory=datetime.utcnow) + created_at: datetime = Field(default_factory=_utcnow) updated_at: Optional[datetime] = None importance: Optional[float] = None ttl_seconds: Optional[int] = None @@ -43,5 +49,6 @@ class SearchConfig(BaseModel): encoder: str = "text-embedding-3-small" top_k: int = 20 rerank_k: int = 10 + rerank_model: Optional[str] = None filters: Optional[dict[str, Any]] = None hybrid_weights: Tuple[float, float] = (0.5, 0.5) \ No newline at end of file diff --git a/meshmind/core/utils.py b/meshmind/core/utils.py index 74e90dc..25dfb53 100644 --- a/meshmind/core/utils.py +++ b/meshmind/core/utils.py @@ -1,16 +1,52 @@ -"""Utility functions for MeshMind.""" -import uuid -from datetime import datetime +"""Utility helpers for MeshMind with optional dependency guards.""" +from __future__ import annotations + import hashlib -from typing import Any -import tiktoken +import uuid +from datetime import datetime, timezone +from functools import lru_cache +from typing import Any, Optional + +_TIKTOKEN = None + + +def _ensure_tiktoken() -> Any: + """Return the ``tiktoken`` module if it is installed.""" + + global _TIKTOKEN + if _TIKTOKEN is None: + try: + import tiktoken # type: ignore + except ImportError as exc: # pragma: no cover - exercised in minimal envs + raise RuntimeError( + "tiktoken is required for token counting but is not installed." + " Install the optional 'tiktoken' extra to enable compression features." + ) from exc + _TIKTOKEN = tiktoken + return _TIKTOKEN + + +@lru_cache(maxsize=8) +def get_token_encoder(encoding_name: str = "o200k_base", optional: bool = False) -> Optional[Any]: + """Return a cached tiktoken encoder or ``None`` when optional.""" + + try: + module = _ensure_tiktoken() + except RuntimeError: + if optional: + return None + raise + return module.get_encoding(encoding_name) + + def generate_uuid() -> str: """Generate a UUID4 string.""" return str(uuid.uuid4()) def current_timestamp() -> datetime: """Get current UTC timestamp.""" - return datetime.utcnow() + + return datetime.now(timezone.utc) def hash_string(value: str) -> str: """Hash a string using SHA-256 and return the hex digest.""" @@ -21,13 +57,7 @@ def hash_dict(data: Any) -> str: return hash_string(str(data)) def num_tokens_from_string(string: str, encoding_name: str = "o200k_base") -> int: - """Returns the number of tokens in a text string. - Args: - string: The text string to count tokens for. - encoding_name: The name of the encoding to use. Defaults to "o200k_base". - Returns: - The number of tokens in the text string. - """ - encoding = tiktoken.get_encoding(encoding_name) - num_tokens = len(encoding.encode(string)) - return num_tokens \ No newline at end of file + """Return the number of tokens in ``string`` for ``encoding_name``.""" + + encoder = get_token_encoder(encoding_name, optional=False) + return len(encoder.encode(string)) diff --git a/meshmind/db/__init__.py b/meshmind/db/__init__.py index e69de29..e5c249e 100644 --- a/meshmind/db/__init__.py +++ b/meshmind/db/__init__.py @@ -0,0 +1,14 @@ +"""Graph driver implementations exposed for convenience.""" +from .base_driver import GraphDriver +from .in_memory_driver import InMemoryGraphDriver +from .memgraph_driver import MemgraphDriver +from .neo4j_driver import Neo4jGraphDriver +from .sqlite_driver import SQLiteGraphDriver + +__all__ = [ + "GraphDriver", + "InMemoryGraphDriver", + "MemgraphDriver", + "Neo4jGraphDriver", + "SQLiteGraphDriver", +] diff --git a/meshmind/db/base_driver.py b/meshmind/db/base_driver.py index 1e1366c..5fb6145 100644 --- a/meshmind/db/base_driver.py +++ b/meshmind/db/base_driver.py @@ -1,6 +1,8 @@ """Abstract base class for graph database drivers.""" +from __future__ import annotations + from abc import ABC, abstractmethod -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional, Sequence import uuid @@ -22,7 +24,61 @@ def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: """Execute a Cypher query and return results.""" raise NotImplementedError + @abstractmethod + def get_entity(self, uid: str) -> Optional[Dict[str, Any]]: + """Return a single entity by UUID, if it exists.""" + raise NotImplementedError + + @abstractmethod + def list_entities( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + """Return entities, optionally filtered by namespace and label.""" + raise NotImplementedError + + def search_entities( + self, + query: Optional[str] = None, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + """Server-side memory search. Defaults to ``list_entities`` when unsupported.""" + + return self.list_entities( + namespace=namespace, + entity_labels=entity_labels, + offset=offset, + limit=limit, + ) + @abstractmethod def delete(self, uuid: uuid.UUID) -> None: """Delete a node or relationship by UUID.""" - raise NotImplementedError \ No newline at end of file + raise NotImplementedError + + @abstractmethod + def delete_triplet(self, subj: str, pred: str, obj: str) -> None: + """Delete a relationship identified by subject/predicate/object.""" + + raise NotImplementedError + + @abstractmethod + def list_triplets(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]: + """Return stored triplets, optionally filtered by namespace.""" + + raise NotImplementedError + + def count_entities( + self, namespace: Optional[str] = None + ) -> Dict[str, Dict[str, int]]: + """Return memory counts grouped by namespace and entity label.""" + + raise NotImplementedError diff --git a/meshmind/db/factory.py b/meshmind/db/factory.py new file mode 100644 index 0000000..e7d18cd --- /dev/null +++ b/meshmind/db/factory.py @@ -0,0 +1,66 @@ +"""Factory helpers for constructing :class:`GraphDriver` instances.""" +from __future__ import annotations + +from typing import Callable, Dict, Type + +from meshmind.core.config import settings +from meshmind.db.base_driver import GraphDriver +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.db.memgraph_driver import MemgraphDriver +from meshmind.db.neo4j_driver import Neo4jGraphDriver +from meshmind.db.sqlite_driver import SQLiteGraphDriver + + +def _normalize_backend(name: str) -> str: + return name.replace("-", "_").lower() + + +def available_backends() -> Dict[str, Type[GraphDriver]]: + """Return the mapping of backend names to driver classes.""" + + return { + "memory": InMemoryGraphDriver, + "in_memory": InMemoryGraphDriver, + "inmemory": InMemoryGraphDriver, + "sqlite": SQLiteGraphDriver, + "memgraph": MemgraphDriver, + "neo4j": Neo4jGraphDriver, + } + + +def create_graph_driver(backend: str | None = None, **kwargs) -> GraphDriver: + """Instantiate a :class:`GraphDriver` for the requested backend.""" + + backend_name = _normalize_backend(backend or settings.GRAPH_BACKEND) + drivers = available_backends() + if backend_name not in drivers: + raise ValueError(f"Unsupported graph backend '{backend_name}'") + + driver_cls: Type[GraphDriver] = drivers[backend_name] + if driver_cls is InMemoryGraphDriver: + return driver_cls() + if driver_cls is SQLiteGraphDriver: + path = kwargs.get("path") or settings.SQLITE_PATH + return driver_cls(path) + if driver_cls is MemgraphDriver: + return driver_cls( + settings.MEMGRAPH_URI, + settings.MEMGRAPH_USERNAME, + settings.MEMGRAPH_PASSWORD, + ) + if driver_cls is Neo4jGraphDriver: + return driver_cls( + settings.NEO4J_URI, + settings.NEO4J_USERNAME, + settings.NEO4J_PASSWORD, + ) + return driver_cls(**kwargs) + + +def graph_driver_factory(backend: str | None = None, **kwargs) -> Callable[[], GraphDriver]: + """Return a callable that lazily constructs the configured driver.""" + + def _factory() -> GraphDriver: + return create_graph_driver(backend=backend, **kwargs) + + return _factory diff --git a/meshmind/db/in_memory_driver.py b/meshmind/db/in_memory_driver.py new file mode 100644 index 0000000..191758b --- /dev/null +++ b/meshmind/db/in_memory_driver.py @@ -0,0 +1,188 @@ +"""In-memory implementation of :class:`GraphDriver` for tests and local development.""" +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Sequence, Tuple +from uuid import UUID, uuid4 + +from meshmind.db.base_driver import GraphDriver + + +class InMemoryGraphDriver(GraphDriver): + """A lightweight graph driver that stores entities and triplets in dictionaries.""" + + def __init__(self) -> None: + self._nodes: Dict[str, Dict[str, Any]] = {} + self._labels: Dict[str, str] = {} + self._triplets: Dict[Tuple[str, str, str], Dict[str, Any]] = {} + + # ------------------------------------------------------------------ + # Helpers + # ------------------------------------------------------------------ + def _ensure_uuid(self, props: Dict[str, Any]) -> str: + uid = props.get("uuid") + if isinstance(uid, UUID): + props["uuid"] = str(uid) + return str(uid) + if isinstance(uid, str): + return uid + new_uid = str(uuid4()) + props["uuid"] = new_uid + return new_uid + + # ------------------------------------------------------------------ + # GraphDriver API + # ------------------------------------------------------------------ + def upsert_entity(self, label: str, name: str, props: Dict[str, Any]) -> None: + props = dict(props) + uid = self._ensure_uuid(props) + props.setdefault("name", name) + props.setdefault("entity_label", label) + self._nodes[uid] = props + self._labels[uid] = label + + def upsert_edge(self, subj: str, pred: str, obj: str, props: Dict[str, Any]) -> None: + key = (str(subj), pred, str(obj)) + payload = dict(props) + payload.setdefault("subject", str(subj)) + payload.setdefault("predicate", pred) + payload.setdefault("object", str(obj)) + self._triplets[key] = payload + + def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: + cypher = cypher.lower().strip() + if "where m.uuid" in cypher: + uid = str(params.get("uuid", "")) + node = self._nodes.get(uid) + if node is None: + return [] + return [{"m": dict(node)}] + if "where m.namespace" in cypher: + namespace = params.get("namespace") + results = [ + {"m": dict(node)} + for node in self._nodes.values() + if node.get("namespace") == namespace + ] + return results + if cypher.startswith("match (m) return m"): + return [{"m": dict(node)} for node in self._nodes.values()] + if cypher.startswith("match (a)-[r"): + namespace = params.get("namespace") + results: List[Dict[str, Any]] = [] + for payload in self._triplets.values(): + if namespace and payload.get("namespace") != namespace: + continue + record = { + "subject": payload.get("subject"), + "predicate": payload.get("predicate"), + "object": payload.get("object"), + "namespace": payload.get("namespace"), + "metadata": payload.get("metadata"), + "reference_time": payload.get("reference_time"), + } + results.append(record) + return results + return [] + + def get_entity(self, uid: str) -> Optional[Dict[str, Any]]: + node = self._nodes.get(str(uid)) + return dict(node) if node else None + + def _filtered_nodes( + self, + namespace: Optional[str], + entity_labels: Optional[Sequence[str]], + ) -> List[Dict[str, Any]]: + labels = set(entity_labels or []) + results: List[Dict[str, Any]] = [] + for node in self._nodes.values(): + if namespace is not None and node.get("namespace") != namespace: + continue + label = node.get("entity_label") or self._labels.get(str(node.get("uuid"))) + if labels and label not in labels: + continue + results.append(dict(node)) + return results + + def _slice(self, items: List[Dict[str, Any]], offset: int, limit: Optional[int]) -> List[Dict[str, Any]]: + offset = max(offset, 0) + if limit is None: + return items[offset:] + if limit <= 0: + return [] + return items[offset : offset + limit] + + def list_entities( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + nodes = self._filtered_nodes(namespace, entity_labels) + return self._slice(nodes, offset, limit) + + def search_entities( + self, + query: Optional[str] = None, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + nodes = self._filtered_nodes(namespace, entity_labels) + if query: + needle = query.lower() + filtered: List[Dict[str, Any]] = [] + for node in nodes: + haystack = " ".join( + str(node.get(key, "")) for key in ("name", "content", "description") + ).lower() + metadata = node.get("metadata") or {} + if isinstance(metadata, dict): + haystack += " " + " ".join(str(value).lower() for value in metadata.values()) + if needle in haystack: + filtered.append(node) + nodes = filtered + return self._slice(nodes, offset, limit) + + def delete(self, uuid: UUID) -> None: + uid = str(uuid) + self._nodes.pop(uid, None) + self._labels.pop(uid, None) + to_delete = [key for key in self._triplets if key[0] == uid or key[2] == uid] + for key in to_delete: + self._triplets.pop(key, None) + + def delete_triplet(self, subj: str, pred: str, obj: str) -> None: + self._triplets.pop((str(subj), pred, str(obj)), None) + + def list_triplets(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]: + results: List[Dict[str, Any]] = [] + for payload in self._triplets.values(): + if namespace and payload.get("namespace") != namespace: + continue + results.append( + { + "subject": payload.get("subject"), + "predicate": payload.get("predicate"), + "object": payload.get("object"), + "namespace": payload.get("namespace"), + "metadata": payload.get("metadata"), + "reference_time": payload.get("reference_time"), + } + ) + return results + + def count_entities(self, namespace: Optional[str] = None) -> Dict[str, Dict[str, int]]: + counts: Dict[str, Dict[str, int]] = {} + for node in self._nodes.values(): + ns = node.get("namespace") + if namespace is not None and ns != namespace: + continue + label = node.get("entity_label") or self._labels.get(str(node.get("uuid"))) or "Unknown" + bucket = counts.setdefault(ns or "default", {}) + bucket[label] = bucket.get(label, 0) + 1 + return counts diff --git a/meshmind/db/memgraph_driver.py b/meshmind/db/memgraph_driver.py index f4e8c4e..a8d79b0 100644 --- a/meshmind/db/memgraph_driver.py +++ b/meshmind/db/memgraph_driver.py @@ -1,110 +1,255 @@ -"""Memgraph implementation of GraphDriver.""" -from typing import Any, Dict, List -from .base_driver import GraphDriver +"""Memgraph implementation of :class:`GraphDriver` using ``mgclient``.""" +from __future__ import annotations - -"""Memgraph implementation of GraphDriver using mgclient.""" -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Sequence from urllib.parse import urlparse -try: +from meshmind.db.base_driver import GraphDriver + +try: # pragma: no cover - optional dependency import mgclient -except ImportError: +except ImportError: # pragma: no cover - optional dependency mgclient = None # type: ignore -from .base_driver import GraphDriver - class MemgraphDriver(GraphDriver): - """Memgraph driver implementation of GraphDriver using mgclient.""" + """Memgraph driver implementation backed by ``mgclient``.""" - def __init__(self, uri: str, username: str = None, password: str = None) -> None: - """Initialize Memgraph driver with Bolt URI and credentials.""" + def __init__(self, uri: str, username: str = "", password: str = "") -> None: if mgclient is None: raise ImportError("mgclient is required for MemgraphDriver") + self.uri = uri self.username = username self.password = password - # Parse URI: bolt://host:port + parsed = urlparse(uri) - host = parsed.hostname or 'localhost' + host = parsed.hostname or "localhost" port = parsed.port or 7687 - # Establish connection - self._conn = mgclient.connect( + + self._conn = mgclient.connect( # type: ignore[union-attr] host=host, port=port, - username=username, - password=password, + username=username or None, + password=password or None, ) self._cursor = self._conn.cursor() - def _execute(self, cypher: str, params: Optional[Dict[str, Any]] = None): - if params is None: - params = {} + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + def _execute(self, cypher: str, params: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]: + params = params or {} self._cursor.execute(cypher, params) try: rows = self._cursor.fetchall() cols = [col[0] for col in self._cursor.description] - results: List[Dict[str, Any]] = [] - for row in rows: - rec: Dict[str, Any] = {} - for idx, val in enumerate(row): - rec[cols[idx]] = val - results.append(rec) - return results except Exception: return [] + results: List[Dict[str, Any]] = [] + for row in rows: + record: Dict[str, Any] = {} + for idx, value in enumerate(row): + record[cols[idx]] = value + results.append(record) + return results + + @staticmethod + def _sanitize_predicate(predicate: str) -> str: + return predicate.replace("`", "") + + @staticmethod + def _normalize_node(node: Any) -> Dict[str, Any]: + if hasattr(node, "properties"): + try: + return dict(node.properties) # type: ignore[attr-defined] + except Exception: + pass + if isinstance(node, dict): + return dict(node) + if hasattr(node, "_properties"): + return dict(getattr(node, "_properties")) + return {k: v for k, v in getattr(node, "__dict__", {}).items() if not k.startswith("_")} + + # ------------------------------------------------------------------ + # GraphDriver API + # ------------------------------------------------------------------ def upsert_entity(self, label: str, name: str, props: Dict[str, Any]) -> None: - """Insert or update an entity node by uuid.""" - uid = props.get('uuid') + uid = props.get("uuid") cypher = ( f"MERGE (n:{label} {{uuid: $uuid}})\n" f"SET n += $props" ) - params = {'uuid': str(uid), 'props': props} + params = {"uuid": str(uid), "props": props} self._execute(cypher, params) self._conn.commit() def upsert_edge(self, subj: str, pred: str, obj: str, props: Dict[str, Any]) -> None: - """Insert or update an edge between two entities identified by uuid.""" + predicate = self._sanitize_predicate(pred) cypher = ( - f"MATCH (a {{uuid: $subj}}), (b {{uuid: $obj}})\n" - f"MERGE (a)-[r:`{pred}`]->(b)\n" - f"SET r += $props" + "MATCH (a {uuid: $subj}), (b {uuid: $obj})\n" + f"MERGE (a)-[r:`{predicate}`]->(b)\n" + "SET r += $props" ) - params = {'subj': str(subj), 'obj': str(obj), 'props': props} + params = {"subj": str(subj), "obj": str(obj), "props": props} self._execute(cypher, params) self._conn.commit() def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: - """Execute a Cypher query and return results as list of dicts.""" return self._execute(cypher, params) + def get_entity(self, uid: str) -> Optional[Dict[str, Any]]: + records = self.find( + "MATCH (m) WHERE m.uuid = $uuid RETURN m", + {"uuid": str(uid)}, + ) + if not records: + return None + node = records[0].get("m", records[0]) + return self._normalize_node(node) + + def list_entities( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + if limit is not None and limit <= 0: + return [] + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + clauses.append("($labels IS NULL OR m.entity_label IN $labels)") + cypher = ["MATCH (m)"] + cypher.append("WHERE " + " AND ".join(clauses)) + cypher.append("RETURN m") + cypher.append("ORDER BY m.namespace, m.entity_label, m.name") + params = { + "namespace": namespace, + "labels": list(entity_labels) if entity_labels else None, + "offset": max(offset, 0), + } + cypher.append("SKIP $offset") + if limit is not None: + cypher.append("LIMIT $limit") + params["limit"] = limit + records = self.find("\n".join(cypher), params) + entities: List[Dict[str, Any]] = [] + for record in records: + node = record.get("m", record) + entities.append(self._normalize_node(node)) + return entities + + def search_entities( + self, + query: Optional[str] = None, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + if limit is not None and limit <= 0: + return [] + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + clauses.append("($labels IS NULL OR m.entity_label IN $labels)") + params = { + "namespace": namespace, + "labels": list(entity_labels) if entity_labels else None, + "offset": max(offset, 0), + "search": query.lower() if query else None, + } + text_clause = ( + "($search IS NULL OR " + "toLower(coalesce(m.name, '')) CONTAINS $search OR " + "toLower(coalesce(m.content, '')) CONTAINS $search OR " + "toLower(coalesce(m.description, '')) CONTAINS $search OR " + "($search IS NOT NULL AND exists(m.metadata) AND " + "any(value IN values(m.metadata) WHERE toLower(toString(value)) CONTAINS $search))" + ")" + ) + clauses.append(text_clause) + cypher = ["MATCH (m)"] + cypher.append("WHERE " + " AND ".join(clauses)) + cypher.append("RETURN m") + cypher.append("ORDER BY m.reference_time DESC, m.name") + cypher.append("SKIP $offset") + if limit is not None: + cypher.append("LIMIT $limit") + params["limit"] = limit + records = self.find("\n".join(cypher), params) + entities: List[Dict[str, Any]] = [] + for record in records: + node = record.get("m", record) + entities.append(self._normalize_node(node)) + return entities + def delete(self, uuid: Any) -> None: - """Delete a node (and detach relationships) by uuid.""" cypher = "MATCH (n {uuid: $uuid}) DETACH DELETE n" - params = {'uuid': str(uuid)} + params = {"uuid": str(uuid)} + self._execute(cypher, params) + self._conn.commit() + + def delete_triplet(self, subj: str, pred: str, obj: str) -> None: + predicate = self._sanitize_predicate(pred) + cypher = ( + "MATCH (a {uuid: $subj})-[r:`{predicate}`]->(b {uuid: $obj}) " + "DELETE r" + ) + params = {"subj": str(subj), "obj": str(obj)} self._execute(cypher, params) self._conn.commit() + def list_triplets(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]: + cypher = ( + "MATCH (a)-[r]->(b)\n" + "WHERE $namespace IS NULL OR r.namespace = $namespace\n" + "RETURN a.uuid AS subject, type(r) AS predicate, b.uuid AS object, " + "r.namespace AS namespace, r.metadata AS metadata, r.reference_time AS reference_time" + ) + params = {"namespace": namespace} + return self._execute(cypher, params) + + def count_entities(self, namespace: Optional[str] = None) -> Dict[str, Dict[str, int]]: + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + cypher = ( + "MATCH (m)\n" + "WHERE " + + " AND ".join(clauses) + + "\nRETURN coalesce(m.namespace, '') AS namespace, " + "m.entity_label AS label, count(m) AS count" + ) + params = {"namespace": namespace} + rows = self.find(cypher, params) + results: Dict[str, Dict[str, int]] = {} + for row in rows: + ns = row.get("namespace") or "default" + label = row.get("label") or "Unknown" + count = int(row.get("count", 0)) + bucket = results.setdefault(ns, {}) + bucket[label] = count + return results + + # ------------------------------------------------------------------ + # Convenience helpers + # ------------------------------------------------------------------ def vector_search(self, embedding: List[float], top_k: int = 10) -> List[Dict[str, Any]]: - """ - Fallback vector search: loads all embeddings and ranks by cosine similarity. - """ from meshmind.core.similarity import cosine_similarity - # Load all entities with embeddings - records = self.find("MATCH (n) WHERE exists(n.embedding) RETURN n.embedding AS emb, n AS node", {}) + + records = self.find( + "MATCH (n) WHERE exists(n.embedding) RETURN n.embedding AS emb, n AS node", + {}, + ) scored = [] for rec in records: - emb = rec.get('emb') + emb = rec.get("emb") if not isinstance(emb, list): continue try: score = cosine_similarity(embedding, emb) except Exception: score = 0.0 - scored.append({'node': rec.get('node'), 'score': float(score)}) - # Sort and take top_k - scored.sort(key=lambda x: x['score'], reverse=True) - return scored[:top_k] \ No newline at end of file + scored.append({"node": rec.get("node"), "score": float(score)}) + scored.sort(key=lambda item: item["score"], reverse=True) + return scored[:top_k] diff --git a/meshmind/db/neo4j_driver.py b/meshmind/db/neo4j_driver.py new file mode 100644 index 0000000..cfd2214 --- /dev/null +++ b/meshmind/db/neo4j_driver.py @@ -0,0 +1,200 @@ +"""Neo4j implementation of :class:`GraphDriver` using the official driver.""" +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Sequence + +from meshmind.db.base_driver import GraphDriver + +try: # pragma: no cover - optional dependency + from neo4j import GraphDatabase # type: ignore +except ImportError: # pragma: no cover - optional dependency + GraphDatabase = None # type: ignore + + +class Neo4jGraphDriver(GraphDriver): + """GraphDriver backed by Neo4j via the ``neo4j`` Python driver.""" + + def __init__(self, uri: str, username: str = "neo4j", password: str = "") -> None: + if GraphDatabase is None: + raise ImportError("neo4j driver is required for Neo4jGraphDriver") + auth = None + if username or password: + auth = (username or None, password or None) + self._driver = GraphDatabase.driver(uri, auth=auth) + + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + def _run(self, cypher: str, params: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]: + params = params or {} + with self._driver.session() as session: # type: ignore[attr-defined] + result = session.run(cypher, **params) + records = [] + for record in result: + records.append(record.data()) + return records + + @staticmethod + def _normalize_node(node: Any) -> Dict[str, Any]: + if hasattr(node, "_properties"): + return dict(node._properties) # type: ignore[attr-defined] + if isinstance(node, dict): + return dict(node) + return {k: v for k, v in getattr(node, "__dict__", {}).items() if not k.startswith("_")} + + # ------------------------------------------------------------------ + # GraphDriver API + # ------------------------------------------------------------------ + def upsert_entity(self, label: str, name: str, props: Dict[str, Any]) -> None: + cypher = ( + f"MERGE (n:{label} {{uuid: $uuid}})\n" + "SET n += $props" + ) + params = {"uuid": str(props.get("uuid")), "props": props} + self._run(cypher, params) + + def upsert_edge(self, subj: str, pred: str, obj: str, props: Dict[str, Any]) -> None: + predicate = pred.replace("`", "") + cypher = ( + "MATCH (a {uuid: $subj}), (b {uuid: $obj})\n" + f"MERGE (a)-[r:`{predicate}`]->(b)\n" + "SET r += $props" + ) + params = {"subj": str(subj), "obj": str(obj), "props": props} + self._run(cypher, params) + + def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: + return self._run(cypher, params) + + def get_entity(self, uid: str) -> Optional[Dict[str, Any]]: + records = self.find("MATCH (m) WHERE m.uuid = $uuid RETURN m", {"uuid": str(uid)}) + if not records: + return None + node = records[0].get("m", records[0]) + return self._normalize_node(node) + + def list_entities( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + if limit is not None and limit <= 0: + return [] + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + clauses.append("($labels IS NULL OR m.entity_label IN $labels)") + cypher = ["MATCH (m)"] + cypher.append("WHERE " + " AND ".join(clauses)) + cypher.append("RETURN m") + cypher.append("ORDER BY m.namespace, m.entity_label, m.name") + cypher.append("SKIP $offset") + if limit is not None: + cypher.append("LIMIT $limit") + params = { + "namespace": namespace, + "labels": list(entity_labels) if entity_labels else None, + "offset": max(offset, 0), + } + if limit is not None: + params["limit"] = limit + records = self.find("\n".join(cypher), params) + return [self._normalize_node(rec.get("m", rec)) for rec in records] + + def search_entities( + self, + query: Optional[str] = None, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + if limit is not None and limit <= 0: + return [] + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + clauses.append("($labels IS NULL OR m.entity_label IN $labels)") + params = { + "namespace": namespace, + "labels": list(entity_labels) if entity_labels else None, + "offset": max(offset, 0), + "search": query.lower() if query else None, + } + text_clause = ( + "($search IS NULL OR " + "toLower(coalesce(m.name, '')) CONTAINS $search OR " + "toLower(coalesce(m.content, '')) CONTAINS $search OR " + "toLower(coalesce(m.description, '')) CONTAINS $search OR " + "($search IS NOT NULL AND exists(m.metadata) AND " + "any(value IN values(m.metadata) WHERE toLower(toString(value)) CONTAINS $search))" + ")" + ) + clauses.append(text_clause) + cypher = ["MATCH (m)"] + cypher.append("WHERE " + " AND ".join(clauses)) + cypher.append("RETURN m") + cypher.append("ORDER BY m.reference_time DESC, m.name") + cypher.append("SKIP $offset") + if limit is not None: + cypher.append("LIMIT $limit") + params["limit"] = limit + records = self.find("\n".join(cypher), params) + return [self._normalize_node(rec.get("m", rec)) for rec in records] + + def delete(self, uuid: Any) -> None: + self._run("MATCH (m {uuid: $uuid}) DETACH DELETE m", {"uuid": str(uuid)}) + + def delete_triplet(self, subj: str, pred: str, obj: str) -> None: + predicate = pred.replace("`", "") + cypher = ( + f"MATCH (a {{uuid: $subj}})-[r:`{predicate}`]->(b {{uuid: $obj}})" + " DELETE r" + ) + self._run(cypher, {"subj": str(subj), "obj": str(obj)}) + + def list_triplets(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]: + cypher = ( + "MATCH (a)-[r]->(b)\n" + "WHERE $namespace IS NULL OR r.namespace = $namespace\n" + "RETURN a.uuid AS subject, type(r) AS predicate, b.uuid AS object, " + "r.namespace AS namespace, r.metadata AS metadata, r.reference_time AS reference_time" + ) + params = {"namespace": namespace} + return self.find(cypher, params) + + def close(self) -> None: + self._driver.close() + + def count_entities(self, namespace: Optional[str] = None) -> Dict[str, Dict[str, int]]: + clauses = ["($namespace IS NULL OR m.namespace = $namespace)"] + cypher = ( + "MATCH (m)\n" + "WHERE " + + " AND ".join(clauses) + + "\nRETURN coalesce(m.namespace, '') AS namespace, " + "m.entity_label AS label, count(m) AS count" + ) + params = {"namespace": namespace} + rows = self.find(cypher, params) + results: Dict[str, Dict[str, int]] = {} + for row in rows: + ns = row.get("namespace") or "default" + label = row.get("label") or "Unknown" + count = int(row.get("count", 0)) + bucket = results.setdefault(ns, {}) + bucket[label] = count + return results + + def verify_connectivity(self) -> bool: + """Use the Neo4j driver to verify connectivity.""" + + checker = getattr(self._driver, "verify_connectivity", None) + if callable(checker): + checker() + return True + try: + self._run("RETURN 1 AS ok") + except Exception: + return False + return True diff --git a/meshmind/db/sqlite_driver.py b/meshmind/db/sqlite_driver.py new file mode 100644 index 0000000..24bb468 --- /dev/null +++ b/meshmind/db/sqlite_driver.py @@ -0,0 +1,308 @@ +"""SQLite implementation of :class:`GraphDriver` for lightweight persistence.""" +from __future__ import annotations + +import json +import sqlite3 +from pathlib import Path +from typing import Any, Dict, List, Optional, Sequence + +from meshmind.db.base_driver import GraphDriver + + +class SQLiteGraphDriver(GraphDriver): + """GraphDriver backed by SQLite tables using simple JSON columns.""" + + def __init__(self, path: str | Path = ":memory:") -> None: + self._path = str(path) + self._conn = sqlite3.connect(self._path) + self._conn.row_factory = sqlite3.Row + self._ensure_schema() + + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + def _ensure_schema(self) -> None: + cur = self._conn.cursor() + cur.execute( + """ + CREATE TABLE IF NOT EXISTS entities ( + uuid TEXT PRIMARY KEY, + label TEXT NOT NULL, + name TEXT NOT NULL, + namespace TEXT, + props TEXT NOT NULL + ) + """ + ) + cur.execute( + """ + CREATE TABLE IF NOT EXISTS triplets ( + subject TEXT NOT NULL, + predicate TEXT NOT NULL, + object TEXT NOT NULL, + namespace TEXT, + metadata TEXT, + reference_time TEXT, + PRIMARY KEY (subject, predicate, object) + ) + """ + ) + self._conn.commit() + + def _row_to_dict(self, row: sqlite3.Row) -> Dict[str, Any]: + payload = dict(row) + if "props" in payload and payload["props"]: + props = payload.pop("props") + if isinstance(props, str): + payload.update(json.loads(props)) + elif isinstance(props, dict): + payload.update(props) + if "metadata" in payload and isinstance(payload["metadata"], str): + payload["metadata"] = json.loads(payload["metadata"]) + return payload + + # ------------------------------------------------------------------ + # GraphDriver API + # ------------------------------------------------------------------ + def upsert_entity(self, label: str, name: str, props: Dict[str, Any]) -> None: + payload = dict(props) + uid = str(payload.get("uuid")) + if not uid: + raise ValueError("Memory props must include a UUID for SQLiteGraphDriver") + payload.setdefault("entity_label", label) + payload.setdefault("name", name) + namespace = payload.get("namespace") + cur = self._conn.cursor() + cur.execute( + """ + INSERT INTO entities (uuid, label, name, namespace, props) + VALUES (:uuid, :label, :name, :namespace, :props) + ON CONFLICT(uuid) DO UPDATE SET + label=excluded.label, + name=excluded.name, + namespace=excluded.namespace, + props=excluded.props + """, + { + "uuid": uid, + "label": payload.get("entity_label", label), + "name": payload.get("name", name), + "namespace": namespace, + "props": json.dumps(payload), + }, + ) + self._conn.commit() + + def upsert_edge(self, subj: str, pred: str, obj: str, props: Dict[str, Any]) -> None: + payload = dict(props) + payload.setdefault("subject", subj) + payload.setdefault("predicate", pred) + payload.setdefault("object", obj) + metadata = payload.get("metadata") or {} + cur = self._conn.cursor() + cur.execute( + """ + INSERT INTO triplets (subject, predicate, object, namespace, metadata, reference_time) + VALUES (:subject, :predicate, :object, :namespace, :metadata, :reference_time) + ON CONFLICT(subject, predicate, object) DO UPDATE SET + namespace=excluded.namespace, + metadata=excluded.metadata, + reference_time=excluded.reference_time + """, + { + "subject": payload["subject"], + "predicate": payload["predicate"], + "object": payload["object"], + "namespace": payload.get("namespace"), + "metadata": json.dumps(metadata), + "reference_time": payload.get("reference_time"), + }, + ) + self._conn.commit() + + def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: + # Provide compatibility for simple MATCH queries used by MemoryManager. + cypher_lower = cypher.lower().strip() + cur = self._conn.cursor() + if "where m.uuid" in cypher_lower: + cur.execute("SELECT * FROM entities WHERE uuid = :uuid", {"uuid": params.get("uuid")}) + row = cur.fetchone() + if not row: + return [] + return [{"m": self._row_to_dict(row)}] + if "where m.namespace" in cypher_lower: + cur.execute( + "SELECT * FROM entities WHERE namespace = :namespace", + {"namespace": params.get("namespace")}, + ) + rows = cur.fetchall() + return [{"m": self._row_to_dict(row)} for row in rows] + if cypher_lower.startswith("match (m) return m"): + cur.execute("SELECT * FROM entities") + rows = cur.fetchall() + return [{"m": self._row_to_dict(row)} for row in rows] + if cypher_lower.startswith("match (a)-[r"): + namespace = params.get("namespace") + if namespace: + cur.execute( + "SELECT * FROM triplets WHERE namespace = :namespace", + {"namespace": namespace}, + ) + else: + cur.execute("SELECT * FROM triplets") + rows = cur.fetchall() + return [dict(row) for row in rows] + return [] + + def get_entity(self, uid: str) -> Optional[Dict[str, Any]]: + cur = self._conn.cursor() + cur.execute("SELECT * FROM entities WHERE uuid = :uuid", {"uuid": uid}) + row = cur.fetchone() + return self._row_to_dict(row) if row else None + + def list_entities( + self, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + cur = self._conn.cursor() + clauses: List[str] = [] + params: Dict[str, Any] = {} + if namespace: + clauses.append("namespace = :namespace") + params["namespace"] = namespace + if entity_labels: + placeholders = [] + for idx, label in enumerate(entity_labels): + key = f"label_{idx}" + placeholders.append(f":{key}") + params[key] = label + clauses.append(f"label IN ({', '.join(placeholders)})") + query = "SELECT * FROM entities" + if clauses: + query += " WHERE " + " AND ".join(clauses) + query += " ORDER BY namespace, label, name" + if limit is not None: + if limit <= 0: + return [] + query += " LIMIT :limit" + params["limit"] = limit + if offset > 0: + query += " OFFSET :offset" + params["offset"] = offset + elif offset > 0: + query += " LIMIT -1 OFFSET :offset" + params["offset"] = offset + cur.execute(query, params) + rows = cur.fetchall() + return [self._row_to_dict(row) for row in rows] + + def search_entities( + self, + query: Optional[str] = None, + namespace: Optional[str] = None, + entity_labels: Optional[Sequence[str]] = None, + *, + offset: int = 0, + limit: Optional[int] = None, + ) -> List[Dict[str, Any]]: + cur = self._conn.cursor() + clauses: List[str] = [] + params: Dict[str, Any] = {} + if namespace: + clauses.append("namespace = :namespace") + params["namespace"] = namespace + if entity_labels: + placeholders = [] + for idx, label in enumerate(entity_labels): + key = f"label_{idx}" + placeholders.append(f":{key}") + params[key] = label + clauses.append(f"label IN ({', '.join(placeholders)})") + if query: + params["needle"] = f"%{query.lower()}%" + clauses.append( + "(LOWER(name) LIKE :needle OR LOWER(props) LIKE :needle)" + ) + sql = "SELECT * FROM entities" + if clauses: + sql += " WHERE " + " AND ".join(clauses) + sql += " ORDER BY namespace, label, name" + if limit is not None: + if limit <= 0: + return [] + sql += " LIMIT :limit" + params["limit"] = limit + if offset > 0: + sql += " OFFSET :offset" + params["offset"] = offset + elif offset > 0: + sql += " LIMIT -1 OFFSET :offset" + params["offset"] = offset + cur.execute(sql, params) + rows = cur.fetchall() + return [self._row_to_dict(row) for row in rows] + + def delete(self, uuid: Any) -> None: + cur = self._conn.cursor() + cur.execute("DELETE FROM entities WHERE uuid = :uuid", {"uuid": str(uuid)}) + cur.execute( + "DELETE FROM triplets WHERE subject = :uuid OR object = :uuid", + {"uuid": str(uuid)}, + ) + self._conn.commit() + + def delete_triplet(self, subj: str, pred: str, obj: str) -> None: + cur = self._conn.cursor() + cur.execute( + "DELETE FROM triplets WHERE subject = :subject AND predicate = :predicate AND object = :object", + {"subject": str(subj), "predicate": pred, "object": str(obj)}, + ) + self._conn.commit() + + def list_triplets(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]: + cur = self._conn.cursor() + if namespace: + cur.execute( + "SELECT * FROM triplets WHERE namespace = :namespace", + {"namespace": namespace}, + ) + else: + cur.execute("SELECT * FROM triplets") + rows = cur.fetchall() + result = [] + for row in rows: + payload = dict(row) + metadata = payload.get("metadata") + payload["metadata"] = json.loads(metadata) if metadata else {} + result.append(payload) + return result + + def count_entities(self, namespace: Optional[str] = None) -> Dict[str, Dict[str, int]]: + cur = self._conn.cursor() + params: Dict[str, Any] = {} + sql = ( + "SELECT COALESCE(namespace, '') AS namespace, label, COUNT(*) AS count " + "FROM entities" + ) + if namespace: + sql += " WHERE namespace = :namespace" + params["namespace"] = namespace + sql += " GROUP BY namespace, label" + cur.execute(sql, params) + rows = cur.fetchall() + results: Dict[str, Dict[str, int]] = {} + for row in rows: + payload = dict(row) + ns = payload.get("namespace") or "default" + label = payload.get("label") or "Unknown" + count = int(payload.get("count", 0)) + bucket = results.setdefault(ns, {}) + bucket[label] = count + return results + + def close(self) -> None: + self._conn.close() diff --git a/meshmind/llm_client.py b/meshmind/llm_client.py new file mode 100644 index 0000000..7208f02 --- /dev/null +++ b/meshmind/llm_client.py @@ -0,0 +1,259 @@ +"""Provider-agnostic wrapper around OpenAI-compatible SDKs.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, Dict, Optional + +DEFAULT_MODEL = "gpt-5-nano" + +try: # pragma: no cover - optional dependency import + from openai import OpenAI as _OpenAI + from openai import RateLimitError as _RateLimitError +except ImportError: # pragma: no cover - exercised in tests without openai + _OpenAI = None # type: ignore[assignment] + + class _RateLimitError(Exception): + """Fallback error used when the OpenAI SDK is unavailable.""" + + pass + + +RateLimitError = _RateLimitError + + +@dataclass(frozen=True) +class LLMConfig: + """Configuration for an OpenAI-compatible client.""" + + api_key: Optional[str] = None + base_urls: Dict[str, Optional[str]] = field( + default_factory=lambda: {"default": None} + ) + models: Dict[str, str] = field(default_factory=lambda: {"default": DEFAULT_MODEL}) + default_model: str = DEFAULT_MODEL + + def model_for(self, operation: str, fallback: Optional[str] = None) -> str: + """Return the preferred model for a given operation.""" + + return ( + self.models.get(operation) + or self.models.get("default") + or fallback + or self.default_model + ) + + def base_url_for(self, operation: str) -> Optional[str]: + """Return the preferred base URL for a given operation.""" + + base_url = self.base_urls.get(operation) + if base_url: + return base_url + return self.base_urls.get("default") or None + + def override( + self, + *, + models: Optional[Dict[str, Optional[str]]] = None, + base_urls: Optional[Dict[str, Optional[str]]] = None, + api_key: Optional[str] = None, + ) -> "LLMConfig": + """Return a copy of the config with overrides applied.""" + + new_models = dict(self.models) + if models: + for key, value in models.items(): + if value: + new_models[key] = value + new_base_urls = dict(self.base_urls) + if base_urls: + for key, value in base_urls.items(): + if value is not None: + new_base_urls[key] = value or None + new_api_key = api_key if api_key not in (None, "") else self.api_key + return LLMConfig( + api_key=new_api_key, + base_urls=new_base_urls, + models=new_models, + default_model=self.default_model, + ) + + +class _ResponsesProxy: + def __init__(self, parent: "LLMClient") -> None: + self._parent = parent + + def create( + self, + *, + operation: Optional[str] = None, + model: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs: Any, + ) -> Any: + return self._parent.responses_create( + operation=operation or "extraction", + model=model, + base_url=base_url, + **kwargs, + ) + + +class _EmbeddingsProxy: + def __init__(self, parent: "LLMClient") -> None: + self._parent = parent + + def create( + self, + *, + operation: Optional[str] = None, + model: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs: Any, + ) -> Any: + return self._parent.embeddings_create( + operation=operation or "embedding", + model=model, + base_url=base_url, + **kwargs, + ) + + +class LLMClient: + """Thin wrapper around the OpenAI SDK that centralises configuration.""" + + def __init__( + self, + config: LLMConfig, + *, + client_kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + if _OpenAI is None: + raise ImportError( + "openai package is required to construct an LLM client." + ) + + self.config = config + base_kwargs = dict(client_kwargs or {}) + if config.api_key: + base_kwargs.setdefault("api_key", config.api_key) + default_base = config.base_url_for("default") + if default_base: + base_kwargs.setdefault("base_url", default_base) + self._default_kwargs = base_kwargs + self._client_cache: Dict[Optional[str], Any] = {} + + self.responses = _ResponsesProxy(self) + self.embeddings = _EmbeddingsProxy(self) + + def _client_for(self, base_url: Optional[str]) -> Any: + key = base_url or self._default_kwargs.get("base_url") or "__default__" + if key not in self._client_cache: + kwargs = dict(self._default_kwargs) + if base_url: + kwargs["base_url"] = base_url + self._client_cache[key] = _OpenAI(**kwargs) + return self._client_cache[key] + + def responses_create( + self, + *, + operation: str = "extraction", + model: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs: Any, + ) -> Any: + resolved_model = model or self.config.model_for(operation) + resolved_base = base_url if base_url not in ("", None) else None + if resolved_base is None: + resolved_base = self.config.base_url_for(operation) + client = self._client_for(resolved_base) + kwargs.setdefault("model", resolved_model) + return client.responses.create(**kwargs) + + def embeddings_create( + self, + *, + operation: str = "embedding", + model: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs: Any, + ) -> Any: + resolved_model = model or self.config.model_for(operation) + resolved_base = base_url if base_url not in ("", None) else None + if resolved_base is None: + resolved_base = self.config.base_url_for(operation) + client = self._client_for(resolved_base) + kwargs.setdefault("model", resolved_model) + return client.embeddings.create(**kwargs) + + def with_overrides( + self, + *, + models: Optional[Dict[str, Optional[str]]] = None, + base_urls: Optional[Dict[str, Optional[str]]] = None, + api_key: Optional[str] = None, + ) -> "LLMClient": + """Create a new client that applies the provided overrides.""" + + return LLMClient( + self.config.override( + models=models, base_urls=base_urls, api_key=api_key + ), + client_kwargs=self._default_kwargs, + ) + + +def build_llm_config_from_settings(settings: Any) -> LLMConfig: + """Construct an :class:`LLMConfig` instance from settings.""" + + base_urls = { + "default": settings.LLM_DEFAULT_BASE_URL or None, + "extraction": settings.LLM_EXTRACTION_BASE_URL or None, + "embedding": settings.LLM_EMBEDDING_BASE_URL or None, + "rerank": settings.LLM_RERANK_BASE_URL or None, + } + models = { + "default": settings.LLM_DEFAULT_MODEL or DEFAULT_MODEL, + "extraction": settings.LLM_EXTRACTION_MODEL or None, + "embedding": settings.LLM_EMBEDDING_MODEL, + "rerank": settings.LLM_RERANK_MODEL or None, + } + cleaned_base_urls = { + key: value for key, value in base_urls.items() if value is not None + } + cleaned_models = { + key: value for key, value in models.items() if value + } + api_key = settings.LLM_API_KEY or settings.OPENAI_API_KEY or None + return LLMConfig( + api_key=api_key, + base_urls={**{"default": cleaned_base_urls.get("default")}, **cleaned_base_urls}, + models={**{"default": cleaned_models.get("default", DEFAULT_MODEL)}, **cleaned_models}, + default_model=cleaned_models.get("default", DEFAULT_MODEL), + ) + + +def build_default_llm_client( + settings: Any, + *, + models: Optional[Dict[str, Optional[str]]] = None, + base_urls: Optional[Dict[str, Optional[str]]] = None, + api_key: Optional[str] = None, + client_kwargs: Optional[Dict[str, Any]] = None, +) -> LLMClient: + """Helper to instantiate an :class:`LLMClient` using application settings.""" + + config = build_llm_config_from_settings(settings) + if any((models, base_urls, api_key)): + config = config.override(models=models, base_urls=base_urls, api_key=api_key) + return LLMClient(config, client_kwargs=client_kwargs) + + +__all__ = [ + "DEFAULT_MODEL", + "LLMClient", + "LLMConfig", + "RateLimitError", + "build_default_llm_client", + "build_llm_config_from_settings", +] diff --git a/meshmind/models/registry.py b/meshmind/models/registry.py index bd98f87..eea632b 100644 --- a/meshmind/models/registry.py +++ b/meshmind/models/registry.py @@ -1,6 +1,6 @@ """Registry for entity and predicate models.""" from typing import Type, Optional, Dict, Set -from pydantic import BaseModel +from meshmind._compat.pydantic import BaseModel class EntityRegistry: @@ -30,4 +30,25 @@ def add(cls, label: str) -> None: @classmethod def allowed(cls, label: str) -> bool: """Check if a predicate label is allowed.""" - return label in cls._predicates \ No newline at end of file + return label in cls._predicates + + @classmethod + def all(cls) -> Set[str]: + """Return all registered predicate labels.""" + + return set(cls._predicates) + + @classmethod + def clear(cls) -> None: + """Remove all registered predicates (testing helper).""" + + cls._predicates.clear() + + @classmethod + def remove(cls, label: str) -> bool: + """Remove a predicate label if it exists.""" + + if label in cls._predicates: + cls._predicates.remove(label) + return True + return False diff --git a/meshmind/pipeline/compress.py b/meshmind/pipeline/compress.py index 9ded746..c7eb995 100644 --- a/meshmind/pipeline/compress.py +++ b/meshmind/pipeline/compress.py @@ -1,13 +1,11 @@ -""" -Pipeline for token-aware compression/summarization of memories. -""" +"""Token-aware compression helpers for memory metadata.""" +from __future__ import annotations + from typing import List -from meshmind.core.types import Memory -try: - import tiktoken -except ImportError: - tiktoken = None # type: ignore +from meshmind.core.observability import log_event, telemetry +from meshmind.core.types import Memory +from meshmind.core.utils import get_token_encoder def compress_memories( @@ -20,19 +18,29 @@ def compress_memories( :param max_tokens: Maximum number of tokens allowed per memory. :return: List of Memory objects with content possibly shortened. """ - encoder = tiktoken.get_encoding('o200k_base') + log_event("pipeline.compress.start", items=len(memories)) + encoder = get_token_encoder("o200k_base", optional=True) + if encoder is None: + telemetry.increment("pipeline.compress.skipped", len(memories)) + return memories compressed = [] - for mem in memories: - content = mem.metadata.get('content') - if not isinstance(content, str): - compressed.append(mem) - continue - tokens = encoder.encode(content) - if len(tokens) <= max_tokens: + modified = 0 + with telemetry.track_duration("pipeline.compress.duration"): + for mem in memories: + content = mem.metadata.get('content') + if not isinstance(content, str): + compressed.append(mem) + continue + tokens = encoder.encode(content) + if len(tokens) <= max_tokens: + compressed.append(mem) + continue + # Truncate tokens and decode back to string + truncated = encoder.decode(tokens[:max_tokens]) + mem.metadata['content'] = truncated compressed.append(mem) - continue - # Truncate tokens and decode back to string - truncated = encoder.decode(tokens[:max_tokens]) - mem.metadata['content'] = truncated - compressed.append(mem) - return compressed \ No newline at end of file + modified += 1 + telemetry.increment("pipeline.compress.processed", len(memories)) + telemetry.increment("pipeline.compress.modified", modified) + log_event("pipeline.compress.complete", modified=modified) + return compressed diff --git a/meshmind/pipeline/consolidate.py b/meshmind/pipeline/consolidate.py index e3f4dc3..398007e 100644 --- a/meshmind/pipeline/consolidate.py +++ b/meshmind/pipeline/consolidate.py @@ -1,29 +1,157 @@ -""" -Pipeline for consolidating and summarizing duplicate memories. -""" -from typing import List, Any +"""Pipeline helpers for consolidating and summarising duplicate memories.""" +from __future__ import annotations + +from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import Dict, Iterable, List, Optional from meshmind.core.types import Memory -def consolidate_memories(memories: List[Memory]) -> List[Memory]: - """ - Consolidate duplicate memories by name, preferring high importance. +@dataclass +class ConsolidationOutcome: + """Result of merging a group of related memories.""" + + updated: Memory + removed_ids: List[str] + skipped_ids: List[str] = field(default_factory=list) + + +@dataclass +class ConsolidationSettings: + """Constraints used when planning consolidation batches.""" + + max_group_size: int = 50 + max_updates: int = 200 + max_updates_per_namespace: int = 40 + + +@dataclass +class ConsolidationPlan: + """Container for consolidation outcomes and any skipped groups.""" + + outcomes: List[ConsolidationOutcome] = field(default_factory=list) + skipped_groups: Dict[str, int] = field(default_factory=dict) + + def add_skipped(self, namespace: str, count: int) -> None: + self.skipped_groups[namespace] = self.skipped_groups.get(namespace, 0) + count + + def __iter__(self): + return iter(self.outcomes) + + def __len__(self) -> int: + return len(self.outcomes) - :param memories: List of Memory objects. - :return: List of consolidated Memory objects. - """ - # Group memories by name - grouped: dict[str, List[Memory]] = {} + +def _merge_metadata(group: Iterable[Memory]) -> Dict[str, object]: + merged: Dict[str, object] = {} + for mem in group: + metadata = getattr(mem, "metadata", {}) or {} + if not isinstance(metadata, dict): + continue + for key, value in metadata.items(): + if key not in merged: + merged[key] = value + continue + existing = merged[key] + if existing == value: + continue + if not isinstance(existing, list): + existing = [existing] + if isinstance(value, list): + for item in value: + if item not in existing: + existing.append(item) + else: + if value not in existing: + existing.append(value) + merged[key] = existing + return merged + + +def _combine_embeddings(group: Iterable[Memory]) -> List[float] | None: + embeddings: List[List[float]] = [] + for mem in group: + emb = getattr(mem, "embedding", None) + if isinstance(emb, list) and emb: + embeddings.append([float(x) for x in emb]) + if not embeddings: + return None + length = len(embeddings[0]) + sums = [0.0] * length + for vector in embeddings: + if len(vector) != length: + continue + for idx, value in enumerate(vector): + sums[idx] += value + count = max(len(embeddings), 1) + return [round(total / count, 6) for total in sums] + + +def _summary_from_group(group: Iterable[Memory]) -> str: + seen: List[str] = [] + for mem in group: + text = "" + metadata = getattr(mem, "metadata", {}) or {} + if isinstance(metadata, dict): + text = str(metadata.get("content") or metadata.get("summary") or "") + if not text: + text = getattr(mem, "name", "") + text = text.strip() + if text and text not in seen: + seen.append(text) + return " \n".join(seen[:3]) + + +def consolidate_memories( + memories: List[Memory], + settings: Optional[ConsolidationSettings] = None, +) -> ConsolidationPlan: + """Consolidate duplicate memories and describe the merge plan.""" + + grouped: Dict[tuple[str, str, str], List[Memory]] = {} for mem in memories: - grouped.setdefault(mem.name, []).append(mem) + key = (getattr(mem, "namespace", ""), getattr(mem, "entity_label", ""), getattr(mem, "name", "")) + grouped.setdefault(key, []).append(mem) + + cfg = settings or ConsolidationSettings() + plan = ConsolidationPlan() + namespace_counts: Dict[str, int] = {} + + for group in grouped.values(): + if len(group) == 1: + continue + namespace = getattr(group[0], "namespace", "") or "default" + if len(group) > cfg.max_group_size: + plan.add_skipped(namespace, len(group)) + continue + if len(plan) >= cfg.max_updates: + plan.add_skipped(namespace, len(group)) + continue + if namespace_counts.get(namespace, 0) >= cfg.max_updates_per_namespace: + plan.add_skipped(namespace, len(group)) + continue - consolidated: List[Memory] = [] - for name, group in grouped.items(): - # Choose the memory with highest importance (fallback to first) - selected = max( + primary = max( group, - key=lambda m: (m.importance or 0.0), + key=lambda m: ( + getattr(m, "importance", 0.0) or 0.0, + getattr(m, "updated_at", getattr(m, "created_at", None)), + ), ) - consolidated.append(selected) - return consolidated \ No newline at end of file + metadata = _merge_metadata(group) + summary = _summary_from_group(group) + if summary: + metadata.setdefault("consolidated_summary", summary) + embedding = _combine_embeddings(group) + update: Dict[str, object] = {"metadata": metadata} + if embedding is not None: + update["embedding"] = embedding + if metadata and "importance" not in metadata: + importance_values = [getattr(mem, "importance", 0.0) or 0.0 for mem in group] + update["importance"] = round(max(importance_values), 3) + updated = primary.model_copy(update=update) + removed_ids = [str(getattr(mem, "uuid", "")) for mem in group if mem is not primary] + namespace_counts[namespace] = namespace_counts.get(namespace, 0) + 1 + plan.outcomes.append(ConsolidationOutcome(updated=updated, removed_ids=removed_ids)) + return plan diff --git a/meshmind/pipeline/expire.py b/meshmind/pipeline/expire.py index cb8070e..57e09de 100644 --- a/meshmind/pipeline/expire.py +++ b/meshmind/pipeline/expire.py @@ -1,7 +1,5 @@ -""" -Pipeline for expiring memories with TTL. -""" -from datetime import datetime, timedelta +"""Pipeline for expiring memories with TTL.""" +from datetime import datetime, timedelta, timezone from typing import List from meshmind.api.memory_manager import MemoryManager @@ -18,7 +16,7 @@ def expire_memories(manager: MemoryManager) -> List[str]: expired = [] # List all memories memories = manager.list_memories() - now = datetime.utcnow() + now = datetime.now(timezone.utc) for mem in memories: ttl = getattr(mem, 'ttl_seconds', None) if ttl is None: diff --git a/meshmind/pipeline/extract.py b/meshmind/pipeline/extract.py index 613073b..59f6c7f 100644 --- a/meshmind/pipeline/extract.py +++ b/meshmind/pipeline/extract.py @@ -1,12 +1,17 @@ -from typing import Any, List, Type +from typing import Any, List, Sequence, Type + +from meshmind.core.observability import log_event, telemetry +from meshmind.core.config import settings +from meshmind.llm_client import LLMClient, LLMConfig, build_llm_config_from_settings def extract_memories( instructions: str, namespace: str, - entity_types: List[Type[Any]], + entity_types: Sequence[Type[Any]], embedding_model: str, - content: List[str], + content: Sequence[str], llm_client: Any = None, + llm_config: LLMConfig | None = None, ) -> List[Any]: """ Extract structured Memory objects from provided content using an LLM. @@ -20,16 +25,16 @@ def extract_memories( :return: List of extracted Memory-like dicts or objects. """ import json - try: - from openai import OpenAI - except ImportError: - raise RuntimeError("openai package is required for extraction pipeline") from meshmind.core.types import Memory from meshmind.core.embeddings import EncoderRegistry + from meshmind.models.registry import EntityRegistry + + log_event("pipeline.extract.start", segments=len(content)) # Initialize default LLM client if not provided if llm_client is None: - llm_client = OpenAI() + config = llm_config or build_llm_config_from_settings(settings) + llm_client = LLMClient(config) # Prepare function schema for Memory items mem_schema = Memory.schema() @@ -46,6 +51,9 @@ def extract_memories( } # Build system prompt using a default template and user instructions + entity_types = list(entity_types) or [Memory] + for model in entity_types: + EntityRegistry.register(model) allowed_labels = [cls.__name__ for cls in entity_types] default_prompt = ( "You are an agent that extracts structured memories from text segments. " @@ -58,15 +66,16 @@ def extract_memories( prompt += f"\nAllowed entity labels: {', '.join(allowed_labels)}." messages = [{"role": "system", "content": prompt}] # Add each text segment as a user message - messages += [{"role": "user", "content": text} for text in content] + messages += [{"role": "user", "content": text} for text in list(content)] # Call chat completion with function-calling - response = llm_client.responses.create( - model="gpt-4.1-mini", - messages=messages, - functions=[function_spec], - function_call={"name": "extract_memories"}, - ) + with telemetry.track_duration("pipeline.extract.duration"): + response = llm_client.responses.create( + operation="extraction", + messages=messages, + functions=[function_spec], + function_call={"name": "extract_memories"}, + ) msg = response.choices[0].message # Parse function call arguments or direct JSON if msg.get("function_call"): @@ -82,7 +91,7 @@ def extract_memories( memories = [] # Instantiate Memory objects, validate entity labels, and compute embeddings - from pydantic import ValidationError + from meshmind._compat.pydantic import ValidationError encoder = EncoderRegistry.get(embedding_model) for entry in items: label = entry.get("entity_label") @@ -99,4 +108,8 @@ def extract_memories( emb = encoder.encode([mem.name])[0] mem.embedding = emb memories.append(mem) - return memories \ No newline at end of file + + telemetry.increment("pipeline.extract.segments", len(content)) + telemetry.increment("pipeline.extract.memories", len(memories)) + log_event("pipeline.extract.complete", memories=len(memories)) + return memories diff --git a/meshmind/pipeline/preprocess.py b/meshmind/pipeline/preprocess.py index fde0077..53d7a39 100644 --- a/meshmind/pipeline/preprocess.py +++ b/meshmind/pipeline/preprocess.py @@ -1,4 +1,12 @@ -from typing import Any, List +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Any, Dict, List + +import math +import re + +from meshmind.core.observability import log_event, telemetry def deduplicate(memories: List[Any], threshold: float = 0.95) -> List[Any]: """ @@ -40,22 +48,106 @@ def deduplicate(memories: List[Any], threshold: float = 0.95) -> List[Any]: unique.append(mem) return unique +def _text_from_memory(mem: Any) -> str: + chunks: List[str] = [] + name = getattr(mem, "name", None) + if isinstance(name, str): + chunks.append(name) + metadata = getattr(mem, "metadata", None) or {} + if isinstance(metadata, dict): + for value in metadata.values(): + if isinstance(value, str): + chunks.append(value) + return " ".join(chunks) + + +def _recent_bonus(reference_time: Any, now: datetime) -> float: + if reference_time is None: + return 0.0 + try: + if isinstance(reference_time, datetime): + ref = reference_time + else: + ref = datetime.fromisoformat(str(reference_time)) + except Exception: + return 0.0 + if ref.tzinfo is None: + ref = ref.replace(tzinfo=timezone.utc) + delta = now - ref + seconds = max(delta.total_seconds(), 0.0) + week = 7 * 24 * 60 * 60 + return max(0.0, 1.0 - min(seconds / week, 1.0)) + + def score_importance(memories: List[Any]) -> List[Any]: - """ - Assign or update importance scores for each Memory. + """Assign a heuristic importance score for each memory.""" - :param memories: List of Memory-like objects. - :return: List of Memory-like objects with updated importance. - """ - # Assign a default importance score if missing + now = datetime.now(timezone.utc) for mem in memories: - if getattr(mem, 'importance', None) is None: - try: - mem.importance = 1.0 - except Exception: + try: + if getattr(mem, "importance", None) not in (None, 0): continue + + text = _text_from_memory(mem) + tokens = re.findall(r"\w+", text.lower()) + unique_ratio = len(set(tokens)) / max(len(tokens), 1) + length_factor = min(len(tokens) / 25.0, 1.5) + digit_bonus = 0.3 if any(ch.isdigit() for ch in text) else 0.0 + recency_bonus = _recent_bonus(getattr(mem, "reference_time", None), now) + metadata_size = len(getattr(mem, "metadata", {}) or {}) + metadata_bonus = min(metadata_size * 0.05, 0.25) + + embedding = getattr(mem, "embedding", None) or [] + magnitude = math.sqrt(sum((float(x) ** 2 for x in embedding))) if embedding else 0.0 + magnitude_bonus = min(magnitude / 10.0, 0.4) + + score = 0.5 + (0.8 * unique_ratio) + length_factor + digit_bonus + score += recency_bonus + metadata_bonus + magnitude_bonus + mem.importance = round(min(score, 5.0), 3) + except Exception: + continue + metrics = summarize_importance(memories) + telemetry.gauge("importance.mean", metrics["mean"]) + telemetry.gauge("importance.stddev", metrics["stddev"]) + telemetry.gauge("importance.count", float(metrics["count"])) + log_event( + "importance.scored", + mean=metrics["mean"], + stddev=metrics["stddev"], + count=metrics["count"], + recent=metrics["recent_bonus"], + ) return memories + +def summarize_importance(memories: List[Any]) -> Dict[str, float]: + """Return descriptive statistics about importance assignments.""" + + values: List[float] = [] + recency_bonus_total = 0.0 + now = datetime.now(timezone.utc) + for mem in memories: + val = getattr(mem, "importance", None) + if val is None: + continue + try: + values.append(float(val)) + recency_bonus_total += _recent_bonus(getattr(mem, "reference_time", None), now) + except Exception: + continue + count = len(values) + if not values: + return {"mean": 0.0, "stddev": 0.0, "count": 0.0, "recent_bonus": 0.0} + mean_value = sum(values) / count + variance = sum((value - mean_value) ** 2 for value in values) / count + stddev = math.sqrt(variance) + return { + "mean": round(mean_value, 3), + "stddev": round(stddev, 3), + "count": float(count), + "recent_bonus": round(recency_bonus_total / count, 3), + } + def compress(memories: List[Any]) -> List[Any]: """ Compress long text fields in Memory objects to save space or reduce tokens. diff --git a/meshmind/pipeline/store.py b/meshmind/pipeline/store.py index 3d0b07c..f4e1b6c 100644 --- a/meshmind/pipeline/store.py +++ b/meshmind/pipeline/store.py @@ -1,23 +1,71 @@ +"""Persistence helpers for storing memories and triplets.""" +from __future__ import annotations + from typing import Any, Iterable + +from meshmind._compat.pydantic import BaseModel + +from meshmind.core.observability import log_event, telemetry +from meshmind.core.types import Triplet from meshmind.db.base_driver import GraphDriver +from meshmind.models.registry import EntityRegistry, PredicateRegistry + + +def _props(obj: Any) -> dict[str, Any]: + if isinstance(obj, BaseModel): + return obj.dict(exclude_none=True) + if hasattr(obj, "dict"): + try: + return obj.dict(exclude_none=True) # type: ignore[attr-defined] + except TypeError: + pass + if isinstance(obj, dict): + return {k: v for k, v in obj.items() if v is not None} + return {k: v for k, v in obj.__dict__.items() if v is not None} + def store_memories( memories: Iterable[Any], graph_driver: GraphDriver, + *, + entity_registry: type[EntityRegistry] | None = None, ) -> None: - """ - Persist a sequence of Memory objects into the graph database. - - :param memories: An iterable of Memory-like objects with attributes for upsert. - :param graph_driver: An instance of GraphDriver to perform database operations. - """ - # Iterate over Memory-like objects and upsert into graph - for mem in memories: - # Use Pydantic-like dict to extract properties - try: - props = mem.dict(exclude_none=True) - except Exception: - # Fallback for non-Pydantic objects - props = mem.__dict__ - # Upsert entity node with label and name - graph_driver.upsert_entity(mem.entity_label, mem.name, props) \ No newline at end of file + """Persist a sequence of Memory objects into the graph database.""" + + registry = entity_registry or EntityRegistry + stored = 0 + with telemetry.track_duration("pipeline.store.memories.duration"): + for mem in memories: + props = _props(mem) + label = getattr(mem, "entity_label", None) + if label and registry.model_for_label(label) is None and isinstance(mem, BaseModel): + registry.register(type(mem)) + graph_driver.upsert_entity(label or "Memory", getattr(mem, "name", ""), props) + stored += 1 + telemetry.increment("pipeline.store.memories.stored", stored) + log_event("pipeline.store.memories", count=stored) + + +def store_triplets( + triplets: Iterable[Triplet], + graph_driver: GraphDriver, + *, + predicate_registry: type[PredicateRegistry] | None = None, +) -> None: + """Persist a collection of ``Triplet`` relationships.""" + + registry = predicate_registry or PredicateRegistry + stored = 0 + with telemetry.track_duration("pipeline.store.triplets.duration"): + for triplet in triplets: + registry.add(triplet.predicate) + props = _props(triplet) + graph_driver.upsert_edge( + triplet.subject, + triplet.predicate, + triplet.object, + props, + ) + stored += 1 + telemetry.increment("pipeline.store.triplets.stored", stored) + log_event("pipeline.store.triplets", count=stored) diff --git a/meshmind/retrieval/__init__.py b/meshmind/retrieval/__init__.py index e69de29..4239b7f 100644 --- a/meshmind/retrieval/__init__.py +++ b/meshmind/retrieval/__init__.py @@ -0,0 +1,39 @@ +"""Retrieval helpers exposed for external consumers.""" + +from .search import ( + search, + search_bm25, + search_exact, + search_fuzzy, + search_regex, + search_vector, +) +from .graph import ( + graph_bm25_search, + graph_exact_search, + graph_fuzzy_search, + graph_hybrid_search, + graph_regex_search, + graph_vector_search, +) +from .vector import vector_search, vector_search_from_embeddings +from .rerank import llm_rerank, apply_reranker + +__all__ = [ + "search", + "search_bm25", + "search_exact", + "search_fuzzy", + "search_regex", + "search_vector", + "graph_hybrid_search", + "graph_vector_search", + "graph_regex_search", + "graph_exact_search", + "graph_bm25_search", + "graph_fuzzy_search", + "vector_search", + "vector_search_from_embeddings", + "llm_rerank", + "apply_reranker", +] diff --git a/meshmind/retrieval/bm25.py b/meshmind/retrieval/bm25.py index 6158b03..a68c1d5 100644 --- a/meshmind/retrieval/bm25.py +++ b/meshmind/retrieval/bm25.py @@ -1,9 +1,17 @@ -""" -TF-IDF based retrieval (approximate BM25) using scikit-learn. -""" +"""TF-IDF based retrieval (approximate BM25) using scikit-learn or fallbacks.""" +from __future__ import annotations + +import math +import re +from collections import Counter from typing import List, Tuple -from sklearn.feature_extraction.text import TfidfVectorizer -from sklearn.metrics.pairwise import cosine_similarity + +try: # pragma: no cover - optional dependency + from sklearn.feature_extraction.text import TfidfVectorizer + from sklearn.metrics.pairwise import cosine_similarity +except ImportError: # pragma: no cover - exercised when sklearn is unavailable + TfidfVectorizer = None # type: ignore + cosine_similarity = None # type: ignore from meshmind.core.types import Memory @@ -21,24 +29,52 @@ def bm25_search( :param top_k: Number of top results to return. :return: List of (Memory, similarity_score) tuples. """ - # Prepare document texts docs = [mem.name for mem in memories] - # Vectorize documents and query - vectorizer = TfidfVectorizer() - tfidf_matrix = vectorizer.fit_transform(docs) - query_vec = vectorizer.transform([query]) - # Compute cosine similarity scores - scores = cosine_similarity(query_vec, tfidf_matrix)[0] - # Rank scores - ranked = sorted( - enumerate(scores), key=lambda x: x[1], reverse=True - ) - # Collect top_k non-zero scores + + if TfidfVectorizer is not None and cosine_similarity is not None: + vectorizer = TfidfVectorizer() + tfidf_matrix = vectorizer.fit_transform(docs) + query_vec = vectorizer.transform([query]) + scores = cosine_similarity(query_vec, tfidf_matrix)[0] + ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True) + results: List[Tuple[Memory, float]] = [] + for idx, score in ranked: + if score <= 0: + break + results.append((memories[idx], float(score))) + if len(results) >= top_k: + break + return results + + tokens = [_tokenize(doc) for doc in docs] + query_tokens = Counter(_tokenize(query)) + doc_freq: Counter[str] = Counter() + for tok_set in map(set, tokens): + for token in tok_set: + doc_freq[token] += 1 + + scores: List[Tuple[int, float]] = [] + total_docs = max(len(tokens), 1) + for idx, doc_tokens in enumerate(tokens): + doc_count = Counter(doc_tokens) + doc_len = len(doc_tokens) or 1 + score = 0.0 + for token, q_tf in query_tokens.items(): + tf = doc_count.get(token, 0) / doc_len + idf = math.log((total_docs + 1) / (doc_freq.get(token, 0) + 1)) + 1.0 + score += tf * idf * q_tf + scores.append((idx, score)) + + ranked = sorted(scores, key=lambda x: x[1], reverse=True) results: List[Tuple[Memory, float]] = [] for idx, score in ranked: if score <= 0: - break + continue results.append((memories[idx], float(score))) if len(results) >= top_k: break - return results \ No newline at end of file + return results + + +def _tokenize(text: str) -> List[str]: + return re.findall(r"\w+", text.lower()) diff --git a/meshmind/retrieval/fuzzy.py b/meshmind/retrieval/fuzzy.py index bd762e0..461da73 100644 --- a/meshmind/retrieval/fuzzy.py +++ b/meshmind/retrieval/fuzzy.py @@ -1,8 +1,14 @@ -""" -Fuzzy string matching retrieval using rapidfuzz. -""" -from typing import List, Tuple -from rapidfuzz import process, fuzz +"""Fuzzy string matching retrieval with optional ``rapidfuzz`` acceleration.""" +from __future__ import annotations + +from difflib import SequenceMatcher +from typing import Callable, List, Tuple + +try: # pragma: no cover - optional dependency + from rapidfuzz import fuzz, process +except ImportError: # pragma: no cover - fallback for environments without rapidfuzz + fuzz = None # type: ignore + process = None # type: ignore from meshmind.core.types import Memory @@ -22,19 +28,31 @@ def fuzzy_search( :param score_cutoff: Minimum score (0-1) to include in results. :return: List of (Memory, normalized_score) tuples. """ - # Build choices mapping choices = [mem.name for mem in memories] - # rapidfuzz returns scores in 0-100 range - raw_results = process.extract( - query, - choices, - scorer=fuzz.WRatio, - limit=top_k, - score_cutoff=score_cutoff * 100, - ) + + if process is not None and fuzz is not None: + raw_results = process.extract( + query, + choices, + scorer=fuzz.WRatio, + limit=top_k, + score_cutoff=score_cutoff * 100, + ) + results: List[Tuple[Memory, float]] = [] + for match, score, idx in raw_results: + results.append((memories[idx], score / 100.0)) + return results + + scorer: Callable[[str, str], float] = _sequence_ratio results: List[Tuple[Memory, float]] = [] - for match, score, idx in raw_results: - # Normalize score to [0,1] - norm = score / 100.0 - results.append((memories[idx], norm)) - return results \ No newline at end of file + for idx, name in enumerate(choices): + score = scorer(query, name) + if score < score_cutoff: + continue + results.append((memories[idx], score)) + results.sort(key=lambda item: item[1], reverse=True) + return results[:top_k] + + +def _sequence_ratio(a: str, b: str) -> float: + return SequenceMatcher(None, a.lower(), b.lower()).ratio() diff --git a/meshmind/retrieval/graph.py b/meshmind/retrieval/graph.py new file mode 100644 index 0000000..20fc96d --- /dev/null +++ b/meshmind/retrieval/graph.py @@ -0,0 +1,225 @@ +"""Graph-backed retrieval helpers that fetch memories directly from a driver.""" +from __future__ import annotations + +from typing import Callable, Iterable, List, Optional, Sequence + +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.types import Memory, SearchConfig +from meshmind.db.base_driver import GraphDriver +from meshmind.retrieval.search import ( + search as hybrid_search, + search_bm25, + search_exact, + search_fuzzy, + search_regex, + search_vector, +) + +Reranker = Callable[[str, Sequence[Memory], int], Sequence[Memory]] + + +def _load_memories( + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + *, + query: Optional[str] = None, + config: Optional[SearchConfig] = None, + top_k: Optional[int] = None, + use_search: bool = True, +) -> List[Memory]: + manager = MemoryManager(driver) + labels = _ensure_sequence(entity_labels) + candidate_limit: Optional[int] = None + if use_search: + limit_hint = 0 + if config is not None: + limit_hint = max(limit_hint, config.top_k * 5) + if config.rerank_k: + limit_hint = max(limit_hint, config.rerank_k * 2) + if top_k: + limit_hint = max(limit_hint, top_k * 5) + candidate_limit = limit_hint or None + return manager.list_memories( + namespace, + labels, + limit=candidate_limit, + query=query if use_search else None, + use_search=use_search, + ) + + +def _ensure_sequence(values: Iterable[str] | None) -> List[str] | None: + if values is None: + return None + return list(values) + + +def graph_hybrid_search( + query: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + config: Optional[SearchConfig] = None, + reranker: Reranker | None = None, +) -> List[Memory]: + """Run the standard hybrid search against memories fetched from the graph.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + query=query, + config=config, + use_search=True, + ) + return hybrid_search( + query, + memories, + namespace=namespace, + entity_labels=labels, + config=config, + reranker=reranker, + ) + + +def graph_vector_search( + query: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + config: Optional[SearchConfig] = None, +) -> List[Memory]: + """Run vector search against graph-backed memories.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + query=query, + config=config, + use_search=True, + ) + return search_vector( + query, + memories, + namespace=namespace, + entity_labels=labels, + config=config, + ) + + +def graph_regex_search( + pattern: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + flags: int | None = None, + top_k: int = 10, +) -> List[Memory]: + """Execute regex search with memories loaded from the graph.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + top_k=top_k, + use_search=False, + ) + return search_regex( + pattern, + memories, + namespace=namespace, + entity_labels=labels, + flags=flags, + top_k=top_k, + ) + + +def graph_exact_search( + query: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + fields: Optional[Iterable[str]] = None, + case_sensitive: bool = False, + top_k: int = 10, +) -> List[Memory]: + """Execute exact-match search using graph-backed memories.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + query=query, + top_k=top_k, + use_search=True, + ) + target_fields = list(fields) if fields else None + return search_exact( + query, + memories, + namespace=namespace, + entity_labels=labels, + fields=target_fields, + case_sensitive=case_sensitive, + top_k=top_k, + ) + + +def graph_bm25_search( + query: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + top_k: int = 10, +) -> List[Memory]: + """Execute BM25 search using graph-backed memories.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + query=query, + top_k=top_k, + use_search=True, + ) + return search_bm25( + query, + memories, + namespace=namespace, + entity_labels=labels, + top_k=top_k, + ) + + +def graph_fuzzy_search( + query: str, + driver: GraphDriver, + namespace: Optional[str] = None, + entity_labels: Optional[Iterable[str]] = None, + top_k: int = 10, +) -> List[Memory]: + """Execute fuzzy search against graph-backed memories.""" + + labels = _ensure_sequence(entity_labels) + memories = _load_memories( + driver, + namespace, + labels, + query=query, + top_k=top_k, + use_search=True, + ) + return search_fuzzy( + query, + memories, + namespace=namespace, + entity_labels=labels, + top_k=top_k, + ) diff --git a/meshmind/retrieval/rerank.py b/meshmind/retrieval/rerank.py new file mode 100644 index 0000000..a18b96d --- /dev/null +++ b/meshmind/retrieval/rerank.py @@ -0,0 +1,83 @@ +"""Helpers for reranking retrieval results.""" +from __future__ import annotations + +from typing import Callable, List, Sequence + +from meshmind.core.types import Memory + +Reranker = Callable[[str, Sequence[Memory], int], Sequence[Memory]] + + +def llm_rerank( + query: str, + memories: Sequence[Memory], + llm_client: object | None, + top_k: int, + model: str | None = None, + endpoint: str | None = None, +) -> List[Memory]: + """Rerank results using an LLM client that supports the Responses API.""" + if llm_client is None or not memories: + return list(memories)[:top_k] + + model_name = model or "gpt-5-nano" + prompt = "\n".join( + [ + "You are a ranking assistant.", + "Given the query and numbered memory summaries, return a JSON array of memory indexes", + "sorted from best to worst match.", + f"Query: {query}", + "Memories:", + ] + ) + for idx, memory in enumerate(memories): + prompt += f"\n{idx}: {memory.name}" + + try: # pragma: no cover - network interaction mocked in tests + response = llm_client.responses.create( # type: ignore[attr-defined] + operation="rerank", + model=model_name, + base_url=endpoint, + input=[{"role": "user", "content": prompt}], + response_format={"type": "json_schema", "json_schema": { + "name": "rankings", + "schema": { + "type": "object", + "properties": { + "order": { + "type": "array", + "items": {"type": "integer"}, + } + }, + "required": ["order"], + }, + }}, + ) + content = response.output[0].content[0].text # type: ignore[index] + except Exception: + return list(memories)[:top_k] + + try: + import json + + data = json.loads(content) + indexes = [idx for idx in data.get("order", []) if 0 <= idx < len(memories)] + except Exception: + return list(memories)[:top_k] + + ranked = [memories[idx] for idx in indexes] + remaining = [mem for mem in memories if mem not in ranked] + ranked.extend(remaining) + return ranked[:top_k] + + +def apply_reranker( + query: str, + candidates: Sequence[Memory], + top_k: int, + reranker: Reranker | None = None, +) -> List[Memory]: + if reranker is None: + return list(candidates)[:top_k] + ranked = reranker(query, candidates, top_k) + return list(ranked)[:top_k] diff --git a/meshmind/retrieval/search.py b/meshmind/retrieval/search.py index 666d7b1..f9da468 100644 --- a/meshmind/retrieval/search.py +++ b/meshmind/retrieval/search.py @@ -1,7 +1,8 @@ -""" -Unified dispatcher for various retrieval strategies. -""" -from typing import List, Optional +"""Unified dispatcher for various retrieval strategies.""" +from __future__ import annotations + +import re +from typing import Callable, List, Optional, Sequence from meshmind.core.types import Memory, SearchConfig from meshmind.retrieval.bm25 import bm25_search @@ -12,6 +13,23 @@ filter_by_entity_labels, filter_by_metadata, ) +from meshmind.retrieval.rerank import apply_reranker +from meshmind.retrieval.vector import vector_search + +Reranker = Callable[[str, Sequence[Memory], int], Sequence[Memory]] + + +def _apply_filters( + memories: Sequence[Memory], + namespace: Optional[str], + entity_labels: Optional[List[str]], + config: Optional[SearchConfig], +) -> List[Memory]: + mems = filter_by_namespace(list(memories), namespace) + mems = filter_by_entity_labels(mems, entity_labels) + if config and config.filters: + mems = filter_by_metadata(mems, config.filters) + return mems def search( @@ -20,28 +38,29 @@ def search( namespace: Optional[str] = None, entity_labels: Optional[List[str]] = None, config: Optional[SearchConfig] = None, + reranker: Reranker | None = None, ) -> List[Memory]: - """ - Perform hybrid search over memories with optional filters. - - :param query: Query string. - :param memories: List of Memory objects. - :param namespace: Filter by namespace. - :param entity_labels: Filter by entity labels. - :param config: SearchConfig overriding defaults. - :return: Ranked list of Memory objects. - """ - # Apply filters - mems = filter_by_namespace(memories, namespace) - mems = filter_by_entity_labels(mems, entity_labels) - if config and config.filters: - mems = filter_by_metadata(mems, config.filters) - - # Use hybrid search by default + """Perform hybrid search with optional reranking.""" cfg = config or SearchConfig() + mems = _apply_filters(memories, namespace, entity_labels, cfg) ranked = hybrid_search(query, mems, cfg) - # Return only Memory objects - return [m for m, _ in ranked] + baseline = [m for m, _ in ranked] + if not baseline: + return [] + + if reranker is None: + return baseline[: cfg.top_k] + + subset = mems[: cfg.rerank_k] + reranked_subset = apply_reranker(query, subset, cfg.rerank_k, reranker) + ordered: List[Memory] = [] + for mem in reranked_subset: + if mem not in ordered: + ordered.append(mem) + for mem in baseline: + if mem not in ordered: + ordered.append(mem) + return ordered[: cfg.top_k] def search_bm25( @@ -51,8 +70,7 @@ def search_bm25( entity_labels: Optional[List[str]] = None, top_k: int = 10, ) -> List[Memory]: - mems = filter_by_namespace(memories, namespace) - mems = filter_by_entity_labels(mems, entity_labels) + mems = _apply_filters(memories, namespace, entity_labels, None) results = bm25_search(query, mems, top_k=top_k) return [m for m, _ in results] @@ -64,7 +82,74 @@ def search_fuzzy( entity_labels: Optional[List[str]] = None, top_k: int = 10, ) -> List[Memory]: - mems = filter_by_namespace(memories, namespace) - mems = filter_by_entity_labels(mems, entity_labels) + mems = _apply_filters(memories, namespace, entity_labels, None) results = fuzzy_search(query, mems, top_k=top_k) - return [m for m, _ in results] \ No newline at end of file + return [m for m, _ in results] + + +def search_vector( + query: str, + memories: List[Memory], + namespace: Optional[str] = None, + entity_labels: Optional[List[str]] = None, + config: Optional[SearchConfig] = None, +) -> List[Memory]: + cfg = config or SearchConfig() + mems = _apply_filters(memories, namespace, entity_labels, cfg) + results = vector_search(query, mems, cfg) + return [m for m, _ in results] + + +def search_regex( + pattern: str, + memories: List[Memory], + namespace: Optional[str] = None, + entity_labels: Optional[List[str]] = None, + flags: int | None = None, + top_k: int = 10, +) -> List[Memory]: + mems = _apply_filters(memories, namespace, entity_labels, None) + regex = re.compile(pattern, flags or re.IGNORECASE) + scored: List[tuple[Memory, int]] = [] + for mem in mems: + haystacks = [mem.name] + [str(value) for value in mem.metadata.values()] + matches = [len(regex.findall(h)) for h in haystacks] + score = max(matches, default=0) + if score > 0: + scored.append((mem, score)) + scored.sort(key=lambda item: item[1], reverse=True) + return [mem for mem, _ in scored[:top_k]] + + +def search_exact( + query: str, + memories: List[Memory], + namespace: Optional[str] = None, + entity_labels: Optional[List[str]] = None, + fields: Optional[List[str]] = None, + case_sensitive: bool = False, + top_k: int = 10, +) -> List[Memory]: + mems = _apply_filters(memories, namespace, entity_labels, None) + needle = query if case_sensitive else query.lower() + fields = fields or ["name"] + + def normalize(value: object) -> str: + text = "" if value is None else str(value) + return text if case_sensitive else text.lower() + + matched: List[Memory] = [] + for mem in mems: + for field in fields: + if field == "metadata": + metadata = getattr(mem, "metadata", {}) + if isinstance(metadata, dict): + if any(normalize(meta_val) == needle for meta_val in metadata.values()): + matched.append(mem) + break + continue + value = getattr(mem, field, None) + if normalize(value) == needle: + matched.append(mem) + break + return matched[:top_k] diff --git a/meshmind/retrieval/vector.py b/meshmind/retrieval/vector.py new file mode 100644 index 0000000..2ca83a6 --- /dev/null +++ b/meshmind/retrieval/vector.py @@ -0,0 +1,57 @@ +"""Vector-only retrieval helpers.""" +from __future__ import annotations + +from typing import Iterable, List, Sequence, Tuple + +from meshmind.core.embeddings import EncoderRegistry +from meshmind.core.similarity import cosine_similarity +from meshmind.core.types import Memory, SearchConfig + + +def vector_search( + query: str, + memories: Sequence[Memory], + config: SearchConfig | None = None, +) -> List[Tuple[Memory, float]]: + """Rank memories using cosine similarity against the query embedding.""" + if not memories: + return [] + + cfg = config or SearchConfig() + encoder = EncoderRegistry.get(cfg.encoder) + query_embedding = encoder.encode([query])[0] + + scored: List[Tuple[Memory, float]] = [] + for memory in memories: + embedding = getattr(memory, "embedding", None) + if embedding is None: + continue + try: + score = cosine_similarity(query_embedding, embedding) + except Exception: + score = 0.0 + scored.append((memory, float(score))) + + scored.sort(key=lambda item: item[1], reverse=True) + return scored[: cfg.top_k] + + +def vector_search_from_embeddings( + query_embedding: Sequence[float], + memories: Iterable[Memory], + top_k: int = 10, +) -> List[Tuple[Memory, float]]: + """Rank memories when the query embedding is precomputed.""" + scored: List[Tuple[Memory, float]] = [] + for memory in memories: + embedding = getattr(memory, "embedding", None) + if embedding is None: + continue + try: + score = cosine_similarity(query_embedding, embedding) + except Exception: + score = 0.0 + scored.append((memory, float(score))) + + scored.sort(key=lambda item: item[1], reverse=True) + return scored[:top_k] diff --git a/meshmind/tasks/celery_app.py b/meshmind/tasks/celery_app.py index 988f355..46fe7f9 100644 --- a/meshmind/tasks/celery_app.py +++ b/meshmind/tasks/celery_app.py @@ -36,4 +36,4 @@ def decorator(fn): app.conf.timezone = 'UTC' app.conf.enable_utc = True else: - app = _DummyCeleryApp() \ No newline at end of file + app = _DummyCeleryApp() diff --git a/meshmind/tasks/scheduled.py b/meshmind/tasks/scheduled.py index eaa4ce2..7814801 100644 --- a/meshmind/tasks/scheduled.py +++ b/meshmind/tasks/scheduled.py @@ -1,6 +1,8 @@ """ Scheduled Celery tasks for expiry, consolidation, and compression. """ +from __future__ import annotations + try: from celery.schedules import crontab _CELERY_BEAT = True @@ -9,25 +11,40 @@ _CELERY_BEAT = False def crontab(*args, **kwargs): # type: ignore return None -from meshmind.tasks.celery_app import app -from meshmind.pipeline.expire import expire_memories -from meshmind.pipeline.consolidate import consolidate_memories -from meshmind.pipeline.compress import compress_memories from meshmind.api.memory_manager import MemoryManager -from meshmind.db.memgraph_driver import MemgraphDriver from meshmind.core.config import settings +from meshmind.core.observability import log_event, telemetry +from meshmind.db.factory import create_graph_driver +from meshmind.pipeline.compress import compress_memories +from meshmind.pipeline.consolidate import ( + ConsolidationOutcome, + ConsolidationPlan, + consolidate_memories, +) +from meshmind.pipeline.expire import expire_memories +from meshmind.tasks.celery_app import app + +_MANAGER: MemoryManager | None = None -# Initialize database driver and memory manager (fallback if mgclient missing) -try: - driver = MemgraphDriver( - settings.MEMGRAPH_URI, - settings.MEMGRAPH_USERNAME, - settings.MEMGRAPH_PASSWORD, - ) - manager = MemoryManager(driver) -except Exception: - driver = None # type: ignore - manager = None # type: ignore + +def _reset_manager() -> None: + global _MANAGER + _MANAGER = None + + +def _get_manager() -> MemoryManager | None: + global _MANAGER + if _MANAGER is not None: + return _MANAGER + + try: + driver = create_graph_driver() + except Exception as exc: + log_event("task.manager.error", error=str(exc)) + return None + + _MANAGER = MemoryManager(driver) + return _MANAGER # Define periodic task schedule if Celery is available if _CELERY_BEAT and hasattr(app, 'conf'): @@ -50,30 +67,69 @@ def crontab(*args, **kwargs): # type: ignore @app.task(name='meshmind.tasks.scheduled.expire_task') def expire_task(): """Delete expired memories based on TTL.""" + manager = _get_manager() if manager is None: return [] - return expire_memories(manager) + log_event("task.expire.start") + with telemetry.track_duration("task.expire.duration"): + results = expire_memories(manager) + telemetry.increment("task.expire.runs") + log_event("task.expire.complete", removed=len(results)) + return results @app.task(name='meshmind.tasks.scheduled.consolidate_task') def consolidate_task(): """Merge duplicate memories and summarise.""" + manager = _get_manager() if manager is None: return 0 memories = manager.list_memories() - consolidated = consolidate_memories(memories) - for mem in consolidated: - manager.update_memory(mem) - return len(consolidated) + log_event("task.consolidate.start", memories=len(memories)) + merged_count = 0 + removed_total = 0 + with telemetry.track_duration("task.consolidate.duration"): + plan = consolidate_memories(memories) + if isinstance(plan, ConsolidationPlan): + skipped = dict(plan.skipped_groups) + else: # backward compatibility + skipped = {} + for outcome in plan: + _apply_consolidation(manager, outcome) + merged_count += 1 + removed_total += len(outcome.removed_ids) + telemetry.increment("task.consolidate.runs") + telemetry.gauge("task.consolidate.skipped_groups", float(len(skipped))) + log_event( + "task.consolidate.complete", + merged=merged_count, + removed=removed_total, + skipped=skipped, + ) + return {"merged": merged_count, "removed": removed_total, "skipped": skipped} @app.task(name='meshmind.tasks.scheduled.compress_task') def compress_task(): """Compress long memories to respect token limits.""" + manager = _get_manager() if manager is None: return 0 memories = manager.list_memories() - compressed = compress_memories(memories) - for mem in compressed: - manager.update_memory(mem) - return len(compressed) \ No newline at end of file + log_event("task.compress.start", memories=len(memories)) + updated = 0 + with telemetry.track_duration("task.compress.duration"): + compressed = compress_memories(memories) + for mem in compressed: + manager.update_memory(mem) + updated += 1 + telemetry.increment("task.compress.runs") + log_event("task.compress.complete", updated=updated) + return updated + + +def _apply_consolidation(manager: MemoryManager, outcome: ConsolidationOutcome) -> None: + manager.update_memory(outcome.updated) + for uid in outcome.removed_ids: + if uid: + manager.delete_memory(uid) diff --git a/meshmind/testing/__init__.py b/meshmind/testing/__init__.py new file mode 100644 index 0000000..8d9da90 --- /dev/null +++ b/meshmind/testing/__init__.py @@ -0,0 +1,14 @@ +"""Testing utilities and doubles for MeshMind.""" +from .fakes import ( + FakeEmbeddingEncoder, + FakeLLMClient, + FakeMemgraphDriver, + FakeRedisBroker, +) + +__all__ = [ + "FakeEmbeddingEncoder", + "FakeLLMClient", + "FakeMemgraphDriver", + "FakeRedisBroker", +] diff --git a/meshmind/testing/fakes.py b/meshmind/testing/fakes.py new file mode 100644 index 0000000..9620dcc --- /dev/null +++ b/meshmind/testing/fakes.py @@ -0,0 +1,237 @@ +"""Testing doubles for MeshMind components and external services.""" +from __future__ import annotations + +import json +from collections import defaultdict, deque +from types import SimpleNamespace +from typing import Any, Deque, Dict, Iterable, List, Optional + +from meshmind.db.in_memory_driver import InMemoryGraphDriver + + +class FakeMemgraphDriver(InMemoryGraphDriver): + """In-memory substitute that records Cypher interactions for assertions.""" + + def __init__(self) -> None: + super().__init__() + self.cypher_calls: List[tuple[str, Dict[str, Any]]] = [] + + def find(self, cypher: str, params: Dict[str, Any]) -> List[Dict[str, Any]]: + self.cypher_calls.append((cypher, dict(params))) + return super().find(cypher, params) + + +class FakeRedisBroker: + """Lightweight Redis replacement implementing the few operations we use.""" + + def __init__(self) -> None: + self._kv: Dict[str, Any] = {} + self._lists: Dict[str, Deque[Any]] = defaultdict(deque) + self._pubsub: Dict[str, List[Any]] = defaultdict(list) + + # Key/value helpers ------------------------------------------------- + def get(self, key: str) -> Any: + return self._kv.get(key) + + def set(self, key: str, value: Any, ex: Optional[int] = None) -> None: # noqa: ARG002 - expiry unused + self._kv[key] = value + + def delete(self, *keys: str) -> int: + removed = 0 + for key in keys: + if key in self._kv: + del self._kv[key] + removed += 1 + if key in self._lists: + del self._lists[key] + removed += 1 + return removed + + # List helpers ------------------------------------------------------ + def lpush(self, key: str, *values: Any) -> int: + lst = self._lists[key] + for value in values: + lst.appendleft(value) + return len(lst) + + def rpush(self, key: str, *values: Any) -> int: + lst = self._lists[key] + for value in values: + lst.append(value) + return len(lst) + + def lrange(self, key: str, start: int, stop: int) -> List[Any]: + lst = list(self._lists[key]) + if stop == -1: + stop = len(lst) + return lst[start:stop + 1] + + # Pub/Sub helpers --------------------------------------------------- + def publish(self, channel: str, message: Any) -> int: + self._pubsub[channel].append(message) + return len(self._pubsub[channel]) + + +class FakeEmbeddingEncoder: + """Deterministic encoder that hashes text into simple float vectors.""" + + def __init__(self, scale: float = 1.0) -> None: + self.scale = scale + + def encode(self, texts: Iterable[str] | str) -> List[List[float]]: + if isinstance(texts, str): + texts = [texts] + vectors: List[List[float]] = [] + for text in texts: + total = sum(ord(ch) for ch in text) + vectors.append([self.scale * (total % 101) / 100.0]) + return vectors + + +class FakeLLMClient: + """OpenAI-compatible stub that records override usage during tests.""" + + class _Proxy: + def __init__(self, parent: "FakeLLMClient", interface: str) -> None: + self._parent = parent + self._interface = interface + + def create( + self, + *, + operation: str | None = None, + model: str | None = None, + base_url: str | None = None, + **kwargs: Any, + ) -> Any: + return self._parent._create( + self._interface, + operation or ("embedding" if self._interface == "embeddings" else "extraction"), + model, + base_url, + **kwargs, + ) + + def __init__( + self, + *, + models: Dict[str, str] | None = None, + base_urls: Dict[str, Optional[str]] | None = None, + api_key: str | None = None, + call_log: List[Dict[str, Any]] | None = None, + ) -> None: + self._models: Dict[str, str] = {"default": "gpt-5-nano"} + if models: + for key, value in models.items(): + if value: + self._models[key] = value + self._base_urls: Dict[str, Optional[str]] = {"default": None} + if base_urls: + for key, value in base_urls.items(): + self._base_urls[key] = value + self.api_key = api_key or "" + self.calls: List[Dict[str, Any]] = call_log if call_log is not None else [] + self.last_override: Dict[str, Any] | None = None + self.config = SimpleNamespace( + model_for=self._model_for, + base_url_for=self._base_url_for, + ) + self.responses = FakeLLMClient._Proxy(self, "responses") + self.embeddings = FakeLLMClient._Proxy(self, "embeddings") + + # ------------------------------------------------------------------ + # Helpers + # ------------------------------------------------------------------ + def _model_for(self, operation: str, fallback: str | None = None) -> str: + return ( + self._models.get(operation) + or self._models.get("default") + or fallback + or "gpt-5-nano" + ) + + def _base_url_for(self, operation: str) -> Optional[str]: + return self._base_urls.get(operation) or self._base_urls.get("default") + + def with_overrides( + self, + *, + models: Dict[str, Optional[str]] | None = None, + base_urls: Dict[str, Optional[str]] | None = None, + api_key: str | None = None, + ) -> "FakeLLMClient": + new_models = dict(self._models) + if models: + for key, value in models.items(): + if value: + new_models[key] = value + new_base_urls = dict(self._base_urls) + if base_urls: + for key, value in base_urls.items(): + new_base_urls[key] = value + override_details = { + "models": models, + "base_urls": base_urls, + "api_key": api_key, + } + child = FakeLLMClient( + models=new_models, + base_urls=new_base_urls, + api_key=api_key or self.api_key, + call_log=self.calls, + ) + self.last_override = override_details + child.last_override = override_details + return child + + def _create( + self, + interface: str, + operation: str, + model: str | None, + base_url: str | None, + **kwargs: Any, + ) -> Any: + resolved_model = model or self._model_for(operation) + resolved_base = base_url if base_url not in ("", None) else self._base_url_for(operation) + payload = { + "interface": interface, + "operation": operation, + "model": resolved_model, + "base_url": resolved_base, + "kwargs": kwargs, + } + self.calls.append(payload) + + # Construct a synthetic response compatible with llm_rerank expectations. + order = [] + inputs = kwargs.get("input", []) + if inputs: + first = inputs[0] + content = first.get("content") if isinstance(first, dict) else None + if isinstance(content, str): + lines = content.splitlines() + elif isinstance(content, list): + lines = [part.get("text", "") for part in content if isinstance(part, dict)] + else: + lines = [] + for line in lines: + prefix = line.split(":", 1)[0] + if prefix.isdigit(): + order.append(int(prefix)) + if not order: + candidate_count = kwargs.get("candidate_count", 0) + if candidate_count: + order = list(range(candidate_count)) + else: + order = [0] + + response_text = json.dumps({"order": order}) + return SimpleNamespace( + output=[ + SimpleNamespace( + content=[SimpleNamespace(text=response_text)] + ) + ] + ) + diff --git a/meshmind/tests/conftest.py b/meshmind/tests/conftest.py new file mode 100644 index 0000000..06848fc --- /dev/null +++ b/meshmind/tests/conftest.py @@ -0,0 +1,62 @@ +import pytest + +from meshmind.api.memory_manager import MemoryManager +from meshmind.api.service import MemoryService +from meshmind.core.embeddings import EncoderRegistry +from meshmind.core.types import Memory +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.testing import FakeLLMClient, FakeMemgraphDriver, FakeRedisBroker + + +@pytest.fixture +def memory_factory(): + def _factory(name: str, **overrides): + payload = {"namespace": "ns", "name": name, "entity_label": "Test"} + payload.update(overrides) + return Memory(**payload) + + return _factory + + +@pytest.fixture +def dummy_encoder(): + EncoderRegistry.clear() + + class _Encoder: + def encode(self, texts): + return [[1.0 if "apple" in text else 0.0] for text in texts] + + name = "dummy-encoder" + EncoderRegistry.register(name, _Encoder()) + yield name + EncoderRegistry.clear() + + +@pytest.fixture +def fake_memgraph_driver(): + return FakeMemgraphDriver() + + +@pytest.fixture +def fake_redis(): + return FakeRedisBroker() + + +@pytest.fixture +def in_memory_driver(): + return InMemoryGraphDriver() + + +@pytest.fixture +def memory_manager(in_memory_driver): + return MemoryManager(in_memory_driver) + + +@pytest.fixture +def fake_llm_client(): + return FakeLLMClient() + + +@pytest.fixture +def memory_service(memory_manager, fake_llm_client): + return MemoryService(memory_manager, llm_client=fake_llm_client) diff --git a/meshmind/tests/docker/full-stack.yml b/meshmind/tests/docker/full-stack.yml new file mode 100644 index 0000000..2b396b2 --- /dev/null +++ b/meshmind/tests/docker/full-stack.yml @@ -0,0 +1,49 @@ +version: "3.9" + +services: + memgraph: + extends: + file: ./memgraph.yml + service: memgraph + container_name: meshmind-int-memgraph + ports: + - "27687:7687" + - "23000:3000" + + neo4j: + extends: + file: ./neo4j.yml + service: neo4j + container_name: meshmind-int-neo4j + ports: + - "27474:7474" + - "27688:7687" + + redis: + extends: + file: ./redis.yml + service: redis + container_name: meshmind-int-redis + ports: + - "26379:6379" + + celery-worker: + build: + context: ../../.. + command: celery -A meshmind.tasks.celery_app worker -B -l info + environment: + MEMGRAPH_URI: bolt://memgraph:7687 + REDIS_URL: redis://redis:6379/0 + PYTHONUNBUFFERED: "1" + depends_on: + memgraph: + condition: service_healthy + redis: + condition: service_healthy + volumes: + - ../../..:/app + working_dir: /app + +networks: + default: + name: meshmind-integration diff --git a/meshmind/tests/docker/memgraph.yml b/meshmind/tests/docker/memgraph.yml new file mode 100644 index 0000000..d9d250e --- /dev/null +++ b/meshmind/tests/docker/memgraph.yml @@ -0,0 +1,22 @@ +version: "3.9" + +services: + memgraph: + image: memgraph/memgraph-platform:latest + container_name: meshmind-test-memgraph + restart: unless-stopped + ports: + - "17687:7687" + - "13000:3000" + environment: + MEMGRAPH_MEMORY_LIMIT: 1GB + healthcheck: + test: ["CMD", "bash", "-c", "cypher-shell --version || exit 1"] + interval: 15s + timeout: 10s + retries: 5 + volumes: + - memgraph-test-data:/var/lib/memgraph + +volumes: + memgraph-test-data: diff --git a/meshmind/tests/docker/neo4j.yml b/meshmind/tests/docker/neo4j.yml new file mode 100644 index 0000000..d38bf73 --- /dev/null +++ b/meshmind/tests/docker/neo4j.yml @@ -0,0 +1,28 @@ +version: "3.9" + +services: + neo4j: + image: neo4j:5.26 + container_name: meshmind-test-neo4j + restart: unless-stopped + ports: + - "17474:7474" + - "17688:7687" + environment: + NEO4J_AUTH: neo4j/meshminD123 + NEO4J_PLUGINS: '["apoc"]' + NEO4J_dbms_security_procedures_unrestricted: apoc.* + NEO4J_apoc_import_file_enabled: "true" + NEO4J_apoc_export_file_enabled: "true" + healthcheck: + test: ["CMD", "cypher-shell", "-u", "neo4j", "-p", "meshminD123", "RETURN 1"] + interval: 15s + timeout: 10s + retries: 5 + volumes: + - neo4j-test-data:/data + - neo4j-test-logs:/logs + +volumes: + neo4j-test-data: + neo4j-test-logs: diff --git a/meshmind/tests/docker/redis.yml b/meshmind/tests/docker/redis.yml new file mode 100644 index 0000000..2466d99 --- /dev/null +++ b/meshmind/tests/docker/redis.yml @@ -0,0 +1,20 @@ +version: "3.9" + +services: + redis: + image: redis:7-alpine + container_name: meshmind-test-redis + restart: unless-stopped + ports: + - "16379:6379" + command: redis-server --save 60 1000 --loglevel warning + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - redis-test-data:/data + +volumes: + redis-test-data: diff --git a/meshmind/tests/test_cli_admin.py b/meshmind/tests/test_cli_admin.py new file mode 100644 index 0000000..4af2aee --- /dev/null +++ b/meshmind/tests/test_cli_admin.py @@ -0,0 +1,68 @@ +from argparse import Namespace +from io import StringIO + +import pytest + +from meshmind.cli import admin +from meshmind.core.observability import telemetry +from meshmind.models.registry import PredicateRegistry + + +@pytest.fixture(autouse=True) +def reset_registry(): + PredicateRegistry.clear() + telemetry.reset() + yield + PredicateRegistry.clear() + telemetry.reset() + + +def test_handle_predicates_add_list_remove(): + stream = StringIO() + admin.handle_predicates(Namespace(add="LIKES", remove=None, list=False), stream=stream) + assert "LIKES" in stream.getvalue() + stream = StringIO() + admin.handle_predicates(Namespace(add=None, remove="LIKES", list=False), stream=stream) + assert "Removed" in stream.getvalue() + stream = StringIO() + admin.handle_predicates(Namespace(add=None, remove=None, list=True), stream=stream) + assert "predicates" in stream.getvalue() + + +def test_handle_maintenance_outputs_snapshot(): + telemetry.increment("events.test") + stream = StringIO() + admin.handle_maintenance(Namespace(reset=False), stream=stream) + output = stream.getvalue() + assert "events.test" in output + + +def test_handle_graph_check_with_verify(monkeypatch): + class DummyDriver: + def verify_connectivity(self): + return True + + monkeypatch.setattr(admin, "create_graph_driver", lambda **kwargs: DummyDriver()) + stream = StringIO() + status = admin.handle_graph_check(Namespace(backend="neo4j"), stream=stream) + assert status == 0 + assert "ok" in stream.getvalue() + + +def test_handle_counts_outputs_grouped(monkeypatch): + class DummyDriver: + def __init__(self): + self.closed = False + + def count_entities(self, namespace=None): # noqa: ARG002 - testing helper + return {"demo": {"Note": 3}} + + def close(self): + self.closed = True + + monkeypatch.setattr(admin, "create_graph_driver", lambda **kwargs: DummyDriver()) + stream = StringIO() + status = admin.handle_counts(Namespace(backend="memory", namespace=None), stream=stream) + + assert status == 0 + assert "\"Note\": 3" in stream.getvalue() diff --git a/meshmind/tests/test_client.py b/meshmind/tests/test_client.py new file mode 100644 index 0000000..5a9946d --- /dev/null +++ b/meshmind/tests/test_client.py @@ -0,0 +1,41 @@ +from meshmind.api.memory_manager import MemoryManager +from meshmind.client import MeshMind +from meshmind.db.in_memory_driver import InMemoryGraphDriver + + +def test_meshmind_list_memories_forwards_filters(monkeypatch): + driver = InMemoryGraphDriver() + client = MeshMind(llm_client=object(), graph_driver=driver) + + captured: dict[str, object] = {} + + def fake_list_memories(self, namespace=None, entity_labels=None, **kwargs): # noqa: ANN001 + captured["namespace"] = namespace + captured["entity_labels"] = entity_labels + captured["kwargs"] = kwargs + return [] + + monkeypatch.setattr(MemoryManager, "list_memories", fake_list_memories) + + client.list_memories( + namespace="demo", + entity_labels=["Note"], + offset=5, + limit=10, + query="alpha", + use_search=True, + ) + + assert captured["namespace"] == "demo" + assert captured["entity_labels"] == ["Note"] + assert captured["kwargs"] == {"offset": 5, "limit": 10, "query": "alpha", "use_search": True} + + +def test_meshmind_memory_counts_delegates(monkeypatch): + driver = InMemoryGraphDriver() + client = MeshMind(llm_client=object(), graph_driver=driver) + + monkeypatch.setattr(MemoryManager, "count_memories", lambda self, namespace=None: {"ns": {"Note": 1}}) + + counts = client.memory_counts(namespace="ns") + assert counts["ns"]["Note"] == 1 diff --git a/meshmind/tests/test_db_drivers.py b/meshmind/tests/test_db_drivers.py new file mode 100644 index 0000000..32fc61a --- /dev/null +++ b/meshmind/tests/test_db_drivers.py @@ -0,0 +1,184 @@ +from uuid import uuid4 + +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.types import Memory, Triplet +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.db.sqlite_driver import SQLiteGraphDriver +from meshmind.testing import FakeEmbeddingEncoder, FakeMemgraphDriver, FakeRedisBroker + + +def _memory_payload(name: str) -> dict: + return { + "uuid": str(uuid4()), + "namespace": "test", + "name": name, + "entity_label": "Note", + "metadata": {"content": name}, + } + + +def test_in_memory_driver_roundtrip(): + driver = InMemoryGraphDriver() + payload = _memory_payload("alpha") + driver.upsert_entity("Note", payload["name"], payload) + + entity = driver.get_entity(payload["uuid"]) + assert entity and entity["name"] == "alpha" + + triplet = Triplet( + subject=payload["uuid"], + predicate="related_to", + object=str(uuid4()), + namespace="test", + entity_label="Relation", + ) + driver.upsert_edge(triplet.subject, triplet.predicate, triplet.object, triplet.dict()) + records = driver.list_triplets("test") + assert records and records[0]["predicate"] == "related_to" + + driver.delete(uuid4()) # deleting unknown should not error + driver.delete_triplet(triplet.subject, triplet.predicate, triplet.object) + assert not driver.list_triplets("test") + + driver.delete(payload["uuid"]) + assert driver.get_entity(payload["uuid"]) is None + + +def test_in_memory_driver_pagination_and_search(): + driver = InMemoryGraphDriver() + for idx in range(5): + payload = _memory_payload(f"item-{idx}") + payload["metadata"]["description"] = f"important note {idx}" + driver.upsert_entity("Note", payload["name"], payload) + + limited = driver.list_entities(namespace="test", offset=1, limit=2) + assert len(limited) == 2 + + searched = driver.search_entities(query="important note 3", namespace="test") + assert len(searched) == 1 + assert searched[0]["name"].endswith("3") + + +def test_sqlite_driver_roundtrip(): + driver = SQLiteGraphDriver(":memory:") + payload = _memory_payload("beta") + driver.upsert_entity("Note", payload["name"], payload) + + entity = driver.get_entity(payload["uuid"]) + assert entity and entity["name"] == "beta" + + triplet = Triplet( + subject=payload["uuid"], + predicate="mentions", + object=str(uuid4()), + namespace="test", + entity_label="Relation", + ) + driver.upsert_edge(triplet.subject, triplet.predicate, triplet.object, triplet.dict()) + records = driver.list_triplets("test") + assert records and records[0]["predicate"] == "mentions" + + driver.delete(payload["uuid"]) + assert driver.get_entity(payload["uuid"]) is None + assert not driver.list_triplets("test") + + +def test_sqlite_list_entities_filters_namespace_and_label(): + driver = SQLiteGraphDriver(":memory:") + note = _memory_payload("note") + task = _memory_payload("task") + task["entity_label"] = "Task" + driver.upsert_entity("Note", note["name"], note) + driver.upsert_entity("Task", task["name"], task) + + filtered = driver.list_entities(namespace="test", entity_labels=["Note"]) + assert len(filtered) == 1 + assert filtered[0]["entity_label"] == "Note" + + everything = driver.list_entities(namespace="test") + assert len(everything) == 2 + + +def test_sqlite_search_entities_and_pagination(): + driver = SQLiteGraphDriver(":memory:") + for idx in range(6): + payload = _memory_payload(f"row-{idx}") + payload["metadata"]["summary"] = "lorem ipsum" + driver.upsert_entity("Note", payload["name"], payload) + + chunk = driver.list_entities(namespace="test", limit=3) + assert len(chunk) == 3 + + second_page = driver.list_entities(namespace="test", offset=3, limit=3) + assert len(second_page) == 3 + + match = driver.search_entities(query="ipsum", namespace="test") + assert len(match) == 6 + + none = driver.search_entities(query="missing", namespace="test", limit=1) + assert none == [] + + +def test_fake_memgraph_driver_behaviour(): + driver = FakeMemgraphDriver() + payload = _memory_payload("gamma") + driver.upsert_entity("Note", payload["name"], payload) + records = driver.find("MATCH (m) RETURN m", {}) + assert records + assert driver.cypher_calls + + +def test_fake_memgraph_entity_label_filtering(): + driver = FakeMemgraphDriver() + note = _memory_payload("alpha") + other = _memory_payload("beta") + other["entity_label"] = "Task" + driver.upsert_entity("Note", note["name"], note) + driver.upsert_entity("Task", other["name"], other) + + filtered = driver.list_entities(namespace="test", entity_labels=["Note"]) + assert len(filtered) == 1 + assert filtered[0]["entity_label"] == "Note" + + +def test_fake_memgraph_counts(): + driver = FakeMemgraphDriver() + alpha = _memory_payload("alpha") + beta = _memory_payload("beta") + beta["entity_label"] = "Task" + driver.upsert_entity("Note", alpha["name"], alpha) + driver.upsert_entity("Task", beta["name"], beta) + + counts = driver.count_entities() + assert counts["test"]["Note"] == 1 + assert counts["test"]["Task"] == 1 + + +def test_memory_manager_count_memories(): + driver = InMemoryGraphDriver() + manager = MemoryManager(driver) + first = _memory_payload("first") + second = _memory_payload("second") + second["entity_label"] = "Task" + manager.add_memory(Memory(**first)) + manager.add_memory(Memory(**second)) + + counts = manager.count_memories() + assert counts["test"]["Note"] == 1 + assert counts["test"]["Task"] == 1 + + +def test_fake_redis_broker_roundtrip(): + broker = FakeRedisBroker() + broker.set("key", "value") + assert broker.get("key") == "value" + broker.lpush("queue", "a", "b") + assert broker.lrange("queue", 0, -1) == ["b", "a"] + broker.publish("events", {"message": "hello"}) + assert broker.delete("key") == 1 + + +def test_fake_embedding_encoder_hashing(): + encoder = FakeEmbeddingEncoder(scale=2.0) + vec = encoder.encode(["alpha"]) + assert isinstance(vec, list) and isinstance(vec[0][0], float) diff --git a/meshmind/tests/test_docs_guard.py b/meshmind/tests/test_docs_guard.py new file mode 100644 index 0000000..aadbbb1 --- /dev/null +++ b/meshmind/tests/test_docs_guard.py @@ -0,0 +1,25 @@ +from scripts.check_docs_sync import check_docs + + +def test_docs_guard_passes_when_docs_cover_changes(monkeypatch): + monkeypatch.setattr("scripts.check_docs_sync._git_diff", lambda base: ["meshmind/api/memory_manager.py", "docs/api.md"]) + assert check_docs("HEAD") == 0 + + +def test_docs_guard_fails_when_docs_missing(monkeypatch): + monkeypatch.setattr("scripts.check_docs_sync._git_diff", lambda base: ["meshmind/core/config.py"]) + result = check_docs("HEAD") + assert result == 1 + + +def test_docs_guard_requires_setup_for_compose(monkeypatch): + monkeypatch.setattr("scripts.check_docs_sync._git_diff", lambda base: ["docker-compose.yml"]) + assert check_docs("HEAD") == 1 + + +def test_docs_guard_passes_when_setup_updated(monkeypatch): + monkeypatch.setattr( + "scripts.check_docs_sync._git_diff", + lambda base: ["docker-compose.yml", "SETUP.md"], + ) + assert check_docs("HEAD") == 0 diff --git a/meshmind/tests/test_driver_factory.py b/meshmind/tests/test_driver_factory.py new file mode 100644 index 0000000..3bc6c62 --- /dev/null +++ b/meshmind/tests/test_driver_factory.py @@ -0,0 +1,30 @@ +import pytest + +from meshmind.db.factory import create_graph_driver, graph_driver_factory +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.db.sqlite_driver import SQLiteGraphDriver + + +def test_create_graph_driver_memory(): + driver = create_graph_driver(backend="memory") + assert isinstance(driver, InMemoryGraphDriver) + + +def test_create_graph_driver_sqlite(tmp_path): + path = tmp_path / "graph.db" + driver = create_graph_driver(backend="sqlite", path=str(path)) + try: + assert isinstance(driver, SQLiteGraphDriver) + finally: + driver.close() + + +def test_graph_driver_factory_callable(): + factory = graph_driver_factory(backend="memory") + driver = factory() + assert isinstance(driver, InMemoryGraphDriver) + + +def test_create_graph_driver_invalid_backend(): + with pytest.raises(ValueError): + create_graph_driver(backend="unknown") diff --git a/meshmind/tests/test_graph_retrieval.py b/meshmind/tests/test_graph_retrieval.py new file mode 100644 index 0000000..1696376 --- /dev/null +++ b/meshmind/tests/test_graph_retrieval.py @@ -0,0 +1,121 @@ +from meshmind.api.memory_manager import MemoryManager +from meshmind.core.types import Memory, SearchConfig +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.retrieval.graph import ( + graph_exact_search, + graph_hybrid_search, + graph_regex_search, + graph_vector_search, +) + + +class TrackingDriver(InMemoryGraphDriver): + def __init__(self) -> None: + super().__init__() + self.search_calls: list[dict[str, object]] = [] + self.list_calls = 0 + + def search_entities(self, *args, **kwargs): # noqa: ANN002 - passthrough to super + self.search_calls.append(dict(kwargs)) + return super().search_entities(*args, **kwargs) + + def list_entities(self, *args, **kwargs): # noqa: ANN002 - passthrough to super + self.list_calls += 1 + return super().list_entities(*args, **kwargs) + + +def test_graph_hybrid_search_uses_driver(dummy_encoder): + driver = TrackingDriver() + manager = MemoryManager(driver) + memory = Memory( + namespace="ns", + name="Apple Pie", + entity_label="Recipe", + embedding=[1.0], + ) + manager.add_memory(memory) + config = SearchConfig(encoder=dummy_encoder, top_k=1) + + results = graph_hybrid_search("apple", driver, namespace="ns", config=config) + + assert results and results[0].name == "Apple Pie" + assert driver.search_calls + assert driver.search_calls[0]["query"] == "apple" + + +def test_graph_vector_search_filters_namespace(dummy_encoder): + driver = TrackingDriver() + manager = MemoryManager(driver) + include = Memory( + namespace="keep", + name="Keep", + entity_label="Note", + embedding=[1.0], + ) + exclude = Memory( + namespace="skip", + name="Skip", + entity_label="Note", + embedding=[0.0], + ) + manager.add_memory(include) + manager.add_memory(exclude) + config = SearchConfig(encoder=dummy_encoder, top_k=5) + + results = graph_vector_search("keep", driver, namespace="keep", config=config) + + assert len(results) == 1 + assert results[0].name == include.name + + +def test_graph_exact_search_filters_entity_labels(dummy_encoder): + driver = TrackingDriver() + manager = MemoryManager(driver) + keep = Memory( + namespace="ns", + name="Retain", + entity_label="Note", + metadata={"content": "keep me"}, + ) + skip = Memory( + namespace="ns", + name="Skip", + entity_label="Task", + metadata={"content": "ignore"}, + ) + manager.add_memory(keep) + manager.add_memory(skip) + + results = graph_exact_search( + "Retain", + driver, + namespace="ns", + entity_labels=["Note"], + fields=["name"], + top_k=5, + ) + + assert len(results) == 1 + assert results[0].entity_label == "Note" + + +def test_graph_regex_search_falls_back_to_list(dummy_encoder): + driver = TrackingDriver() + manager = MemoryManager(driver) + memory = Memory( + namespace="ns", + name="Alpha", + entity_label="Note", + metadata={"content": "Value"}, + ) + manager.add_memory(memory) + + results = graph_regex_search( + "Alpha", + driver, + namespace="ns", + entity_labels=["Note"], + ) + assert results + assert driver.list_calls >= 1 + assert not driver.search_calls # regex skips driver search diff --git a/meshmind/tests/test_llm_client.py b/meshmind/tests/test_llm_client.py new file mode 100644 index 0000000..3fed2f7 --- /dev/null +++ b/meshmind/tests/test_llm_client.py @@ -0,0 +1,54 @@ +from meshmind.llm_client import DEFAULT_MODEL, LLMClient, LLMConfig + + +class DummyResponses: + def __init__(self, store): + self._store = store + + def create(self, **kwargs): + self._store.append(("responses", kwargs)) + return {"kwargs": kwargs} + + +class DummyEmbeddings: + def __init__(self, store): + self._store = store + + def create(self, **kwargs): + self._store.append(("embeddings", kwargs)) + return {"kwargs": kwargs} + + +def test_llm_config_override_models(): + config = LLMConfig(api_key="key") + overridden = config.override(models={"extraction": "custom"}) + assert overridden.model_for("extraction") == "custom" + assert overridden.model_for("rerank") == DEFAULT_MODEL + + +def test_llm_client_applies_operation_defaults(monkeypatch): + calls = [] + + class DummyOpenAI: # pragma: no cover - construction only used in test + def __init__(self, **kwargs): + calls.append(kwargs) + self.responses = DummyResponses(calls) + self.embeddings = DummyEmbeddings(calls) + + monkeypatch.setattr("meshmind.llm_client._OpenAI", DummyOpenAI) + + config = LLMConfig( + api_key="token", + base_urls={"default": None, "extraction": "https://extract"}, + models={"default": DEFAULT_MODEL, "embedding": "embed-model"}, + ) + client = LLMClient(config) + client.responses.create(messages=[{"role": "user", "content": "hi"}]) + assert calls[0]["base_url"] == "https://extract" + responses_kwargs = calls[1][1] + assert responses_kwargs["model"] == DEFAULT_MODEL + + client.embeddings.create(input=["text"]) + assert calls[-1][0] == "embeddings" + embeddings_kwargs = calls[-1][1] + assert embeddings_kwargs["model"] == "embed-model" diff --git a/meshmind/tests/test_memgraph_driver.py b/meshmind/tests/test_memgraph_driver.py index 03f7e79..66de8cf 100644 --- a/meshmind/tests/test_memgraph_driver.py +++ b/meshmind/tests/test_memgraph_driver.py @@ -55,8 +55,23 @@ def commit(self): # Test upsert_edge does not raise edge_props = {'rel': 'value'} driver.upsert_edge('id1', 'REL', 'id2', edge_props) + # Test delete_triplet uses predicate sanitisation + driver.delete_triplet('id1', 'REL', 'id2') + assert 'DELETE r' in driver._cursor._last_query + # Test list_triplets returns parsed dicts + driver._cursor.description = [ + ('subject',), + ('predicate',), + ('object',), + ('namespace',), + ('metadata',), + ('reference_time',), + ] + driver._cursor._rows = [(('s',), ('p',), ('o',), ('ns',), ({'k': 'v'},), (None,))] + triplets = driver.list_triplets() + assert triplets and triplets[0]['subject'] == ('s',) # Test vector_search returns list # Use dummy record driver._cursor._rows = [([1.0], {'uuid': 'id1'})] out = driver.vector_search([1.0], top_k=1) - assert isinstance(out, list) \ No newline at end of file + assert isinstance(out, list) diff --git a/meshmind/tests/test_neo4j_driver.py b/meshmind/tests/test_neo4j_driver.py new file mode 100644 index 0000000..20cf3f7 --- /dev/null +++ b/meshmind/tests/test_neo4j_driver.py @@ -0,0 +1,40 @@ +import pytest + +from meshmind.db import neo4j_driver + + +def test_verify_connectivity_uses_driver(monkeypatch): + class DummySession: + def run(self, cypher, **params): + return [] + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + class DummyNeo4jDriver: + def __init__(self, uri, auth=None): + self.uri = uri + self.auth = auth + self.connected = False + + def session(self): + return DummySession() + + def verify_connectivity(self): + self.connected = True + return True + + def close(self): + pass + + class DummyGraphDatabase: + @staticmethod + def driver(uri, auth=None): + return DummyNeo4jDriver(uri, auth) + + monkeypatch.setattr(neo4j_driver, "GraphDatabase", DummyGraphDatabase) + driver = neo4j_driver.Neo4jGraphDriver("bolt://localhost:7687", "neo4j", "pass") + assert driver.verify_connectivity() is True diff --git a/meshmind/tests/test_observability.py b/meshmind/tests/test_observability.py new file mode 100644 index 0000000..f191d7b --- /dev/null +++ b/meshmind/tests/test_observability.py @@ -0,0 +1,24 @@ +import pytest + +from meshmind.core.observability import log_event, telemetry +from meshmind.pipeline.store import store_memories + + +@pytest.fixture(autouse=True) +def reset_telemetry(): + telemetry.reset() + yield + telemetry.reset() + + +def test_log_event_increments_counter(): + log_event("unit.test", value=1) + snapshot = telemetry.snapshot() + assert snapshot["counters"]["events.unit.test"] == 1 + + +def test_store_memories_tracks_metrics(memory_factory, in_memory_driver): + memories = [memory_factory("one"), memory_factory("two")] + store_memories(memories, in_memory_driver) + snapshot = telemetry.snapshot() + assert snapshot["counters"]["pipeline.store.memories.stored"] == 2 diff --git a/meshmind/tests/test_pipeline_extract.py b/meshmind/tests/test_pipeline_extract.py index 6b72966..1707cac 100644 --- a/meshmind/tests/test_pipeline_extract.py +++ b/meshmind/tests/test_pipeline_extract.py @@ -1,11 +1,10 @@ import json + import pytest -import openai from meshmind.client import MeshMind from meshmind.core.types import Memory from meshmind.core.embeddings import EncoderRegistry -from meshmind.db.memgraph_driver import MemgraphDriver class DummyEncoder: @@ -14,39 +13,27 @@ def encode(self, texts): return [[len(text)] for text in texts] -class DummyChoice: - def __init__(self, message): - self.message = message - class DummyResponse: - def __init__(self, arg_json): - func_call = {'arguments': arg_json} - self.choices = [DummyChoice({'function_call': func_call})] + def __init__(self, payload): + self.choices = [type('Choice', (), {'message': payload})] -@pytest.fixture(autouse=True) -def patch_openai(monkeypatch): - """Patch OpenAI client to use DummyChat for responses.""" - class DummyChat: +class DummyLLMClient: + class responses: # type: ignore[assignment] @staticmethod - def create(model, messages, functions, function_call): + def create(**kwargs): + messages = kwargs["messages"] names = [m['content'] for m in messages if m['role'] == 'user'] items = [{'name': n, 'entity_label': 'Memory'} for n in names] arg_json = json.dumps({'memories': items}) - return DummyResponse(arg_json) - class DummyModelClient: - def __init__(self): - self.responses = DummyChat - monkeypatch.setattr(openai, 'OpenAI', lambda *args, **kwargs: DummyModelClient()) - return None + return DummyResponse({'function_call': {'arguments': arg_json}}) def test_extract_memories_basic(tmp_path): # Register dummy encoder + EncoderRegistry.clear() EncoderRegistry.register('text-embedding-3-small', DummyEncoder()) - mm = MeshMind() - # override default llm_client to use dummy - mm.llm_client = openai.OpenAI() + mm = MeshMind(llm_client=DummyLLMClient()) # Run extraction texts = ['alpha', 'beta'] results = mm.extract_memories( @@ -64,16 +51,17 @@ def test_extract_memories_basic(tmp_path): assert mem.embedding == [len(text)] -def test_extract_invalid_label(monkeypatch): - # Monkeypatch to return an entry with invalid label - def bad_create(*args, **kwargs): - arg_json = json.dumps({'memories': [{'name': 'x', 'entity_label': 'Bad'}]}) - return DummyResponse(arg_json) - from openai import OpenAI - llm_client = OpenAI() - monkeypatch.setattr(llm_client.responses, 'create', bad_create) +def test_extract_invalid_label(): + class BadLLMClient: + class responses: # type: ignore[assignment] + @staticmethod + def create(**kwargs): + arg_json = json.dumps({'memories': [{'name': 'x', 'entity_label': 'Bad'}]}) + return DummyResponse({'function_call': {'arguments': arg_json}}) + + EncoderRegistry.clear() EncoderRegistry.register('text-embedding-3-small', DummyEncoder()) - mm = MeshMind(llm_client=llm_client) + mm = MeshMind(llm_client=BadLLMClient()) with pytest.raises(ValueError) as e: mm.extract_memories( instructions='Extract:', @@ -81,4 +69,4 @@ def bad_create(*args, **kwargs): entity_types=[Memory], content=['x'], ) - assert 'Invalid entity_label' in str(e.value) \ No newline at end of file + assert 'Invalid entity_label' in str(e.value) diff --git a/meshmind/tests/test_pipeline_maintenance.py b/meshmind/tests/test_pipeline_maintenance.py index fccb78d..5945af8 100644 --- a/meshmind/tests/test_pipeline_maintenance.py +++ b/meshmind/tests/test_pipeline_maintenance.py @@ -1,71 +1,68 @@ -try: - import pytest -except ImportError: - # Define fallback for pytest.approx - class pytest: - @staticmethod - def approx(x): - return x -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone + +import pytest -from meshmind.pipeline.expire import expire_memories -from meshmind.pipeline.consolidate import consolidate_memories -from meshmind.pipeline.compress import compress_memories from meshmind.core.types import Memory +from meshmind.pipeline.consolidate import consolidate_memories +from meshmind.pipeline.expire import expire_memories class DummyManager: def __init__(self, memories): self._memories = memories - self.deleted = [] + self.deleted: list[str] = [] def list_memories(self): - return self._memories + return list(self._memories) def delete_memory(self, memory_id): self.deleted.append(str(memory_id)) - # Methods to satisfy manager interface - def update_memory(self, memory): + def update_memory(self, memory): # pragma: no cover - interface placeholder pass -def make_memory(name, created_at, ttl): +def make_memory(name: str, created_at: datetime, ttl: int | None = None) -> Memory: mem = Memory(namespace="ns", name=name, entity_label="Test") mem.created_at = created_at - mem.reference_time = None mem.ttl_seconds = ttl return mem def test_expire_memories(monkeypatch): - now = datetime(2025, 1, 1, 12, 0, 0) - # Create memories: one expired, one not - m1 = make_memory("a", now - timedelta(seconds=3600), ttl=1800) - m2 = make_memory("b", now - timedelta(seconds=600), ttl=1800) - manager = DummyManager([m1, m2]) - # Monkeypatch datetime.utcnow + now = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc) + expired = make_memory("old", now - timedelta(hours=2), ttl=1800) + active = make_memory("fresh", now - timedelta(minutes=5), ttl=1800) + manager = DummyManager([expired, active]) + class DummyDateTime: @classmethod def utcnow(cls): return now - monkeypatch.setattr('meshmind.pipeline.expire.datetime', DummyDateTime) - expired = expire_memories(manager) - assert str(m1.uuid) in expired - assert str(m2.uuid) not in expired - assert str(m1.uuid) in manager.deleted - - -def test_consolidate_memories(): - # Two memories with same name, different importance - m1 = Memory(namespace="ns", name="x", entity_label="Test") - m2 = Memory(namespace="ns", name="x", entity_label="Test") - m1.importance = 0.5 - m2.importance = 1.0 - m3 = Memory(namespace="ns", name="y", entity_label="Test") - result = consolidate_memories([m1, m2, m3]) - # Expect one for 'x' with higher importance and one for 'y' - names = {m.name for m in result} - assert names == {"x", "y"} - selected_x = next(m for m in result if m.name == "x") - assert selected_x.importance == pytest.approx(1.0) + + @classmethod + def now(cls, tz=None): + if tz is None: + return now.replace(tzinfo=None) + return now.astimezone(tz) + + monkeypatch.setattr("meshmind.pipeline.expire.datetime", DummyDateTime) + + removed = expire_memories(manager) + + assert str(expired.uuid) in removed + assert str(active.uuid) not in removed + assert str(expired.uuid) in manager.deleted + + +def test_consolidate_memories_returns_outcomes(): + primary = Memory(namespace="ns", name="Alice", entity_label="Test", metadata={"content": "likes tea"}) + duplicate = Memory(namespace="ns", name="Alice", entity_label="Test", metadata={"content": "likes coffee"}) + primary.importance = 0.8 + duplicate.importance = 0.2 + + plan = consolidate_memories([primary, duplicate]) + assert len(plan.outcomes) == 1 + outcome = plan.outcomes[0] + assert outcome.updated.metadata["consolidated_summary"] + assert outcome.removed_ids diff --git a/meshmind/tests/test_pipeline_preprocess_store.py b/meshmind/tests/test_pipeline_preprocess_store.py index 2d10abd..3ebef61 100644 --- a/meshmind/tests/test_pipeline_preprocess_store.py +++ b/meshmind/tests/test_pipeline_preprocess_store.py @@ -1,26 +1,66 @@ import pytest -from meshmind.pipeline.preprocess import deduplicate, score_importance, compress -from meshmind.pipeline.store import store_memories +from datetime import datetime, timezone + +from meshmind.pipeline.consolidate import consolidate_memories +from meshmind.pipeline.preprocess import ( + compress, + deduplicate, + score_importance, +) +from meshmind.pipeline.store import store_memories, store_triplets from meshmind.api.memory_manager import MemoryManager -from meshmind.core.types import Memory +from meshmind.core.types import Memory, Triplet +from meshmind.models.registry import PredicateRegistry +from meshmind.core.observability import telemetry class DummyDriver: def __init__(self): self.entities = [] self.deleted = [] + self.edges = [] + self.deleted_edges = [] def upsert_entity(self, label, name, props): - self.entities.append((label, name, props)) + self.entities.append((label, name, dict(props))) + + def upsert_edge(self, subj, pred, obj, props): + self.edges.append((subj, pred, obj, props)) def delete(self, uuid): self.deleted.append(uuid) + self.entities = [entry for entry in self.entities if str(entry[2].get("uuid")) != str(uuid)] + + def delete_triplet(self, subj, pred, obj): + self.deleted_edges.append((subj, pred, obj)) def find(self, cypher, params): # Return empty for simplicity return [] + def list_triplets(self, namespace=None): + return [] + + def get_entity(self, uuid): + for _, _, props in self.entities: + if str(props.get("uuid")) == str(uuid): + return dict(props) + return None + + def list_entities(self, namespace=None, entity_labels=None, *, offset=0, limit=None): + results = [] + for _, _, props in self.entities: + if namespace is None or props.get("namespace") == namespace: + if entity_labels and props.get("entity_label") not in set(entity_labels): + continue + results.append(dict(props)) + if limit is None: + return results[offset:] + if limit <= 0: + return [] + return results[offset : offset + limit] + def make_memory(name: str) -> Memory: return Memory(namespace="ns", name=name, entity_label="Test") @@ -35,11 +75,31 @@ def test_deduplicate_removes_duplicates(): assert {m.name for m in result} == {"a", "b"} -def test_score_importance_sets_default(): - m = make_memory("x") - m.importance = None - scored = score_importance([m]) - assert scored[0].importance == pytest.approx(1.0) +def test_score_importance_heuristic_variation(): + recent = make_memory("Urgent server outage 500 error") + recent.metadata = {"content": "Service unavailable 500"} + recent.reference_time = datetime.now(timezone.utc) + + mundane = make_memory("Note") + mundane.metadata = {"content": "write unit tests"} + + scored = score_importance([recent, mundane]) + values = [mem.importance for mem in scored] + assert all(val is not None for val in values) + assert scored[0].importance != scored[1].importance + assert max(values) <= 5.0 + + +def test_score_importance_records_metrics(): + telemetry.reset() + m1 = make_memory("Outage 500") + m1.metadata = {"content": "Service returned 500 error"} + m1.reference_time = datetime.now(timezone.utc) + score_importance([m1]) + snapshot = telemetry.snapshot() + gauges = snapshot["gauges"] + assert "importance.mean" in gauges + assert gauges["importance.count"] >= 1.0 def test_compress_noop(): @@ -47,6 +107,21 @@ def test_compress_noop(): assert compress([m])[0] is m +def test_consolidate_memories_merges_duplicates(): + base = make_memory("Alice") + duplicate = make_memory("Alice") + duplicate.metadata = {"content": "Alice likes tea"} + base.metadata = {"content": "Alice likes coffee"} + duplicate.importance = 0.2 + base.importance = 0.9 + + plan = consolidate_memories([base, duplicate]) + assert len(plan.outcomes) == 1 + outcome = plan.outcomes[0] + assert "consolidated_summary" in outcome.updated.metadata + assert outcome.removed_ids + + def test_store_memories_calls_driver(): d = DummyDriver() m1 = make_memory("node1") @@ -57,6 +132,21 @@ def test_store_memories_calls_driver(): assert d.entities[0][1] == "node1" +def test_store_triplets_registers_predicate(): + PredicateRegistry.clear() + d = DummyDriver() + triplet = Triplet( + subject="s", + predicate="RELATES", + object="o", + namespace="ns", + entity_label="Relation", + ) + store_triplets([triplet], d) + assert d.edges and d.edges[0][1] == "RELATES" + assert "RELATES" in PredicateRegistry.all() + + def test_memory_manager_add_update_delete(): d = DummyDriver() mgr = MemoryManager(d) @@ -76,6 +166,24 @@ def test_memory_manager_add_update_delete(): # list returns empty or list lst = mgr.list_memories() assert isinstance(lst, list) + assert mgr.list_memories(entity_labels=["Other"]) == [] + + +def test_memory_manager_triplet_roundtrip(): + d = DummyDriver() + mgr = MemoryManager(d) + triplet = Triplet( + subject="s", + predicate="RELATES", + object="o", + namespace="ns", + entity_label="Relation", + ) + mgr.add_triplet(triplet) + assert d.edges + mgr.delete_triplet(triplet.subject, triplet.predicate, triplet.object) + assert d.deleted_edges + assert mgr.list_triplets() == [] def test_deduplicate_by_embedding_similarity(): # Two memories with similar embeddings should be deduplicated @@ -89,4 +197,4 @@ def test_deduplicate_by_embedding_similarity(): assert len(result_high) == 1 # With low threshold, keep both result_low = deduplicate([m1, m2], threshold=0.1) - assert len(result_low) == 2 \ No newline at end of file + assert len(result_low) == 2 diff --git a/meshmind/tests/test_retrieval.py b/meshmind/tests/test_retrieval.py index 66abd33..728243e 100644 --- a/meshmind/tests/test_retrieval.py +++ b/meshmind/tests/test_retrieval.py @@ -1,76 +1,127 @@ import pytest -from meshmind.core.types import Memory, SearchConfig -from meshmind.retrieval.bm25 import bm25_search -from meshmind.retrieval.fuzzy import fuzzy_search +from meshmind.client import MeshMind +from meshmind.core.types import SearchConfig +from meshmind.db.in_memory_driver import InMemoryGraphDriver +from meshmind.retrieval import ( + apply_reranker, + llm_rerank, + search, + search_bm25, + search_exact, + search_fuzzy, + search_regex, + search_vector, +) from meshmind.retrieval.hybrid import hybrid_search -from meshmind.retrieval.search import search, search_bm25, search_fuzzy - - -def make_memory(name: str) -> Memory: - return Memory(namespace="ns", name=name, entity_label="Test") - - -@pytest.fixture(autouse=True) -def add_embeddings(): - # Assign dummy embeddings equal to length of name - def _hook(mem: Memory): - mem.embedding = [len(mem.name)] - return mem - Memory.pre_init = _hook - yield - delattr(Memory, 'pre_init') - - -def test_bm25_search(): - docs = [make_memory("apple pie"), make_memory("banana split"), make_memory("cherry tart")] - results = bm25_search("apple", docs, top_k=2) - # Expect 'apple pie' first - assert results and results[0][0].name == "apple pie" - assert results[0][1] > 0 - - -def test_fuzzy_search(): - docs = [make_memory("apple pie"), make_memory("banana split")] - results = fuzzy_search("apple pie", docs, top_k=2) - assert results and results[0][0].name == "apple pie" - assert 0 < results[0][1] <= 1.0 - - -def test_hybrid_search(): - # Setup memories - m1 = make_memory("apple") - m2 = make_memory("banana") - m1.embedding = [1.0] - m2.embedding = [0.0] - docs = [m1, m2] - config = SearchConfig(encoder="dummy", top_k=2, hybrid_weights=(0.5, 0.5)) - # Register dummy encoder that returns [1] for 'apple' and [0] for 'banana' - class DummyEncoder: - def encode(self, texts): - return [[1.0] if "apple" in t else [0.0] for t in texts] - from meshmind.core.embeddings import EncoderRegistry - EncoderRegistry.register("dummy", DummyEncoder()) - results = hybrid_search("apple", docs, config) - # apple should have highest hybrid score - assert results[0][0].name == "apple" - - -def test_search_dispatcher(): - m1 = make_memory("apple") - m2 = make_memory("banana") - m1.embedding = [1.0] - m2.embedding = [0.0] - docs = [m1, m2] - from meshmind.core.embeddings import EncoderRegistry - class DummyEncoder: - def encode(self, texts): return [[1.0] if "apple" in t else [0.0] for t in texts] - EncoderRegistry.register("dummy", DummyEncoder()) - config = SearchConfig(encoder="dummy", top_k=1, hybrid_weights=(0.5,0.5)) - res = search("apple", docs, namespace="ns", entity_labels=["Test"], config=config) - assert len(res) == 1 and res[0].name == "apple" - # BM25 and fuzzy via dispatcher - res2 = search_bm25("banana", docs) - assert res2 and res2[0].name == "banana" - res3 = search_fuzzy("banana", docs) - assert res3 and res3[0].name == "banana" \ No newline at end of file + + +def test_bm25_search(memory_factory): + docs = [ + memory_factory("apple pie"), + memory_factory("banana split"), + memory_factory("cherry tart"), + ] + results = search_bm25("apple", docs, top_k=2) + assert results and results[0].name == "apple pie" + + +def test_fuzzy_search(memory_factory): + docs = [memory_factory("apple pie"), memory_factory("banana split")] + results = search_fuzzy("apple pie", docs, top_k=2) + assert results and results[0].name == "apple pie" + + +def test_hybrid_search(memory_factory, dummy_encoder): + m1 = memory_factory("apple", embedding=[1.0]) + m2 = memory_factory("banana", embedding=[0.0]) + config = SearchConfig(encoder=dummy_encoder, top_k=2, hybrid_weights=(0.5, 0.5)) + ranked = hybrid_search("apple", [m1, m2], config) + assert ranked[0][0].name == "apple" + + +def test_vector_search(memory_factory, dummy_encoder): + m1 = memory_factory("apple", embedding=[1.0]) + m2 = memory_factory("banana", embedding=[0.0]) + config = SearchConfig(encoder=dummy_encoder, top_k=1) + results = search_vector("apple", [m1, m2], config=config) + assert results == [m1] + + +def test_regex_search(memory_factory): + docs = [ + memory_factory("Visit Paris", metadata={"city": "Paris"}), + memory_factory("Visit Berlin", metadata={"city": "Berlin"}), + ] + results = search_regex("paris", docs, top_k=5) + assert len(results) == 1 and results[0].name == "Visit Paris" + + +def test_exact_search(memory_factory): + docs = [ + memory_factory("Python"), + memory_factory("Rust", metadata={"language": "Rust"}), + ] + results = search_exact("rust", docs, fields=["metadata"], case_sensitive=False) + assert results and results[0].name == "Rust" + + +def test_search_dispatcher_with_rerank(memory_factory, dummy_encoder): + m1 = memory_factory("apple", embedding=[1.0]) + m2 = memory_factory("banana", embedding=[0.1]) + docs = [m2, m1] + config = SearchConfig(encoder=dummy_encoder, top_k=2, rerank_k=2) + + class DummyLLM: + class responses: + @staticmethod + def create(**kwargs): + return type( + "Resp", + (), + { + "output": [ + type( + "Out", + (), + { + "content": [ + type("Text", (), {"text": '{"order": [1, 0]}'}) + ] + }, + ) + ] + }, + ) + + reranked = search( + "apple", + docs, + config=config, + reranker=lambda q, c, k: llm_rerank(q, c, DummyLLM(), k, model="dummy"), + ) + assert reranked[0].name == "apple" + + +def test_apply_reranker_default(memory_factory): + docs = [memory_factory("alpha"), memory_factory("beta")] + ranked = apply_reranker("alpha", docs, top_k=1) + assert ranked == [docs[0]] + + +def test_llm_rerank_failure(memory_factory): + docs = [memory_factory("alpha"), memory_factory("beta")] + result = llm_rerank("alpha", docs, llm_client=None, top_k=2) + assert len(result) == 2 + + +def test_client_search_uses_graph_when_memories_none(dummy_encoder, memory_factory): + driver = InMemoryGraphDriver() + client = MeshMind(llm_client=object(), embedding_model=dummy_encoder, graph_driver=driver) + memory = memory_factory("apple", embedding=[1.0]) + client.create_memory(memory) + config = SearchConfig(encoder=dummy_encoder, top_k=1) + + results = client.search("apple", memories=None, namespace="ns", config=config) + + assert results and results[0].name == "apple" diff --git a/meshmind/tests/test_service_interfaces.py b/meshmind/tests/test_service_interfaces.py new file mode 100644 index 0000000..5d4a373 --- /dev/null +++ b/meshmind/tests/test_service_interfaces.py @@ -0,0 +1,230 @@ +from uuid import UUID + +from meshmind.api.grpc import ( + GrpcServiceStub, + IngestMemoriesRequest, + IngestTripletsRequest, + MemoryCountsRequest, + SearchRequest, +) +from meshmind.api.rest import RestAPIStub +from meshmind.api.service import MemoryPayload, SearchPayload, TripletPayload +from meshmind.core.types import Memory + + +def _memory(name: str) -> MemoryPayload: + return MemoryPayload(namespace="test", name=name, entity_label="Note") + + +def test_memory_service_ingest_and_search(memory_service, dummy_encoder): + payloads = [_memory("apple"), _memory("banana")] + uuids = memory_service.ingest_memories(payloads) + assert len(uuids) == 2 + + captured: dict[str, object] = {} + + sample = Memory( + uuid=UUID(uuids[0]), + namespace="test", + name="apple", + entity_label="Note", + content="apple", + ) + + def fake_list(namespace=None, entity_labels=None, **kwargs): # noqa: ANN001 + captured["namespace"] = namespace + captured["entity_labels"] = entity_labels + captured["kwargs"] = kwargs + return [sample] + + original = memory_service.manager.list_memories + memory_service.manager.list_memories = fake_list # type: ignore[assignment] + + request = SearchPayload(query="apple", namespace="test", encoder=dummy_encoder, top_k=1) + results = memory_service.search(request) + memory_service.manager.list_memories = original # restore + + assert results and isinstance(results[0], Memory) + assert captured["namespace"] == "test" + assert captured["entity_labels"] is None or captured["entity_labels"] == [] + assert captured["kwargs"]["query"] == "apple" + assert captured["kwargs"]["use_search"] is True + assert captured["kwargs"]["limit"] is not None + + +def test_rest_stub_routes(memory_service, dummy_encoder): + app = RestAPIStub(memory_service) + response = app.dispatch( + "POST", + "/memories", + {"memories": [_memory("alpha").model_dump()]}, + ) + assert "uuids" in response + + search_response = app.dispatch( + "POST", + "/search", + {"query": "alpha", "namespace": "test", "encoder": dummy_encoder, "top_k": 1}, + ) + assert search_response["results"] + + memory_service.llm_client.calls.clear() + override_response = app.dispatch( + "POST", + "/search", + { + "query": "alpha", + "namespace": "test", + "encoder": dummy_encoder, + "top_k": 1, + "use_llm_rerank": True, + "llm_models": {"rerank": "rerank-dev"}, + "llm_base_urls": {"rerank": "https://llm.example/rerank"}, + "llm_api_key": "override-key", + }, + ) + assert override_response["results"] + assert memory_service.llm_client.calls + last_call = memory_service.llm_client.calls[-1] + assert last_call["model"] == "rerank-dev" + assert last_call["base_url"] == "https://llm.example/rerank" + assert memory_service.llm_client.last_override["models"]["rerank"] == "rerank-dev" + + filtered = app.dispatch( + "GET", + "/memories", + {"namespace": "test", "entity_labels": ["Note"]}, + ) + assert filtered["memories"] + + filtered_none = app.dispatch( + "GET", + "/memories", + {"namespace": "test", "entity_labels": ["Task"]}, + ) + assert filtered_none["memories"] == [] + + counts = app.dispatch("GET", "/memories/counts", {"namespace": "test"}) + assert counts["counts"]["test"]["Note"] >= 1 + + +def test_memory_service_list_memories_forwards_kwargs(memory_service): + captured: dict[str, object] = {} + + def fake_list(namespace=None, entity_labels=None, **kwargs): # noqa: ANN001 + captured["namespace"] = namespace + captured["entity_labels"] = entity_labels + captured["kwargs"] = kwargs + return [] + + original = memory_service.manager.list_memories + memory_service.manager.list_memories = fake_list # type: ignore[assignment] + + memory_service.list_memories( + namespace="demo", + entity_labels=["Note"], + offset=5, + limit=10, + query="python", + use_search=False, + ) + + memory_service.manager.list_memories = original # restore + + assert captured["namespace"] == "demo" + assert captured["entity_labels"] == ["Note"] + assert captured["kwargs"] == {"offset": 5, "limit": 10, "query": "python", "use_search": False} + + +def test_memory_service_search_applies_llm_overrides(memory_service, dummy_encoder): + sample = Memory( + uuid=UUID(int=1), + namespace="test", + name="apple", + entity_label="Note", + content="apple", + ) + + def fake_list(namespace=None, entity_labels=None, **kwargs): # noqa: ANN001 + return [sample] + + original = memory_service.manager.list_memories + memory_service.manager.list_memories = fake_list # type: ignore[assignment] + + payload = SearchPayload( + query="apple", + namespace="test", + encoder=dummy_encoder, + top_k=1, + use_llm_rerank=True, + llm_models={"rerank": "direct-rerank"}, + llm_base_urls={"default": "https://llm.example/default"}, + llm_api_key="override-key", + ) + + memory_service.llm_client.calls.clear() + results = memory_service.search(payload) + memory_service.manager.list_memories = original + + assert results + assert memory_service.llm_client.calls + direct_call = memory_service.llm_client.calls[-1] + assert direct_call["model"] == "direct-rerank" + assert direct_call["base_url"] == "https://llm.example/default" + assert memory_service.llm_client.last_override["base_urls"]["default"] == "https://llm.example/default" + + +def test_grpc_stub(memory_service, dummy_encoder): + grpc = GrpcServiceStub(memory_service) + request = IngestMemoriesRequest(memories=[_memory("cherry").model_dump()]) + resp = grpc.IngestMemories(request) + assert resp.uuids + + search_resp = grpc.Search( + SearchRequest( + query="cherry", + namespace="test", + encoder=dummy_encoder, + top_k=1, + entity_labels=["Note"], + ) + ) + assert search_resp.results + + memory_service.llm_client.calls.clear() + override_resp = grpc.Search( + SearchRequest( + query="cherry", + namespace="test", + encoder=dummy_encoder, + top_k=1, + entity_labels=["Note"], + use_llm_rerank=True, + llm_models={"rerank": "rerank-grpc"}, + llm_base_urls={"rerank": "https://llm.example/grpc"}, + llm_api_key="override-key", + ) + ) + assert override_resp.results + assert memory_service.llm_client.calls + grpc_call = memory_service.llm_client.calls[-1] + assert grpc_call["model"] == "rerank-grpc" + assert grpc_call["base_url"] == "https://llm.example/grpc" + + counts_resp = grpc.MemoryCounts(MemoryCountsRequest(namespace="test")) + assert counts_resp.counts["test"]["Note"] >= 1 + + triplet_resp = grpc.IngestTriplets( + IngestTripletsRequest( + triplets=[ + TripletPayload( + subject=resp.uuids[0], + predicate="linked_to", + object=resp.uuids[0], + namespace="test", + entity_label="Relation", + ).model_dump() + ] + ) + ) + assert triplet_resp.stored == 1 diff --git a/meshmind/tests/test_setup_scripts.py b/meshmind/tests/test_setup_scripts.py new file mode 100644 index 0000000..0d05725 --- /dev/null +++ b/meshmind/tests/test_setup_scripts.py @@ -0,0 +1,31 @@ +"""Smoke tests for environment provisioning scripts.""" +from __future__ import annotations + +import os +import subprocess +from pathlib import Path + +import pytest + + +@pytest.mark.parametrize("script_name", ["install_setup.sh", "maintenance_setup.sh"]) +def test_setup_scripts_validate_optional_packages(script_name: str) -> None: + repo_root = Path(__file__).resolve().parents[2] + script_path = repo_root / "run" / script_name + env = os.environ.copy() + env.update({ + "MESH_SKIP_SYSTEM_PACKAGES": "1", + "MESH_SKIP_PYTHON_SYNC": "1", + }) + result = subprocess.run( + ["bash", str(script_path)], + cwd=repo_root, + env=env, + capture_output=True, + text=True, + check=True, + ) + stdout = result.stdout + assert "Validated optional packages: fastapi, neo4j, pymgclient" in stdout + assert "Skipping system package installation" in stdout + assert "Skipping Python dependency sync" in stdout diff --git a/meshmind/tests/test_tasks_scheduled.py b/meshmind/tests/test_tasks_scheduled.py new file mode 100644 index 0000000..16bb7a1 --- /dev/null +++ b/meshmind/tests/test_tasks_scheduled.py @@ -0,0 +1,44 @@ +import pytest +import pytest + +from meshmind.core.types import Memory +from meshmind.tasks import scheduled + + +class StubManager: + def __init__(self, memories): + self._memories = memories + self.updated = [] + self.deleted = [] + + def list_memories(self): + return list(self._memories) + + def update_memory(self, memory): + self.updated.append(memory) + + def delete_memory(self, memory_id): + self.deleted.append(str(memory_id)) + + +@pytest.fixture(autouse=True) +def reset_manager(): + scheduled._reset_manager() + yield + scheduled._reset_manager() + + +def test_consolidate_task_persists_updates(monkeypatch): + mem_a = Memory(namespace="ns", name="Alice", entity_label="Person", metadata={"content": "Alice likes tea"}) + mem_b = Memory(namespace="ns", name="Alice", entity_label="Person", metadata={"content": "Alice likes coffee"}) + mem_b.importance = 0.9 + + manager = StubManager([mem_a, mem_b]) + monkeypatch.setattr(scheduled, "_MANAGER", manager, raising=False) + + stats = scheduled.consolidate_task() + + assert stats["merged"] == 1 + assert stats["removed"] >= 1 + assert manager.updated + assert manager.deleted diff --git a/pyproject.toml b/pyproject.toml index 062f6d7..a73c7fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "meshmind" version = "0.1.0" description = "AI Agent Memory management system using LLMs and graph databases" readme = "README.md" -requires-python = ">=3.13" +requires-python = ">=3.11,<3.13" dependencies = [ "openai>=1.78.0", "pydantic>=2.11.4", @@ -17,8 +17,33 @@ dependencies = [ "sentence-transformers>=4.1.0", "typing-extensions>=4.13.2", "tiktoken>=0.9.0", + "fastapi>=0.115.6", + "uvicorn[standard]>=0.34.0", + "neo4j>=5.26.0", "pymgclient>=1.4.0", + "redis>=5.0.0", ] [project.scripts] meshmind = "meshmind.cli.__main__:main" + +[project.optional-dependencies] +dev = [ + "ruff>=0.7.3", + "pyright>=1.1.386", + "typeguard>=4.3.0", + "toml-sort>=0.23.1", + "yamllint>=1.35.1", +] +docs = [ + "mkdocs>=1.6.1", + "mkdocs-material>=9.5.39", +] +testing = [ + "pytest-cov>=5.0.0", + "httpx>=0.28.1", + "fastapi>=0.115.0", + "uvicorn>=0.32.0", + "neo4j>=5.25.0", + "pymgclient>=1.5.1", +] diff --git a/run/install_setup.sh b/run/install_setup.sh new file mode 100755 index 0000000..6ad4ff9 --- /dev/null +++ b/run/install_setup.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Provision a fresh MeshMind development environment with full optional coverage. +# This script assumes outbound internet access and root privileges. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +if [[ "${MESH_SKIP_SYSTEM_PACKAGES:-0}" == "1" ]]; then + echo "[${SCRIPT_NAME}] Skipping system package installation (MESH_SKIP_SYSTEM_PACKAGES=1)" +else + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + cmake \ + curl \ + git \ + pkg-config \ + python3 \ + python3-dev \ + python3-venv \ + python3-pip \ + libssl-dev \ + libffi-dev \ + libkrb5-dev \ + libsasl2-dev \ + libpq-dev \ + libopenblas-dev \ + liblapack-dev +fi + +if [[ "${MESH_SKIP_PYTHON_SYNC:-0}" != "1" ]]; then + if ! command -v uv >/dev/null 2>&1; then + curl -LsSf https://astral.sh/uv/install.sh | sh -s -- --install-dir /usr/local/bin --force + fi +fi + +cd "${REPO_ROOT}" + +python - <<'PY' +import sys +from pathlib import Path + +try: + import tomllib # type: ignore +except ModuleNotFoundError: # pragma: no cover - Python <3.11 fallback + import tomli as tomllib # type: ignore + +data = tomllib.loads(Path("pyproject.toml").read_text()) +extras = data.get("project", {}).get("optional-dependencies", {}) +required = {"neo4j", "pymgclient", "fastapi"} +available: set[str] = set() +for group in ("dev", "docs", "testing"): + for dep in extras.get(group, []): + normalized = dep.split(";", 1)[0].split("[", 1)[0] + normalized = normalized.split("==", 1)[0].split(">=", 1)[0].strip() + if normalized: + available.add(normalized) +missing = required - available +if missing: + sys.stderr.write(f"Missing optional packages in extras: {', '.join(sorted(missing))}\n") + sys.exit(1) +print("Validated optional packages: " + ", ".join(sorted(required))) +PY + +if [[ "${MESH_SKIP_PYTHON_SYNC:-0}" == "1" ]]; then + echo "[${SCRIPT_NAME}] Skipping Python dependency sync (MESH_SKIP_PYTHON_SYNC=1)" +else + if [ -f "${REPO_ROOT}/uv.lock" ]; then + uv pip sync --system "${REPO_ROOT}/uv.lock" + else + uv pip install --system ".[dev,docs,testing]" + fi +fi + +# Ensure scripts are executable for convenience. +chmod +x run/*.sh || true + diff --git a/run/maintenance_setup.sh b/run/maintenance_setup.sh new file mode 100755 index 0000000..b20fb35 --- /dev/null +++ b/run/maintenance_setup.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Refresh an existing MeshMind environment restored from cache. +# Installs missing system packages, syncs Python dependencies, and +# prepares the workspace for end-to-end testing. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" + +if [[ "${MESH_SKIP_SYSTEM_PACKAGES:-0}" == "1" ]]; then + echo "[${SCRIPT_NAME}] Skipping system package installation (MESH_SKIP_SYSTEM_PACKAGES=1)" +else + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + build-essential \ + cmake \ + curl \ + git \ + pkg-config \ + python3 \ + python3-dev \ + python3-venv \ + python3-pip \ + libssl-dev \ + libffi-dev \ + libkrb5-dev \ + libsasl2-dev \ + libpq-dev \ + libopenblas-dev \ + liblapack-dev +fi + +if [[ "${MESH_SKIP_PYTHON_SYNC:-0}" != "1" ]]; then + if ! command -v uv >/dev/null 2>&1; then + curl -LsSf https://astral.sh/uv/install.sh | sh -s -- --install-dir /usr/local/bin --force + fi +fi + +cd "${REPO_ROOT}" + +python - <<'PY' +import sys +from pathlib import Path + +try: + import tomllib # type: ignore +except ModuleNotFoundError: # pragma: no cover - Python <3.11 fallback + import tomli as tomllib # type: ignore + +data = tomllib.loads(Path("pyproject.toml").read_text()) +extras = data.get("project", {}).get("optional-dependencies", {}) +required = {"neo4j", "pymgclient", "fastapi"} +available: set[str] = set() +for group in ("dev", "docs", "testing"): + for dep in extras.get(group, []): + normalized = dep.split(";", 1)[0].split("[", 1)[0] + normalized = normalized.split("==", 1)[0].split(">=", 1)[0].strip() + if normalized: + available.add(normalized) +missing = required - available +if missing: + sys.stderr.write(f"Missing optional packages in extras: {', '.join(sorted(missing))}\n") + sys.exit(1) +print("Validated optional packages: " + ", ".join(sorted(required))) +PY + +if [[ "${MESH_SKIP_PYTHON_SYNC:-0}" == "1" ]]; then + echo "[${SCRIPT_NAME}] Skipping Python dependency sync (MESH_SKIP_PYTHON_SYNC=1)" +else + if [ -f "${REPO_ROOT}/uv.lock" ]; then + uv pip sync --system "${REPO_ROOT}/uv.lock" + else + uv pip install --system ".[dev,docs,testing]" + fi +fi + +# Update git submodules (if any) to keep tooling consistent. +if [ -f .gitmodules ]; then + git submodule update --init --recursive +fi + diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/check_docs_sync.py b/scripts/check_docs_sync.py new file mode 100755 index 0000000..a2495a9 --- /dev/null +++ b/scripts/check_docs_sync.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Ensure documentation updates accompany relevant code changes.""" + +from __future__ import annotations + +import argparse +import subprocess +import sys +from pathlib import Path +from typing import Dict, Iterable, List, Set + +PROJECT_ROOT = Path(__file__).resolve().parent.parent + +DOC_MAP: Dict[str, List[str]] = { + "meshmind/api": ["docs/api.md", "docs/operations.md"], + "meshmind/retrieval": ["docs/retrieval.md"], + "meshmind/db": ["docs/persistence.md"], + "meshmind/pipeline": ["docs/pipelines.md"], + "meshmind/core": ["docs/architecture.md", "docs/development.md"], + "meshmind/llm_client.py": ["docs/architecture.md", "docs/configuration.md"], + "meshmind/cli": ["docs/operations.md"], + "meshmind/tasks": ["docs/operations.md", "docs/telemetry.md"], + "meshmind/tests/docker": ["SETUP.md", "docs/operations.md"], + "docker-compose": ["SETUP.md", "docs/operations.md", "ENVIRONMENT_NEEDS.md"], + "Dockerfile": ["SETUP.md", "ENVIRONMENT_NEEDS.md"], +} + +DOC_PREFIXES = ("docs/",) +ALWAYS_DOC_FILES = { + "README.md", + "PROJECT.md", + "SOT.md", + "PLAN.md", + "RECOMMENDATIONS.md", + "RESUME_NOTES.md", + "SETUP.md", + "ENVIRONMENT_NEEDS.md", + "NEEDED_FOR_TESTING.md", +} + + +def _git_diff(base: str) -> List[str]: + cmd = ["git", "diff", "--name-only", f"{base}..HEAD"] + result = subprocess.run(cmd, capture_output=True, text=True, check=False) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "git diff failed") + return [line.strip() for line in result.stdout.splitlines() if line.strip()] + + +def _docs_touched(files: Iterable[str]) -> Set[str]: + docs: Set[str] = set() + for path in files: + if path.startswith(DOC_PREFIXES) or path in ALWAYS_DOC_FILES: + docs.add(path) + return docs + + +def check_docs(base: str) -> int: + changed = _git_diff(base) + if not changed: + return 0 + + docs_changed = _docs_touched(changed) + missing: Dict[str, Dict[str, Iterable[str]]] = {} + + for prefix, required_docs in DOC_MAP.items(): + touched = [path for path in changed if path.startswith(prefix)] + if not touched: + continue + if any(doc in docs_changed for doc in required_docs): + continue + missing[prefix] = {"files": touched, "docs": required_docs} + + if missing: + print("Documentation updates required for the following areas:", file=sys.stderr) + for prefix, info in missing.items(): + print(f"- {prefix}", file=sys.stderr) + print(f" touched: {', '.join(info['files'])}", file=sys.stderr) + print(f" expected docs: {', '.join(info['docs'])}", file=sys.stderr) + return 1 + + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--base", + default="origin/main", + help="Git reference to diff against (default: origin/main)", + ) + args = parser.parse_args() + + try: + return check_docs(args.base) + except RuntimeError as exc: # pragma: no cover - git errors depend on CI setup + print(str(exc), file=sys.stderr) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/uv.lock b/uv.lock index c20d1b4..981aa3c 100644 --- a/uv.lock +++ b/uv.lock @@ -1,1267 +1,2432 @@ -version = 1 -requires-python = ">=3.13" +# This file was autogenerated by uv via the following command: +# uv pip compile pyproject.toml --extra dev --extra docs --extra testing --format pylock.toml -o pylock.toml +lock-version = "1.0" +created-by = "uv" +requires-python = ">=3.13.3" -[[package]] +[[packages]] name = "amqp" version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "vine" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432", size = 129013 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2", size = 50944 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/79/fc/ec94a357dfc6683d8c86f8b4cfa5416a4c36b28052ec8260c77aca96a443/amqp-5.3.1.tar.gz", upload-time = 2024-11-12T19:55:44Z, size = 129013, hashes = { sha256 = "cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/26/99/fc813cd978842c26c82534010ea849eee9ab3a13ea2b74e95cb9c99e747b/amqp-5.3.1-py3-none-any.whl", upload-time = 2024-11-12T19:55:41Z, size = 50944, hashes = { sha256 = "43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", upload-time = 2024-05-20T21:33:25Z, size = 16081, hashes = { sha256 = "aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", upload-time = 2024-05-20T21:33:24Z, size = 13643, hashes = { sha256 = "1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53" } }] -[[package]] +[[packages]] name = "anyio" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "sniffio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, -] - -[[package]] +version = "4.11.0" +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", upload-time = 2025-09-23T09:19:12Z, size = 219094, hashes = { sha256 = "82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", upload-time = 2025-09-23T09:19:10Z, size = 109097, hashes = { sha256 = "0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc" } }] + +[[packages]] +name = "babel" +version = "2.17.0" +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", upload-time = 2025-02-01T15:17:41Z, size = 9951852, hashes = { sha256 = "0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", upload-time = 2025-02-01T15:17:37Z, size = 10182537, hashes = { sha256 = "4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2" } }] + +[[packages]] +name = "backrefs" +version = "5.9" +sdist = { url = "https://files.pythonhosted.org/packages/eb/a7/312f673df6a79003279e1f55619abbe7daebbb87c17c976ddc0345c04c7b/backrefs-5.9.tar.gz", upload-time = 2025-06-22T19:34:13Z, size = 5765857, hashes = { sha256 = "808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/4d/798dc1f30468134906575156c089c492cf79b5a5fd373f07fe26c4d046bf/backrefs-5.9-py310-none-any.whl", upload-time = 2025-06-22T19:34:05Z, size = 380267, hashes = { sha256 = "db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f" } }, + { url = "https://files.pythonhosted.org/packages/55/07/f0b3375bf0d06014e9787797e6b7cc02b38ac9ff9726ccfe834d94e9991e/backrefs-5.9-py311-none-any.whl", upload-time = 2025-06-22T19:34:06Z, size = 392072, hashes = { sha256 = "6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf" } }, + { url = "https://files.pythonhosted.org/packages/9d/12/4f345407259dd60a0997107758ba3f221cf89a9b5a0f8ed5b961aef97253/backrefs-5.9-py312-none-any.whl", upload-time = 2025-06-22T19:34:08Z, size = 397947, hashes = { sha256 = "7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa" } }, + { url = "https://files.pythonhosted.org/packages/10/bf/fa31834dc27a7f05e5290eae47c82690edc3a7b37d58f7fb35a1bdbf355b/backrefs-5.9-py313-none-any.whl", upload-time = 2025-06-22T19:34:09Z, size = 399843, hashes = { sha256 = "cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b" } }, + { url = "https://files.pythonhosted.org/packages/fc/24/b29af34b2c9c41645a9f4ff117bae860291780d73880f449e0b5d948c070/backrefs-5.9-py314-none-any.whl", upload-time = 2025-06-22T19:34:11Z, size = 411762, hashes = { sha256 = "df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9" } }, + { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", upload-time = 2025-06-22T19:34:12Z, size = 380265, hashes = { sha256 = "f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60" } }, +] + +[[packages]] name = "billiard" -version = "4.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/58/1546c970afcd2a2428b1bfafecf2371d8951cc34b46701bea73f4280989e/billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f", size = 155031 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/da/43b15f28fe5f9e027b41c539abc5469052e9d48fd75f8ff094ba2a0ae767/billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb", size = 86766 }, -] +version = "4.2.2" +sdist = { url = "https://files.pythonhosted.org/packages/b9/6a/1405343016bce8354b29d90aad6b0bf6485b5e60404516e4b9a3a9646cf0/billiard-4.2.2.tar.gz", upload-time = 2025-09-20T14:44:40Z, size = 155592, hashes = { sha256 = "e815017a062b714958463e07ba15981d802dc53d41c5b69d28c5a7c238f8ecf3" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a6/80/ef8dff49aae0e4430f81842f7403e14e0ca59db7bbaf7af41245b67c6b25/billiard-4.2.2-py3-none-any.whl", upload-time = 2025-09-20T14:44:39Z, size = 86896, hashes = { sha256 = "4bc05dcf0d1cc6addef470723aac2a6232f3c7ed7475b0b580473a9145829457" } }] -[[package]] +[[packages]] name = "celery" -version = "5.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "billiard" }, - { name = "click" }, - { name = "click-didyoumean" }, - { name = "click-plugins" }, - { name = "click-repl" }, - { name = "kombu" }, - { name = "python-dateutil" }, - { name = "vine" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bf/03/5d9c6c449248958f1a5870e633a29d7419ff3724c452a98ffd22688a1a6a/celery-5.5.2.tar.gz", hash = "sha256:4d6930f354f9d29295425d7a37261245c74a32807c45d764bedc286afd0e724e", size = 1666892 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/94/8e825ac1cf59d45d20c4345d4461e6b5263ae475f708d047c3dad0ac6401/celery-5.5.2-py3-none-any.whl", hash = "sha256:54425a067afdc88b57cd8d94ed4af2ffaf13ab8c7680041ac2c4ac44357bdf4c", size = 438626 }, -] - -[package.optional-dependencies] -redis = [ - { name = "redis" }, -] +version = "5.5.3" +sdist = { url = "https://files.pythonhosted.org/packages/bb/7d/6c289f407d219ba36d8b384b42489ebdd0c84ce9c413875a8aae0c85f35b/celery-5.5.3.tar.gz", upload-time = 2025-06-01T11:08:12Z, size = 1667144, hashes = { sha256 = "6c972ae7968c2b5281227f01c3a3f984037d21c5129d07bf3550cc2afc6b10a5" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/c9/af/0dcccc7fdcdf170f9a1585e5e96b6fb0ba1749ef6be8c89a6202284759bd/celery-5.5.3-py3-none-any.whl", upload-time = 2025-06-01T11:08:09Z, size = 438775, hashes = { sha256 = "0b5761a07057acee94694464ca482416b959568904c9dfa41ce8413a7d65d525" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, -] +version = "2025.10.5" +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", upload-time = 2025-10-05T04:12:15Z, size = 164519, hashes = { sha256 = "47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", upload-time = 2025-10-05T04:12:14Z, size = 163286, hashes = { sha256 = "0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de" } }] -[[package]] +[[packages]] name = "cffi" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, -] - -[[package]] +version = "2.0.0" +marker = "python_full_version >= '3.9' and platform_python_implementation != 'PyPy'" +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", upload-time = 2025-09-08T23:24:04Z, size = 523588, hashes = { sha256 = "44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:22:08Z, size = 184283, hashes = { sha256 = "0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44" } }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:22:10Z, size = 180504, hashes = { sha256 = "f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49" } }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-08T23:22:12Z, size = 208811, hashes = { sha256 = "53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c" } }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:22:13Z, size = 216402, hashes = { sha256 = "3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb" } }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:22:14Z, size = 203217, hashes = { sha256 = "5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0" } }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:22:15Z, size = 203079, hashes = { sha256 = "9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4" } }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:22:17Z, size = 216475, hashes = { sha256 = "fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453" } }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:22:19Z, size = 218829, hashes = { sha256 = "cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495" } }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", upload-time = 2025-09-08T23:22:20Z, size = 211211, hashes = { sha256 = "e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5" } }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:22:22Z, size = 218036, hashes = { sha256 = "8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb" } }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", upload-time = 2025-09-08T23:22:23Z, size = 172184, hashes = { sha256 = "1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a" } }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", upload-time = 2025-09-08T23:22:24Z, size = 182790, hashes = { sha256 = "b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739" } }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:22:26Z, size = 184344, hashes = { sha256 = "b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe" } }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:22:28Z, size = 180560, hashes = { sha256 = "2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c" } }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-08T23:22:29Z, size = 209613, hashes = { sha256 = "baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92" } }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:22:31Z, size = 216476, hashes = { sha256 = "730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93" } }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:22:32Z, size = 203374, hashes = { sha256 = "6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5" } }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:22:34Z, size = 202597, hashes = { sha256 = "9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664" } }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:22:35Z, size = 215574, hashes = { sha256 = "8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26" } }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:22:36Z, size = 218971, hashes = { sha256 = "a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9" } }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", upload-time = 2025-09-08T23:22:38Z, size = 211972, hashes = { sha256 = "94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414" } }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:22:39Z, size = 217078, hashes = { sha256 = "5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743" } }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", upload-time = 2025-09-08T23:22:40Z, size = 172076, hashes = { sha256 = "c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5" } }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", upload-time = 2025-09-08T23:22:42Z, size = 182820, hashes = { sha256 = "66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5" } }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", upload-time = 2025-09-08T23:22:43Z, size = 177635, hashes = { sha256 = "c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d" } }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:22:44Z, size = 185271, hashes = { sha256 = "6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d" } }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:22:45Z, size = 181048, hashes = { sha256 = "8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c" } }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-08T23:22:47Z, size = 212529, hashes = { sha256 = "21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe" } }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:22:48Z, size = 220097, hashes = { sha256 = "b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062" } }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:22:50Z, size = 207983, hashes = { sha256 = "1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e" } }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:22:51Z, size = 206519, hashes = { sha256 = "81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037" } }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:22:52Z, size = 219572, hashes = { sha256 = "3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba" } }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:22:54Z, size = 222963, hashes = { sha256 = "3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94" } }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:22:55Z, size = 221361, hashes = { sha256 = "2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187" } }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", upload-time = 2025-09-08T23:22:57Z, size = 172932, hashes = { sha256 = "da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18" } }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", upload-time = 2025-09-08T23:22:58Z, size = 183557, hashes = { sha256 = "da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5" } }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", upload-time = 2025-09-08T23:22:59Z, size = 177762, hashes = { sha256 = "4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6" } }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:23:00Z, size = 185230, hashes = { sha256 = "00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb" } }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:23:02Z, size = 181043, hashes = { sha256 = "45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca" } }, + { 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", upload-time = 2025-09-08T23:23:03Z, size = 212446, hashes = { sha256 = "07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b" } }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:23:04Z, size = 220101, hashes = { sha256 = "d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b" } }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:23:06Z, size = 207948, hashes = { sha256 = "f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2" } }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:23:07Z, size = 206422, hashes = { sha256 = "dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3" } }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:23:09Z, size = 219499, hashes = { sha256 = "c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26" } }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:23:10Z, size = 222928, hashes = { sha256 = "d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c" } }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:23:12Z, size = 221302, hashes = { sha256 = "6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b" } }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", upload-time = 2025-09-08T23:23:14Z, size = 172909, hashes = { sha256 = "74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27" } }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", upload-time = 2025-09-08T23:23:15Z, size = 183402, hashes = { sha256 = "19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75" } }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", upload-time = 2025-09-08T23:23:16Z, size = 177780, hashes = { sha256 = "256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91" } }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:23:18Z, size = 185320, hashes = { sha256 = "fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5" } }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:23:19Z, size = 181487, hashes = { sha256 = "c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13" } }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:23:20Z, size = 220049, hashes = { sha256 = "24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b" } }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:23:22Z, size = 207793, hashes = { sha256 = "12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c" } }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:23:23Z, size = 206300, hashes = { sha256 = "d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef" } }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:23:24Z, size = 219244, hashes = { sha256 = "afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775" } }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:23:26Z, size = 222828, hashes = { sha256 = "737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205" } }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:23:27Z, size = 220926, hashes = { sha256 = "38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1" } }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", upload-time = 2025-09-08T23:23:44Z, size = 175328, hashes = { sha256 = "087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f" } }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", upload-time = 2025-09-08T23:23:45Z, size = 185650, hashes = { sha256 = "203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25" } }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", upload-time = 2025-09-08T23:23:47Z, size = 180687, hashes = { sha256 = "dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad" } }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:23:29Z, size = 188773, hashes = { sha256 = "9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9" } }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:23:30Z, size = 185013, hashes = { sha256 = "7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d" } }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:23:31Z, size = 221593, hashes = { sha256 = "7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c" } }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:23:33Z, size = 209354, hashes = { sha256 = "92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8" } }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:23:34Z, size = 208480, hashes = { sha256 = "b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc" } }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:23:36Z, size = 221584, hashes = { sha256 = "28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592" } }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:23:37Z, size = 224443, hashes = { sha256 = "7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512" } }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:23:38Z, size = 223437, hashes = { sha256 = "6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4" } }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", upload-time = 2025-09-08T23:23:40Z, size = 180487, hashes = { sha256 = "1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e" } }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-08T23:23:41Z, size = 191726, hashes = { sha256 = "d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6" } }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-08T23:23:43Z, size = 184195, hashes = { sha256 = "0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9" } }, + { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T23:23:48Z, size = 184288, hashes = { sha256 = "fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf" } }, + { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:23:49Z, size = 180509, hashes = { sha256 = "de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7" } }, + { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-08T23:23:51Z, size = 208813, hashes = { sha256 = "4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c" } }, + { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-08T23:23:52Z, size = 216498, hashes = { sha256 = "3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165" } }, + { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", upload-time = 2025-09-08T23:23:53Z, size = 203243, hashes = { sha256 = "e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534" } }, + { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", upload-time = 2025-09-08T23:23:55Z, size = 203158, hashes = { sha256 = "cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f" } }, + { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-08T23:23:56Z, size = 216548, hashes = { sha256 = "61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63" } }, + { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T23:23:57Z, size = 218897, hashes = { sha256 = "0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2" } }, + { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", upload-time = 2025-09-08T23:23:59Z, size = 211249, hashes = { sha256 = "1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65" } }, + { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T23:24:00Z, size = 218041, hashes = { sha256 = "89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322" } }, + { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", upload-time = 2025-09-08T23:24:01Z, size = 172138, hashes = { sha256 = "2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a" } }, + { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", upload-time = 2025-09-08T23:24:02Z, size = 182794, hashes = { sha256 = "b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9" } }, +] + +[[packages]] 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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, - { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, - { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, -] - -[[package]] +version = "3.4.4" +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", upload-time = 2025-10-14T04:42:32Z, size = 129418, hashes = { sha256 = "94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2025-10-14T04:40:11Z, size = 209709, hashes = { sha256 = "e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d" } }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-14T04:40:13Z, size = 148814, hashes = { sha256 = "4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8" } }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-14T04:40:14Z, size = 144467, hashes = { sha256 = "027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad" } }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-10-14T04:40:16Z, size = 162280, hashes = { sha256 = "f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8" } }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-10-14T04:40:17Z, size = 159454, hashes = { sha256 = "798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d" } }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-10-14T04:40:19Z, size = 153609, hashes = { sha256 = "9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313" } }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-10-14T04:40:20Z, size = 151849, hashes = { sha256 = "9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e" } }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:40:21Z, size = 151586, hashes = { sha256 = "077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93" } }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:40:23Z, size = 145290, hashes = { sha256 = "244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0" } }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:40:24Z, size = 163663, hashes = { sha256 = "64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84" } }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:40:25Z, size = 151964, hashes = { sha256 = "faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e" } }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:40:26Z, size = 161064, hashes = { sha256 = "6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db" } }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:40:28Z, size = 155015, hashes = { sha256 = "cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6" } }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", upload-time = 2025-10-14T04:40:29Z, size = 99792, hashes = { sha256 = "f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f" } }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", upload-time = 2025-10-14T04:40:30Z, size = 107198, hashes = { sha256 = "a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d" } }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", upload-time = 2025-10-14T04:40:32Z, size = 100262, hashes = { sha256 = "cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69" } }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2025-10-14T04:40:33Z, size = 206988, hashes = { sha256 = "6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8" } }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-14T04:40:34Z, size = 147324, hashes = { sha256 = "5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0" } }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-14T04:40:36Z, size = 142742, hashes = { sha256 = "a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3" } }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-10-14T04:40:37Z, size = 160863, hashes = { sha256 = "8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc" } }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-10-14T04:40:38Z, size = 157837, hashes = { sha256 = "d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897" } }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-10-14T04:40:40Z, size = 151550, hashes = { sha256 = "840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381" } }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-10-14T04:40:41Z, size = 149162, hashes = { sha256 = "ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815" } }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:40:42Z, size = 150019, hashes = { sha256 = "d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0" } }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:40:43Z, size = 143310, hashes = { sha256 = "277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161" } }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:40:44Z, size = 162022, hashes = { sha256 = "31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4" } }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:40:46Z, size = 149383, hashes = { sha256 = "0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89" } }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:40:47Z, size = 159098, hashes = { sha256 = "9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569" } }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:40:48Z, size = 152991, hashes = { sha256 = "ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224" } }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", upload-time = 2025-10-14T04:40:49Z, size = 99456, hashes = { sha256 = "eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a" } }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", upload-time = 2025-10-14T04:40:50Z, size = 106978, hashes = { sha256 = "5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016" } }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", upload-time = 2025-10-14T04:40:52Z, size = 99969, hashes = { sha256 = "65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1" } }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2025-10-14T04:40:53Z, size = 208425, hashes = { sha256 = "0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394" } }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-14T04:40:54Z, size = 148162, hashes = { sha256 = "b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25" } }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-14T04:40:55Z, size = 144558, hashes = { sha256 = "74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef" } }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-10-14T04:40:57Z, size = 161497, hashes = { sha256 = "f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d" } }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-10-14T04:40:58Z, size = 159240, hashes = { sha256 = "2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8" } }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-10-14T04:40:59Z, size = 153471, hashes = { sha256 = "11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86" } }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-10-14T04:41:00Z, size = 150864, hashes = { sha256 = "ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a" } }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:41:01Z, size = 150647, hashes = { sha256 = "21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f" } }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:41:03Z, size = 145110, hashes = { sha256 = "5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc" } }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:41:04Z, size = 162839, hashes = { sha256 = "5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf" } }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:41:05Z, size = 150667, hashes = { sha256 = "d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15" } }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:41:06Z, size = 160535, hashes = { sha256 = "af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9" } }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:41:08Z, size = 154816, hashes = { sha256 = "780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0" } }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", upload-time = 2025-10-14T04:41:09Z, size = 99694, hashes = { sha256 = "5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26" } }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", upload-time = 2025-10-14T04:41:10Z, size = 107131, hashes = { sha256 = "a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525" } }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", upload-time = 2025-10-14T04:41:11Z, size = 100390, hashes = { sha256 = "376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3" } }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2025-10-14T04:41:13Z, size = 208091, hashes = { sha256 = "e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794" } }, + { 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", upload-time = 2025-10-14T04:41:14Z, size = 147936, hashes = { sha256 = "6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed" } }, + { 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", upload-time = 2025-10-14T04:41:15Z, size = 144180, hashes = { sha256 = "3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72" } }, + { 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", upload-time = 2025-10-14T04:41:16Z, size = 161346, hashes = { sha256 = "81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328" } }, + { 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", upload-time = 2025-10-14T04:41:17Z, size = 158874, hashes = { sha256 = "5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede" } }, + { 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", upload-time = 2025-10-14T04:41:19Z, size = 153076, hashes = { sha256 = "a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894" } }, + { 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", upload-time = 2025-10-14T04:41:20Z, size = 150601, hashes = { sha256 = "bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1" } }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:41:21Z, size = 150376, hashes = { sha256 = "f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490" } }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:41:22Z, size = 144825, hashes = { sha256 = "554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44" } }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:41:23Z, size = 162583, hashes = { sha256 = "74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133" } }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:41:25Z, size = 150366, hashes = { sha256 = "c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3" } }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:41:26Z, size = 160300, hashes = { sha256 = "362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e" } }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:41:28Z, size = 154465, hashes = { sha256 = "9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc" } }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", upload-time = 2025-10-14T04:41:29Z, size = 99404, hashes = { sha256 = "9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac" } }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", upload-time = 2025-10-14T04:41:31Z, size = 107092, hashes = { sha256 = "b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14" } }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", upload-time = 2025-10-14T04:41:32Z, size = 100408, hashes = { sha256 = "542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2" } }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", upload-time = 2025-10-14T04:41:33Z, size = 207746, hashes = { sha256 = "da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd" } }, + { 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", upload-time = 2025-10-14T04:41:34Z, size = 147889, hashes = { sha256 = "8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb" } }, + { 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", upload-time = 2025-10-14T04:41:36Z, size = 143641, hashes = { sha256 = "74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e" } }, + { 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", upload-time = 2025-10-14T04:41:37Z, size = 160779, hashes = { sha256 = "752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14" } }, + { 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", upload-time = 2025-10-14T04:41:38Z, size = 159035, hashes = { sha256 = "d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191" } }, + { 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", upload-time = 2025-10-14T04:41:39Z, size = 152542, hashes = { sha256 = "ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838" } }, + { 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", upload-time = 2025-10-14T04:41:41Z, size = 149524, hashes = { sha256 = "cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6" } }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:41:42Z, size = 150395, hashes = { sha256 = "c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e" } }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:41:43Z, size = 143680, hashes = { sha256 = "47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c" } }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:41:44Z, size = 162045, hashes = { sha256 = "82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090" } }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:41:46Z, size = 149687, hashes = { sha256 = "2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152" } }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:41:47Z, size = 160014, hashes = { sha256 = "799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828" } }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:41:48Z, size = 154044, hashes = { sha256 = "99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec" } }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", upload-time = 2025-10-14T04:41:49Z, size = 99940, hashes = { sha256 = "f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9" } }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", upload-time = 2025-10-14T04:41:51Z, size = 107104, hashes = { sha256 = "8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c" } }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", upload-time = 2025-10-14T04:41:52Z, size = 100743, hashes = { sha256 = "de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2" } }, + { url = "https://files.pythonhosted.org/packages/0a/4e/3926a1c11f0433791985727965263f788af00db3482d89a7545ca5ecc921/charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", upload-time = 2025-10-14T04:41:53Z, size = 198599, hashes = { sha256 = "ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84" } }, + { url = "https://files.pythonhosted.org/packages/ec/7c/b92d1d1dcffc34592e71ea19c882b6709e43d20fa498042dea8b815638d7/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-14T04:41:54Z, size = 143090, hashes = { sha256 = "eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3" } }, + { url = "https://files.pythonhosted.org/packages/84/ce/61a28d3bb77281eb24107b937a497f3c43089326d27832a63dcedaab0478/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-14T04:41:55Z, size = 139490, hashes = { sha256 = "c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac" } }, + { url = "https://files.pythonhosted.org/packages/c0/bd/c9e59a91b2061c6f8bb98a150670cb16d4cd7c4ba7d11ad0cdf789155f41/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-10-14T04:41:56Z, size = 155334, hashes = { sha256 = "2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af" } }, + { url = "https://files.pythonhosted.org/packages/bf/37/f17ae176a80f22ff823456af91ba3bc59df308154ff53aef0d39eb3d3419/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-10-14T04:41:58Z, size = 152823, hashes = { sha256 = "778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2" } }, + { url = "https://files.pythonhosted.org/packages/bf/fa/cf5bb2409a385f78750e78c8d2e24780964976acdaaed65dbd6083ae5b40/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-10-14T04:41:59Z, size = 147618, hashes = { sha256 = "f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d" } }, + { url = "https://files.pythonhosted.org/packages/9b/63/579784a65bc7de2d4518d40bb8f1870900163e86f17f21fd1384318c459d/charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-10-14T04:42:00Z, size = 145516, hashes = { sha256 = "a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3" } }, + { url = "https://files.pythonhosted.org/packages/a3/a9/94ec6266cd394e8f93a4d69cca651d61bf6ac58d2a0422163b30c698f2c7/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:42:01Z, size = 145266, hashes = { sha256 = "194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63" } }, + { url = "https://files.pythonhosted.org/packages/09/14/d6626eb97764b58c2779fa7928fa7d1a49adb8ce687c2dbba4db003c1939/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:42:02Z, size = 139559, hashes = { sha256 = "6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7" } }, + { url = "https://files.pythonhosted.org/packages/09/01/ddbe6b01313ba191dbb0a43c7563bc770f2448c18127f9ea4b119c44dff0/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:42:04Z, size = 156653, hashes = { sha256 = "cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4" } }, + { url = "https://files.pythonhosted.org/packages/95/c8/d05543378bea89296e9af4510b44c704626e191da447235c8fdedfc5b7b2/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:42:05Z, size = 145644, hashes = { sha256 = "b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf" } }, + { url = "https://files.pythonhosted.org/packages/72/01/2866c4377998ef8a1f6802f6431e774a4c8ebe75b0a6e569ceec55c9cbfb/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:42:06Z, size = 153964, hashes = { sha256 = "e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074" } }, + { url = "https://files.pythonhosted.org/packages/4a/66/66c72468a737b4cbd7851ba2c522fe35c600575fbeac944460b4fd4a06fe/charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:42:07Z, size = 148777, hashes = { sha256 = "5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a" } }, + { url = "https://files.pythonhosted.org/packages/50/94/d0d56677fdddbffa8ca00ec411f67bb8c947f9876374ddc9d160d4f2c4b3/charset_normalizer-3.4.4-cp38-cp38-win32.whl", upload-time = 2025-10-14T04:42:08Z, size = 98687, hashes = { sha256 = "837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa" } }, + { url = "https://files.pythonhosted.org/packages/00/64/c3bc303d1b586480b1c8e6e1e2191a6d6dd40255244e5cf16763dcec52e6/charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", upload-time = 2025-10-14T04:42:09Z, size = 106115, hashes = { sha256 = "44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576" } }, + { url = "https://files.pythonhosted.org/packages/46/7c/0c4760bccf082737ca7ab84a4c2034fcc06b1f21cf3032ea98bd6feb1725/charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2025-10-14T04:42:10Z, size = 209609, hashes = { sha256 = "a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9" } }, + { url = "https://files.pythonhosted.org/packages/bb/a4/69719daef2f3d7f1819de60c9a6be981b8eeead7542d5ec4440f3c80e111/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-14T04:42:12Z, size = 149029, hashes = { sha256 = "1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d" } }, + { url = "https://files.pythonhosted.org/packages/e6/21/8d4e1d6c1e6070d3672908b8e4533a71b5b53e71d16828cc24d0efec564c/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-14T04:42:13Z, size = 144580, hashes = { sha256 = "fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608" } }, + { url = "https://files.pythonhosted.org/packages/a7/0a/a616d001b3f25647a9068e0b9199f697ce507ec898cacb06a0d5a1617c99/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-10-14T04:42:14Z, size = 162340, hashes = { sha256 = "0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc" } }, + { url = "https://files.pythonhosted.org/packages/85/93/060b52deb249a5450460e0585c88a904a83aec474ab8e7aba787f45e79f2/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-10-14T04:42:16Z, size = 159619, hashes = { sha256 = "cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e" } }, + { url = "https://files.pythonhosted.org/packages/dd/21/0274deb1cc0632cd587a9a0ec6b4674d9108e461cb4cd40d457adaeb0564/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-10-14T04:42:17Z, size = 153980, hashes = { sha256 = "4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1" } }, + { url = "https://files.pythonhosted.org/packages/28/2b/e3d7d982858dccc11b31906976323d790dded2017a0572f093ff982d692f/charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-10-14T04:42:19Z, size = 152174, hashes = { sha256 = "fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3" } }, + { url = "https://files.pythonhosted.org/packages/6e/ff/4a269f8e35f1e58b2df52c131a1fa019acb7ef3f8697b7d464b07e9b492d/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-10-14T04:42:20Z, size = 151666, hashes = { sha256 = "7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6" } }, + { url = "https://files.pythonhosted.org/packages/da/c9/ec39870f0b330d58486001dd8e532c6b9a905f5765f58a6f8204926b4a93/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", upload-time = 2025-10-14T04:42:21Z, size = 145550, hashes = { sha256 = "5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88" } }, + { url = "https://files.pythonhosted.org/packages/75/8f/d186ab99e40e0ed9f82f033d6e49001701c81244d01905dd4a6924191a30/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", upload-time = 2025-10-14T04:42:22Z, size = 163721, hashes = { sha256 = "4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1" } }, + { url = "https://files.pythonhosted.org/packages/96/b1/6047663b9744df26a7e479ac1e77af7134b1fcf9026243bb48ee2d18810f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", upload-time = 2025-10-14T04:42:23Z, size = 152127, hashes = { sha256 = "7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf" } }, + { url = "https://files.pythonhosted.org/packages/59/78/e5a6eac9179f24f704d1be67d08704c3c6ab9f00963963524be27c18ed87/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", upload-time = 2025-10-14T04:42:24Z, size = 161175, hashes = { sha256 = "2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318" } }, + { url = "https://files.pythonhosted.org/packages/e5/43/0e626e42d54dd2f8dd6fc5e1c5ff00f05fbca17cb699bedead2cae69c62f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-10-14T04:42:27Z, size = 155375, hashes = { sha256 = "cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c" } }, + { url = "https://files.pythonhosted.org/packages/e9/91/d9615bf2e06f35e4997616ff31248c3657ed649c5ab9d35ea12fce54e380/charset_normalizer-3.4.4-cp39-cp39-win32.whl", upload-time = 2025-10-14T04:42:28Z, size = 99692, hashes = { sha256 = "2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505" } }, + { url = "https://files.pythonhosted.org/packages/d1/a9/6c040053909d9d1ef4fcab45fddec083aedc9052c10078339b47c8573ea8/charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", upload-time = 2025-10-14T04:42:29Z, size = 107192, hashes = { sha256 = "f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966" } }, + { url = "https://files.pythonhosted.org/packages/f0/c6/4fa536b2c0cd3edfb7ccf8469fa0f363ea67b7213a842b90909ca33dd851/charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", upload-time = 2025-10-14T04:42:30Z, size = 100220, hashes = { sha256 = "b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50" } }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", upload-time = 2025-10-14T04:42:31Z, size = 53402, hashes = { sha256 = "7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f" } }, +] + +[[packages]] name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, -] +version = "8.3.0" +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", upload-time = 2025-09-18T17:32:23Z, size = 276943, hashes = { sha256 = "e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", upload-time = 2025-09-18T17:32:22Z, size = 107295, hashes = { sha256 = "9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc" } }] -[[package]] +[[packages]] name = "click-didyoumean" version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463", size = 3089 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c", size = 3631 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", upload-time = 2024-03-24T08:22:07Z, size = 3089, hashes = { sha256 = "4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", upload-time = 2024-03-24T08:22:06Z, size = 3631, hashes = { sha256 = "5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c" } }] -[[package]] +[[packages]] name = "click-plugins" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497 }, -] +version = "1.1.1.2" +sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/34847b59150da33690a36da3681d6bbc2ec14ee9a846bc30a6746e5984e4/click_plugins-1.1.1.2.tar.gz", upload-time = 2025-06-25T00:47:37Z, size = 8343, hashes = { sha256 = "d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/3d/9a/2abecb28ae875e39c8cad711eb1186d8d14eab564705325e77e4e6ab9ae5/click_plugins-1.1.1.2-py2.py3-none-any.whl", upload-time = 2025-06-25T00:47:36Z, size = 11051, hashes = { sha256 = "008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6" } }] -[[package]] +[[packages]] name = "click-repl" version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "prompt-toolkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", size = 10449 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", upload-time = 2023-06-15T12:43:51Z, size = 10449, hashes = { sha256 = "17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", upload-time = 2023-06-15T12:43:48Z, size = 10289, hashes = { sha256 = "fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", upload-time = 2022-10-25T02:36:22Z, size = 27697, hashes = { sha256 = "08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", upload-time = 2022-10-25T02:36:20Z, size = 25335, hashes = { sha256 = "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" } }] + +[[packages]] +name = "coverage" +version = "7.10.7" +sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", upload-time = 2025-09-21T20:03:56Z, size = 827704, hashes = { sha256 = "f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-09-21T20:00:57Z, size = 217987, hashes = { sha256 = "fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a" } }, + { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:01:00Z, size = 218388, hashes = { sha256 = "e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5" } }, + { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:01:01Z, size = 245148, hashes = { sha256 = "240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17" } }, + { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:01:03Z, size = 246958, hashes = { sha256 = "8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b" } }, + { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:01:04Z, size = 248819, hashes = { sha256 = "6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87" } }, + { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:01:06Z, size = 245754, hashes = { sha256 = "e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e" } }, + { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:01:07Z, size = 246860, hashes = { sha256 = "c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e" } }, + { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:01:08Z, size = 244877, hashes = { sha256 = "b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df" } }, + { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:01:10Z, size = 245108, hashes = { sha256 = "606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0" } }, + { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:01:11Z, size = 245752, hashes = { sha256 = "10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13" } }, + { url = "https://files.pythonhosted.org/packages/6a/28/ea1a84a60828177ae3b100cb6723838523369a44ec5742313ed7db3da160/coverage-7.10.7-cp310-cp310-win32.whl", upload-time = 2025-09-21T20:01:13Z, size = 220497, hashes = { sha256 = "b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b" } }, + { url = "https://files.pythonhosted.org/packages/fc/1a/a81d46bbeb3c3fd97b9602ebaa411e076219a150489bcc2c025f151bd52d/coverage-7.10.7-cp310-cp310-win_amd64.whl", upload-time = 2025-09-21T20:01:14Z, size = 221392, hashes = { sha256 = "3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807" } }, + { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-21T20:01:16Z, size = 218102, hashes = { sha256 = "a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59" } }, + { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:01:17Z, size = 218505, hashes = { sha256 = "65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a" } }, + { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:01:19Z, size = 248898, hashes = { sha256 = "5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699" } }, + { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:01:20Z, size = 250831, hashes = { sha256 = "35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d" } }, + { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:01:22Z, size = 252937, hashes = { sha256 = "4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e" } }, + { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:01:23Z, size = 249021, hashes = { sha256 = "6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23" } }, + { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:01:25Z, size = 250626, hashes = { sha256 = "4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab" } }, + { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:01:27Z, size = 248682, hashes = { sha256 = "121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82" } }, + { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:01:28Z, size = 248402, hashes = { sha256 = "88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2" } }, + { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:01:30Z, size = 249320, hashes = { sha256 = "ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61" } }, + { url = "https://files.pythonhosted.org/packages/2f/88/a23cc185f6a805dfc4fdf14a94016835eeb85e22ac3a0e66d5e89acd6462/coverage-7.10.7-cp311-cp311-win32.whl", upload-time = 2025-09-21T20:01:32Z, size = 220536, hashes = { sha256 = "972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14" } }, + { url = "https://files.pythonhosted.org/packages/fe/ef/0b510a399dfca17cec7bc2f05ad8bd78cf55f15c8bc9a73ab20c5c913c2e/coverage-7.10.7-cp311-cp311-win_amd64.whl", upload-time = 2025-09-21T20:01:33Z, size = 221425, hashes = { sha256 = "a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2" } }, + { url = "https://files.pythonhosted.org/packages/51/7f/023657f301a276e4ba1850f82749bc136f5a7e8768060c2e5d9744a22951/coverage-7.10.7-cp311-cp311-win_arm64.whl", upload-time = 2025-09-21T20:01:34Z, size = 220103, hashes = { sha256 = "736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a" } }, + { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-21T20:01:36Z, size = 218290, hashes = { sha256 = "7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417" } }, + { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:01:37Z, size = 218515, hashes = { sha256 = "18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973" } }, + { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:01:39Z, size = 250020, hashes = { sha256 = "399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c" } }, + { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:01:41Z, size = 252769, hashes = { sha256 = "314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7" } }, + { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:01:43Z, size = 253901, hashes = { sha256 = "c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6" } }, + { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:01:44Z, size = 250413, hashes = { sha256 = "bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59" } }, + { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:01:45Z, size = 251820, hashes = { sha256 = "a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b" } }, + { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:01:47Z, size = 249941, hashes = { sha256 = "78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a" } }, + { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:01:48Z, size = 249519, hashes = { sha256 = "5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb" } }, + { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:01:50Z, size = 251375, hashes = { sha256 = "d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1" } }, + { url = "https://files.pythonhosted.org/packages/5e/75/61b9bbd6c7d24d896bfeec57acba78e0f8deac68e6baf2d4804f7aae1f88/coverage-7.10.7-cp312-cp312-win32.whl", upload-time = 2025-09-21T20:01:51Z, size = 220699, hashes = { sha256 = "77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256" } }, + { url = "https://files.pythonhosted.org/packages/ca/f3/3bf7905288b45b075918d372498f1cf845b5b579b723c8fd17168018d5f5/coverage-7.10.7-cp312-cp312-win_amd64.whl", upload-time = 2025-09-21T20:01:53Z, size = 221512, hashes = { sha256 = "f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba" } }, + { url = "https://files.pythonhosted.org/packages/5c/44/3e32dbe933979d05cf2dac5e697c8599cfe038aaf51223ab901e208d5a62/coverage-7.10.7-cp312-cp312-win_arm64.whl", upload-time = 2025-09-21T20:01:55Z, size = 220147, hashes = { sha256 = "bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf" } }, + { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-21T20:01:56Z, size = 218320, hashes = { sha256 = "981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d" } }, + { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:01:58Z, size = 218575, hashes = { sha256 = "73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b" } }, + { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:01:59Z, size = 249568, hashes = { sha256 = "a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e" } }, + { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:02:01Z, size = 252174, hashes = { sha256 = "10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b" } }, + { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:02:02Z, size = 253447, hashes = { sha256 = "c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49" } }, + { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:02:04Z, size = 249779, hashes = { sha256 = "69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911" } }, + { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:02:06Z, size = 251604, hashes = { sha256 = "7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0" } }, + { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:02:07Z, size = 249497, hashes = { sha256 = "b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f" } }, + { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:02:10Z, size = 249350, hashes = { sha256 = "b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c" } }, + { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:02:12Z, size = 251111, hashes = { sha256 = "99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f" } }, + { url = "https://files.pythonhosted.org/packages/a2/9e/9fdb08f4bf476c912f0c3ca292e019aab6712c93c9344a1653986c3fd305/coverage-7.10.7-cp313-cp313-win32.whl", upload-time = 2025-09-21T20:02:13Z, size = 220746, hashes = { sha256 = "dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698" } }, + { url = "https://files.pythonhosted.org/packages/b1/b1/a75fd25df44eab52d1931e89980d1ada46824c7a3210be0d3c88a44aaa99/coverage-7.10.7-cp313-cp313-win_amd64.whl", upload-time = 2025-09-21T20:02:15Z, size = 221541, hashes = { sha256 = "cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843" } }, + { url = "https://files.pythonhosted.org/packages/14/3a/d720d7c989562a6e9a14b2c9f5f2876bdb38e9367126d118495b89c99c37/coverage-7.10.7-cp313-cp313-win_arm64.whl", upload-time = 2025-09-21T20:02:17Z, size = 220170, hashes = { sha256 = "4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546" } }, + { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-21T20:02:18Z, size = 219029, hashes = { sha256 = "a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c" } }, + { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:02:20Z, size = 219259, hashes = { sha256 = "dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15" } }, + { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:02:22Z, size = 260592, hashes = { sha256 = "f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4" } }, + { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:02:24Z, size = 262768, hashes = { sha256 = "1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0" } }, + { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:02:26Z, size = 264995, hashes = { sha256 = "83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0" } }, + { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:02:27Z, size = 259546, hashes = { sha256 = "50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65" } }, + { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:02:29Z, size = 262544, hashes = { sha256 = "2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541" } }, + { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:02:31Z, size = 260308, hashes = { sha256 = "2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6" } }, + { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:02:32Z, size = 258920, hashes = { sha256 = "0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999" } }, + { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:02:34Z, size = 261434, hashes = { sha256 = "4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2" } }, + { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", upload-time = 2025-09-21T20:02:37Z, size = 221403, hashes = { sha256 = "2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a" } }, + { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-21T20:02:39Z, size = 222469, hashes = { sha256 = "33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb" } }, + { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-21T20:02:40Z, size = 220731, hashes = { sha256 = "86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb" } }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-21T20:02:42Z, size = 218302, hashes = { sha256 = "b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520" } }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:02:44Z, size = 218578, hashes = { sha256 = "212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32" } }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:02:46Z, size = 249629, hashes = { sha256 = "3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f" } }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:02:48Z, size = 252162, hashes = { sha256 = "bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a" } }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:02:50Z, size = 253517, hashes = { sha256 = "813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360" } }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:02:51Z, size = 249632, hashes = { sha256 = "93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69" } }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:02:53Z, size = 251520, hashes = { sha256 = "cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14" } }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:02:55Z, size = 249455, hashes = { sha256 = "39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe" } }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:02:57Z, size = 249287, hashes = { sha256 = "925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e" } }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:02:59Z, size = 250946, hashes = { sha256 = "2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd" } }, + { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", upload-time = 2025-09-21T20:03:01Z, size = 221009, hashes = { sha256 = "b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2" } }, + { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", upload-time = 2025-09-21T20:03:03Z, size = 221804, hashes = { sha256 = "1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681" } }, + { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", upload-time = 2025-09-21T20:03:05Z, size = 220384, hashes = { sha256 = "097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880" } }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-21T20:03:06Z, size = 219047, hashes = { sha256 = "a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63" } }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:03:08Z, size = 219266, hashes = { sha256 = "9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2" } }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:03:10Z, size = 260767, hashes = { sha256 = "8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d" } }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:03:11Z, size = 262931, hashes = { sha256 = "affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0" } }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:03:13Z, size = 265186, hashes = { sha256 = "6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699" } }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:03:15Z, size = 259470, hashes = { sha256 = "03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9" } }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:03:17Z, size = 262626, hashes = { sha256 = "1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f" } }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:03:19Z, size = 260386, hashes = { sha256 = "0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1" } }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:03:21Z, size = 258852, hashes = { sha256 = "a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0" } }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:03:23Z, size = 261534, hashes = { sha256 = "a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399" } }, + { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", upload-time = 2025-09-21T20:03:24Z, size = 221784, hashes = { sha256 = "67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235" } }, + { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-21T20:03:26Z, size = 222905, hashes = { sha256 = "e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d" } }, + { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-21T20:03:28Z, size = 220922, hashes = { sha256 = "7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a" } }, + { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2025-09-21T20:03:30Z, size = 217978, hashes = { sha256 = "fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3" } }, + { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-21T20:03:32Z, size = 218370, hashes = { sha256 = "bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c" } }, + { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", upload-time = 2025-09-21T20:03:33Z, size = 244802, hashes = { sha256 = "567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396" } }, + { url = "https://files.pythonhosted.org/packages/b0/49/8a070782ce7e6b94ff6a0b6d7c65ba6bc3091d92a92cef4cd4eb0767965c/coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-09-21T20:03:36Z, size = 246625, hashes = { sha256 = "2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40" } }, + { url = "https://files.pythonhosted.org/packages/6a/92/1c1c5a9e8677ce56d42b97bdaca337b2d4d9ebe703d8c174ede52dbabd5f/coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-21T20:03:38Z, size = 248399, hashes = { sha256 = "c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594" } }, + { url = "https://files.pythonhosted.org/packages/c0/54/b140edee7257e815de7426d5d9846b58505dffc29795fff2dfb7f8a1c5a0/coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-21T20:03:40Z, size = 245142, hashes = { sha256 = "912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a" } }, + { url = "https://files.pythonhosted.org/packages/e4/9e/6d6b8295940b118e8b7083b29226c71f6154f7ff41e9ca431f03de2eac0d/coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-09-21T20:03:42Z, size = 246284, hashes = { sha256 = "f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b" } }, + { url = "https://files.pythonhosted.org/packages/db/e5/5e957ca747d43dbe4d9714358375c7546cb3cb533007b6813fc20fce37ad/coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", upload-time = 2025-09-21T20:03:44Z, size = 244353, hashes = { sha256 = "cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3" } }, + { url = "https://files.pythonhosted.org/packages/9a/45/540fc5cc92536a1b783b7ef99450bd55a4b3af234aae35a18a339973ce30/coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", upload-time = 2025-09-21T20:03:46Z, size = 244430, hashes = { sha256 = "f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0" } }, + { url = "https://files.pythonhosted.org/packages/75/0b/8287b2e5b38c8fe15d7e3398849bb58d382aedc0864ea0fa1820e8630491/coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-09-21T20:03:48Z, size = 245311, hashes = { sha256 = "635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f" } }, + { url = "https://files.pythonhosted.org/packages/0c/1d/29724999984740f0c86d03e6420b942439bf5bd7f54d4382cae386a9d1e9/coverage-7.10.7-cp39-cp39-win32.whl", upload-time = 2025-09-21T20:03:50Z, size = 220500, hashes = { sha256 = "5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431" } }, + { url = "https://files.pythonhosted.org/packages/43/11/4b1e6b129943f905ca54c339f343877b55b365ae2558806c1be4f7476ed5/coverage-7.10.7-cp39-cp39-win_amd64.whl", upload-time = 2025-09-21T20:03:51Z, size = 221408, hashes = { sha256 = "c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07" } }, + { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", upload-time = 2025-09-21T20:03:53Z, size = 209952, hashes = { sha256 = "f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260" } }, +] + +[[packages]] name = "cryptography" -version = "44.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053", size = 711096 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/53/c776d80e9d26441bb3868457909b4e74dd9ccabd182e10b2b0ae7a07e265/cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88", size = 6670281 }, - { url = "https://files.pythonhosted.org/packages/6a/06/af2cf8d56ef87c77319e9086601bef621bedf40f6f59069e1b6d1ec498c5/cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137", size = 3959305 }, - { url = "https://files.pythonhosted.org/packages/ae/01/80de3bec64627207d030f47bf3536889efee8913cd363e78ca9a09b13c8e/cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c", size = 4171040 }, - { url = "https://files.pythonhosted.org/packages/bd/48/bb16b7541d207a19d9ae8b541c70037a05e473ddc72ccb1386524d4f023c/cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76", size = 3963411 }, - { url = "https://files.pythonhosted.org/packages/42/b2/7d31f2af5591d217d71d37d044ef5412945a8a8e98d5a2a8ae4fd9cd4489/cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359", size = 3689263 }, - { url = "https://files.pythonhosted.org/packages/25/50/c0dfb9d87ae88ccc01aad8eb93e23cfbcea6a6a106a9b63a7b14c1f93c75/cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43", size = 4196198 }, - { url = "https://files.pythonhosted.org/packages/66/c9/55c6b8794a74da652690c898cb43906310a3e4e4f6ee0b5f8b3b3e70c441/cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01", size = 3966502 }, - { url = "https://files.pythonhosted.org/packages/b6/f7/7cb5488c682ca59a02a32ec5f975074084db4c983f849d47b7b67cc8697a/cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d", size = 4196173 }, - { url = "https://files.pythonhosted.org/packages/d2/0b/2f789a8403ae089b0b121f8f54f4a3e5228df756e2146efdf4a09a3d5083/cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904", size = 4087713 }, - { url = "https://files.pythonhosted.org/packages/1d/aa/330c13655f1af398fc154089295cf259252f0ba5df93b4bc9d9c7d7f843e/cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44", size = 4299064 }, - { url = "https://files.pythonhosted.org/packages/10/a8/8c540a421b44fd267a7d58a1fd5f072a552d72204a3f08194f98889de76d/cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d", size = 2773887 }, - { url = "https://files.pythonhosted.org/packages/b9/0d/c4b1657c39ead18d76bbd122da86bd95bdc4095413460d09544000a17d56/cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d", size = 3209737 }, - { url = "https://files.pythonhosted.org/packages/34/a3/ad08e0bcc34ad436013458d7528e83ac29910943cea42ad7dd4141a27bbb/cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f", size = 6673501 }, - { url = "https://files.pythonhosted.org/packages/b1/f0/7491d44bba8d28b464a5bc8cc709f25a51e3eac54c0a4444cf2473a57c37/cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759", size = 3960307 }, - { url = "https://files.pythonhosted.org/packages/f7/c8/e5c5d0e1364d3346a5747cdcd7ecbb23ca87e6dea4f942a44e88be349f06/cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645", size = 4170876 }, - { url = "https://files.pythonhosted.org/packages/73/96/025cb26fc351d8c7d3a1c44e20cf9a01e9f7cf740353c9c7a17072e4b264/cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2", size = 3964127 }, - { url = "https://files.pythonhosted.org/packages/01/44/eb6522db7d9f84e8833ba3bf63313f8e257729cf3a8917379473fcfd6601/cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54", size = 3689164 }, - { url = "https://files.pythonhosted.org/packages/68/fb/d61a4defd0d6cee20b1b8a1ea8f5e25007e26aeb413ca53835f0cae2bcd1/cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93", size = 4198081 }, - { url = "https://files.pythonhosted.org/packages/1b/50/457f6911d36432a8811c3ab8bd5a6090e8d18ce655c22820994913dd06ea/cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c", size = 3967716 }, - { url = "https://files.pythonhosted.org/packages/35/6e/dca39d553075980ccb631955c47b93d87d27f3596da8d48b1ae81463d915/cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f", size = 4197398 }, - { url = "https://files.pythonhosted.org/packages/9b/9d/d1f2fe681eabc682067c66a74addd46c887ebacf39038ba01f8860338d3d/cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5", size = 4087900 }, - { url = "https://files.pythonhosted.org/packages/c4/f5/3599e48c5464580b73b236aafb20973b953cd2e7b44c7c2533de1d888446/cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b", size = 4301067 }, - { url = "https://files.pythonhosted.org/packages/a7/6c/d2c48c8137eb39d0c193274db5c04a75dab20d2f7c3f81a7dcc3a8897701/cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028", size = 2775467 }, - { url = "https://files.pythonhosted.org/packages/c9/ad/51f212198681ea7b0deaaf8846ee10af99fba4e894f67b353524eab2bbe5/cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334", size = 3210375 }, -] - -[[package]] +version = "46.0.2" +sdist = { url = "https://files.pythonhosted.org/packages/4a/9b/e301418629f7bfdf72db9e80ad6ed9d1b83c487c471803eaa6464c511a01/cryptography-46.0.2.tar.gz", upload-time = 2025-10-01T00:29:11Z, size = 749293, hashes = { sha256 = "21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/98/7a8df8c19a335c8028414738490fc3955c0cecbfdd37fcc1b9c3d04bd561/cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", upload-time = 2025-10-01T00:27:22Z, size = 7261255, hashes = { sha256 = "f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663" } }, + { url = "https://files.pythonhosted.org/packages/c6/38/b2adb2aa1baa6706adc3eb746691edd6f90a656a9a65c3509e274d15a2b8/cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-10-01T00:27:25Z, size = 4297596, hashes = { sha256 = "1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02" } }, + { url = "https://files.pythonhosted.org/packages/e4/27/0f190ada240003119488ae66c897b5e97149292988f556aef4a6a2a57595/cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-10-01T00:27:27Z, size = 4450899, hashes = { sha256 = "bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135" } }, + { url = "https://files.pythonhosted.org/packages/85/d5/e4744105ab02fdf6bb58ba9a816e23b7a633255987310b4187d6745533db/cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", upload-time = 2025-10-01T00:27:29Z, size = 4300382, hashes = { sha256 = "9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92" } }, + { url = "https://files.pythonhosted.org/packages/33/fb/bf9571065c18c04818cb07de90c43fc042c7977c68e5de6876049559c72f/cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-01T00:27:30Z, size = 4017347, hashes = { sha256 = "97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659" } }, + { url = "https://files.pythonhosted.org/packages/35/72/fc51856b9b16155ca071080e1a3ad0c3a8e86616daf7eb018d9565b99baa/cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", upload-time = 2025-10-01T00:27:32Z, size = 4983500, hashes = { sha256 = "4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa" } }, + { url = "https://files.pythonhosted.org/packages/c1/53/0f51e926799025e31746d454ab2e36f8c3f0d41592bc65cb9840368d3275/cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", upload-time = 2025-10-01T00:27:34Z, size = 4482591, hashes = { sha256 = "fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08" } }, + { url = "https://files.pythonhosted.org/packages/86/96/4302af40b23ab8aa360862251fb8fc450b2a06ff24bc5e261c2007f27014/cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", upload-time = 2025-10-01T00:27:37Z, size = 4300019, hashes = { sha256 = "1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5" } }, + { url = "https://files.pythonhosted.org/packages/9b/59/0be12c7fcc4c5e34fe2b665a75bc20958473047a30d095a7657c218fa9e8/cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", upload-time = 2025-10-01T00:27:40Z, size = 4950006, hashes = { sha256 = "1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc" } }, + { url = "https://files.pythonhosted.org/packages/55/1d/42fda47b0111834b49e31590ae14fd020594d5e4dadd639bce89ad790fba/cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", upload-time = 2025-10-01T00:27:42Z, size = 4482088, hashes = { sha256 = "be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b" } }, + { url = "https://files.pythonhosted.org/packages/17/50/60f583f69aa1602c2bdc7022dae86a0d2b837276182f8c1ec825feb9b874/cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", upload-time = 2025-10-01T00:27:44Z, size = 4425599, hashes = { sha256 = "9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1" } }, + { url = "https://files.pythonhosted.org/packages/d1/57/d8d4134cd27e6e94cf44adb3f3489f935bde85f3a5508e1b5b43095b917d/cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", upload-time = 2025-10-01T00:27:46Z, size = 4697458, hashes = { sha256 = "9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b" } }, + { url = "https://files.pythonhosted.org/packages/d1/2b/531e37408573e1da33adfb4c58875013ee8ac7d548d1548967d94a0ae5c4/cryptography-46.0.2-cp311-abi3-win32.whl", upload-time = 2025-10-01T00:27:48Z, size = 3056077, hashes = { sha256 = "8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee" } }, + { url = "https://files.pythonhosted.org/packages/a8/cd/2f83cafd47ed2dc5a3a9c783ff5d764e9e70d3a160e0df9a9dcd639414ce/cryptography-46.0.2-cp311-abi3-win_amd64.whl", upload-time = 2025-10-01T00:27:50Z, size = 3512585, hashes = { sha256 = "758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb" } }, + { url = "https://files.pythonhosted.org/packages/00/36/676f94e10bfaa5c5b86c469ff46d3e0663c5dc89542f7afbadac241a3ee4/cryptography-46.0.2-cp311-abi3-win_arm64.whl", upload-time = 2025-10-01T00:27:52Z, size = 2927474, hashes = { sha256 = "218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470" } }, + { url = "https://files.pythonhosted.org/packages/6f/cc/47fc6223a341f26d103cb6da2216805e08a37d3b52bee7f3b2aee8066f95/cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", upload-time = 2025-10-01T00:27:54Z, size = 7198626, hashes = { sha256 = "bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8" } }, + { url = "https://files.pythonhosted.org/packages/93/22/d66a8591207c28bbe4ac7afa25c4656dc19dc0db29a219f9809205639ede/cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-10-01T00:27:57Z, size = 4287584, hashes = { sha256 = "e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a" } }, + { url = "https://files.pythonhosted.org/packages/8c/3e/fac3ab6302b928e0398c269eddab5978e6c1c50b2b77bb5365ffa8633b37/cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-10-01T00:27:58Z, size = 4433796, hashes = { sha256 = "a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b" } }, + { url = "https://files.pythonhosted.org/packages/7d/d8/24392e5d3c58e2d83f98fe5a2322ae343360ec5b5b93fe18bc52e47298f5/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", upload-time = 2025-10-01T00:28:00Z, size = 4292126, hashes = { sha256 = "9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20" } }, + { url = "https://files.pythonhosted.org/packages/ed/38/3d9f9359b84c16c49a5a336ee8be8d322072a09fac17e737f3bb11f1ce64/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-01T00:28:02Z, size = 3993056, hashes = { sha256 = "2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73" } }, + { url = "https://files.pythonhosted.org/packages/d6/a3/4c44fce0d49a4703cc94bfbe705adebf7ab36efe978053742957bc7ec324/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", upload-time = 2025-10-01T00:28:04Z, size = 4967604, hashes = { sha256 = "0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee" } }, + { url = "https://files.pythonhosted.org/packages/eb/c2/49d73218747c8cac16bb8318a5513fde3129e06a018af3bc4dc722aa4a98/cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", upload-time = 2025-10-01T00:28:06Z, size = 4465367, hashes = { sha256 = "5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2" } }, + { url = "https://files.pythonhosted.org/packages/1b/64/9afa7d2ee742f55ca6285a54386ed2778556a4ed8871571cb1c1bfd8db9e/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", upload-time = 2025-10-01T00:28:08Z, size = 4291678, hashes = { sha256 = "27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f" } }, + { url = "https://files.pythonhosted.org/packages/50/48/1696d5ea9623a7b72ace87608f6899ca3c331709ac7ebf80740abb8ac673/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", upload-time = 2025-10-01T00:28:10Z, size = 4931366, hashes = { sha256 = "512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e" } }, + { url = "https://files.pythonhosted.org/packages/eb/3c/9dfc778401a334db3b24435ee0733dd005aefb74afe036e2d154547cb917/cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", upload-time = 2025-10-01T00:28:12Z, size = 4464738, hashes = { sha256 = "07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36" } }, + { url = "https://files.pythonhosted.org/packages/dc/b1/abcde62072b8f3fd414e191a6238ce55a0050e9738090dc6cded24c12036/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-10-01T00:28:14Z, size = 4419305, hashes = { sha256 = "48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a" } }, + { url = "https://files.pythonhosted.org/packages/c7/1f/3d2228492f9391395ca34c677e8f2571fb5370fe13dc48c1014f8c509864/cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-10-01T00:28:15Z, size = 4681201, hashes = { sha256 = "e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c" } }, + { url = "https://files.pythonhosted.org/packages/de/77/b687745804a93a55054f391528fcfc76c3d6bfd082ce9fb62c12f0d29fc1/cryptography-46.0.2-cp314-cp314t-win32.whl", upload-time = 2025-10-01T00:28:17Z, size = 3022492, hashes = { sha256 = "e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1" } }, + { url = "https://files.pythonhosted.org/packages/60/a5/8d498ef2996e583de0bef1dcc5e70186376f00883ae27bf2133f490adf21/cryptography-46.0.2-cp314-cp314t-win_amd64.whl", upload-time = 2025-10-01T00:28:19Z, size = 3496215, hashes = { sha256 = "48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9" } }, + { url = "https://files.pythonhosted.org/packages/56/db/ee67aaef459a2706bc302b15889a1a8126ebe66877bab1487ae6ad00f33d/cryptography-46.0.2-cp314-cp314t-win_arm64.whl", upload-time = 2025-10-01T00:28:21Z, size = 2919255, hashes = { sha256 = "8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0" } }, + { url = "https://files.pythonhosted.org/packages/d5/bb/fa95abcf147a1b0bb94d95f53fbb09da77b24c776c5d87d36f3d94521d2c/cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", upload-time = 2025-10-01T00:28:22Z, size = 7248090, hashes = { sha256 = "a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685" } }, + { url = "https://files.pythonhosted.org/packages/b7/66/f42071ce0e3ffbfa80a88feadb209c779fda92a23fbc1e14f74ebf72ef6b/cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-10-01T00:28:25Z, size = 4293123, hashes = { sha256 = "d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b" } }, + { url = "https://files.pythonhosted.org/packages/a8/5d/1fdbd2e5c1ba822828d250e5a966622ef00185e476d1cd2726b6dd135e53/cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-10-01T00:28:26Z, size = 4439524, hashes = { sha256 = "bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1" } }, + { url = "https://files.pythonhosted.org/packages/c8/c1/5e4989a7d102d4306053770d60f978c7b6b1ea2ff8c06e0265e305b23516/cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", upload-time = 2025-10-01T00:28:29Z, size = 4297264, hashes = { sha256 = "ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c" } }, + { url = "https://files.pythonhosted.org/packages/28/78/b56f847d220cb1d6d6aef5a390e116ad603ce13a0945a3386a33abc80385/cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", upload-time = 2025-10-01T00:28:31Z, size = 4011872, hashes = { sha256 = "cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af" } }, + { url = "https://files.pythonhosted.org/packages/e1/80/2971f214b066b888944f7b57761bf709ee3f2cf805619a18b18cab9b263c/cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", upload-time = 2025-10-01T00:28:33Z, size = 4978458, hashes = { sha256 = "3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b" } }, + { url = "https://files.pythonhosted.org/packages/a5/84/0cb0a2beaa4f1cbe63ebec4e97cd7e0e9f835d0ba5ee143ed2523a1e0016/cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", upload-time = 2025-10-01T00:28:36Z, size = 4472195, hashes = { sha256 = "5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21" } }, + { url = "https://files.pythonhosted.org/packages/30/8b/2b542ddbf78835c7cd67b6fa79e95560023481213a060b92352a61a10efe/cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", upload-time = 2025-10-01T00:28:37Z, size = 4296791, hashes = { sha256 = "d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6" } }, + { url = "https://files.pythonhosted.org/packages/78/12/9065b40201b4f4876e93b9b94d91feb18de9150d60bd842a16a21565007f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", upload-time = 2025-10-01T00:28:39Z, size = 4939629, hashes = { sha256 = "04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023" } }, + { url = "https://files.pythonhosted.org/packages/f6/9e/6507dc048c1b1530d372c483dfd34e7709fc542765015425f0442b08547f/cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", upload-time = 2025-10-01T00:28:41Z, size = 4471988, hashes = { sha256 = "8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e" } }, + { url = "https://files.pythonhosted.org/packages/b1/86/d025584a5f7d5c5ec8d3633dbcdce83a0cd579f1141ceada7817a4c26934/cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", upload-time = 2025-10-01T00:28:43Z, size = 4422989, hashes = { sha256 = "747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90" } }, + { url = "https://files.pythonhosted.org/packages/4b/39/536370418b38a15a61bbe413006b79dfc3d2b4b0eafceb5581983f973c15/cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", upload-time = 2025-10-01T00:28:45Z, size = 4685578, hashes = { sha256 = "6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be" } }, + { url = "https://files.pythonhosted.org/packages/15/52/ea7e2b1910f547baed566c866fbb86de2402e501a89ecb4871ea7f169a81/cryptography-46.0.2-cp38-abi3-win32.whl", upload-time = 2025-10-01T00:28:47Z, size = 3036711, hashes = { sha256 = "0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c" } }, + { url = "https://files.pythonhosted.org/packages/71/9e/171f40f9c70a873e73c2efcdbe91e1d4b1777a03398fa1c4af3c56a2477a/cryptography-46.0.2-cp38-abi3-win_amd64.whl", upload-time = 2025-10-01T00:28:48Z, size = 3500007, hashes = { sha256 = "f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62" } }, + { url = "https://files.pythonhosted.org/packages/3e/7c/15ad426257615f9be8caf7f97990cf3dcbb5b8dd7ed7e0db581a1c4759dd/cryptography-46.0.2-cp38-abi3-win_arm64.whl", upload-time = 2025-10-01T00:28:51Z, size = 2918153, hashes = { sha256 = "91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1" } }, + { url = "https://files.pythonhosted.org/packages/25/b2/067a7db693488f19777ecf73f925bcb6a3efa2eae42355bafaafa37a6588/cryptography-46.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", upload-time = 2025-10-01T00:28:53Z, size = 3701860, hashes = { sha256 = "f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34" } }, + { url = "https://files.pythonhosted.org/packages/87/12/47c2aab2c285f97c71a791169529dbb89f48fc12e5f62bb6525c3927a1a2/cryptography-46.0.2-pp310-pypy310_pp73-win_amd64.whl", upload-time = 2025-10-01T00:28:55Z, size = 3429917, hashes = { sha256 = "e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807" } }, + { url = "https://files.pythonhosted.org/packages/b7/8c/1aabe338149a7d0f52c3e30f2880b20027ca2a485316756ed6f000462db3/cryptography-46.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", upload-time = 2025-10-01T00:28:57Z, size = 3714495, hashes = { sha256 = "1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5" } }, + { url = "https://files.pythonhosted.org/packages/e3/0a/0d10eb970fe3e57da9e9ddcfd9464c76f42baf7b3d0db4a782d6746f788f/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", upload-time = 2025-10-01T00:28:58Z, size = 4243379, hashes = { sha256 = "fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4" } }, + { url = "https://files.pythonhosted.org/packages/7d/60/e274b4d41a9eb82538b39950a74ef06e9e4d723cb998044635d9deb1b435/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", upload-time = 2025-10-01T00:29:00Z, size = 4409533, hashes = { sha256 = "2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d" } }, + { url = "https://files.pythonhosted.org/packages/19/9a/fb8548f762b4749aebd13b57b8f865de80258083fe814957f9b0619cfc56/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", upload-time = 2025-10-01T00:29:02Z, size = 4243120, hashes = { sha256 = "9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46" } }, + { url = "https://files.pythonhosted.org/packages/71/60/883f24147fd4a0c5cab74ac7e36a1ff3094a54ba5c3a6253d2ff4b19255b/cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", upload-time = 2025-10-01T00:29:04Z, size = 4408940, hashes = { sha256 = "7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a" } }, + { url = "https://files.pythonhosted.org/packages/d9/b5/c5e179772ec38adb1c072b3aa13937d2860509ba32b2462bf1dda153833b/cryptography-46.0.2-pp311-pypy311_pp73-win_amd64.whl", upload-time = 2025-10-01T00:29:06Z, size = 3438518, hashes = { sha256 = "c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612" } }, +] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", upload-time = 2023-12-24T09:54:32Z, size = 60722, hashes = { sha256 = "2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", upload-time = 2023-12-24T09:54:30Z, size = 20277, hashes = { sha256 = "7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2" } }] + +[[packages]] +name = "fastapi" +version = "0.119.0" +sdist = { url = "https://files.pythonhosted.org/packages/0a/f9/5c5bcce82a7997cc0eb8c47b7800f862f6b56adc40486ed246e5010d443b/fastapi-0.119.0.tar.gz", upload-time = 2025-10-11T17:13:40Z, size = 336756, hashes = { sha256 = "451082403a2c1f0b99c6bd57c09110ed5463856804c8078d38e5a1f1035dbbb7" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/ce/70/584c4d7cad80f5e833715c0a29962d7c93b4d18eed522a02981a6d1b6ee5/fastapi-0.119.0-py3-none-any.whl", upload-time = 2025-10-11T17:13:39Z, size = 107095, hashes = { sha256 = "90a2e49ed19515320abb864df570dd766be0662c5d577688f1600170f7f73cf2" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, -] +version = "3.20.0" +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", upload-time = 2025-10-08T18:03:50Z, size = 18922, hashes = { sha256 = "711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", upload-time = 2025-10-08T18:03:48Z, size = 16054, hashes = { sha256 = "339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2" } }] -[[package]] +[[packages]] name = "fsspec" -version = "2025.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/d8/8425e6ba5fcec61a1d16e41b1b71d2bf9344f1fe48012c2b48b9620feae5/fsspec-2025.3.2.tar.gz", hash = "sha256:e52c77ef398680bbd6a98c0e628fbc469491282981209907bbc8aea76a04fdc6", size = 299281 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/4b/e0cfc1a6f17e990f3e64b7d941ddc4acdc7b19d6edd51abf495f32b1a9e4/fsspec-2025.3.2-py3-none-any.whl", hash = "sha256:2daf8dc3d1dfa65b6aa37748d112773a7a08416f6c70d96b264c96476ecaf711", size = 194435 }, -] +version = "2025.9.0" +sdist = { url = "https://files.pythonhosted.org/packages/de/e0/bab50af11c2d75c9c4a2a26a5254573c0bd97cea152254401510950486fa/fsspec-2025.9.0.tar.gz", upload-time = 2025-09-02T19:10:49Z, size = 304847, hashes = { sha256 = "19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/47/71/70db47e4f6ce3e5c37a607355f80da8860a33226be640226ac52cb05ef2e/fsspec-2025.9.0-py3-none-any.whl", upload-time = 2025-09-02T19:10:47Z, size = 199289, hashes = { sha256 = "530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7" } }] + +[[packages]] +name = "ghp-import" +version = "2.1.0" +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", upload-time = 2022-05-02T15:47:16Z, size = 10943, hashes = { sha256 = "9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", upload-time = 2022-05-02T15:47:14Z, size = 11034, hashes = { sha256 = "8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", upload-time = 2025-04-24T03:35:25Z, size = 101250, hashes = { sha256 = "4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", upload-time = 2025-04-24T03:35:24Z, size = 37515, hashes = { sha256 = "63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" } }] -[[package]] +[[packages]] name = "hf-xet" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/2c/70009910fcbd204bde75842b60c1e47fe72edb0e978954cb8001735885c7/hf_xet-1.1.0.tar.gz", hash = "sha256:a7c2a4c2b6eee9ce0a1a367a82b60d95ba634420ef1c250addad7aa4af419cf4", size = 263996 } +version = "1.1.10" +marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" +sdist = { url = "https://files.pythonhosted.org/packages/74/31/feeddfce1748c4a233ec1aa5b7396161c07ae1aa9b7bdbc9a72c3c7dd768/hf_xet-1.1.10.tar.gz", upload-time = 2025-09-12T20:10:27Z, size = 487910, hashes = { sha256 = "408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97" } } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/fd/0db331297e331f0f02005fd7ea666439bf15efd74f0dd62af02a43236a1b/hf_xet-1.1.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0322c42551e275fcb7949c083a54a81b2898e50787c9aa74284fcb8d2c58c12c", size = 5069444 }, - { url = "https://files.pythonhosted.org/packages/b9/7d/4d7ae44219d3744ad55669cb90ef3d4ed9f5f8a4729fa635a6499491cb78/hf_xet-1.1.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:667153a0304ac2debf2af95a8ff7687186f885b493f4cd16344869af270cd110", size = 4881465 }, - { url = "https://files.pythonhosted.org/packages/83/9a/d40d2a57b132d609d8a4ccc29e59ed69749021610616749cabcda2532158/hf_xet-1.1.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995eeffb119636ea617b96c7d7bf3c3f5ea8727fa57974574e25d700b8532d48", size = 53584225 }, - { url = "https://files.pythonhosted.org/packages/2e/01/d94553f91d85746e0862f24d239da88d10f5ce252b028565744e982432f4/hf_xet-1.1.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3aee847da362393331f515c4010d0aaa1c2669acfcca1f4b28946d6949cc0086", size = 52043680 }, - { url = "https://files.pythonhosted.org/packages/29/89/1f31853bf378f0ceb3363c07fd8a12af9b904b1f8c21e65eb5c19397bc98/hf_xet-1.1.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68c5813a6074aa36e12ef5983230e3b03148cce61e0fcdd294096493795565b4", size = 53072672 }, - { url = "https://files.pythonhosted.org/packages/b5/9f/5ecb92b18a4b2135a72a95dc08bcbeda9176f46642c745ee052420d2aea8/hf_xet-1.1.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ee9222bf9274b1c198b88a929de0b5a49349c4962d89c5b3b2f0f7f47d9761c", size = 53521053 }, - { url = "https://files.pythonhosted.org/packages/53/d6/cb32842cbf1cf5a154b41fa918a2fd86003af9bca227a2397cd7f312a8a6/hf_xet-1.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:73153eab9abf3d6973b21e94a67ccba5d595c3e12feb8c0bf50be02964e7f126", size = 4204376 }, + { url = "https://files.pythonhosted.org/packages/f7/a2/343e6d05de96908366bdc0081f2d8607d61200be2ac802769c4284cc65bd/hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", upload-time = 2025-09-12T20:10:22Z, size = 2761466, hashes = { sha256 = "686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d" } }, + { url = "https://files.pythonhosted.org/packages/31/f9/6215f948ac8f17566ee27af6430ea72045e0418ce757260248b483f4183b/hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", upload-time = 2025-09-12T20:10:21Z, size = 2623807, hashes = { sha256 = "71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b" } }, + { url = "https://files.pythonhosted.org/packages/15/07/86397573efefff941e100367bbda0b21496ffcdb34db7ab51912994c32a2/hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-12T20:10:19Z, size = 3186960, hashes = { sha256 = "6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435" } }, + { url = "https://files.pythonhosted.org/packages/01/a7/0b2e242b918cc30e1f91980f3c4b026ff2eedaf1e2ad96933bca164b2869/hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", upload-time = 2025-09-12T20:10:17Z, size = 3087167, hashes = { sha256 = "eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c" } }, + { url = "https://files.pythonhosted.org/packages/4a/25/3e32ab61cc7145b11eee9d745988e2f0f4fafda81b25980eebf97d8cff15/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", upload-time = 2025-09-12T20:10:24Z, size = 3248612, hashes = { sha256 = "0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06" } }, + { url = "https://files.pythonhosted.org/packages/2c/3d/ab7109e607ed321afaa690f557a9ada6d6d164ec852fd6bf9979665dc3d6/hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", upload-time = 2025-09-12T20:10:25Z, size = 3353360, hashes = { sha256 = "f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f" } }, + { url = "https://files.pythonhosted.org/packages/ee/0e/471f0a21db36e71a2f1752767ad77e92d8cde24e974e03d662931b1305ec/hf_xet-1.1.10-cp37-abi3-win_amd64.whl", upload-time = 2025-09-12T20:10:28Z, size = 2804691, hashes = { sha256 = "5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045" } }, ] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, -] - -[[package]] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", upload-time = 2025-04-24T22:06:22Z, size = 85484, hashes = { sha256 = "6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", upload-time = 2025-04-24T22:06:20Z, size = 78784, hashes = { sha256 = "2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55" } }] + +[[packages]] +name = "httptools" +version = "0.7.1" +sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", upload-time = 2025-10-10T03:55:08Z, size = 258961, hashes = { sha256 = "abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2025-10-10T03:54:20Z, size = 204531, hashes = { sha256 = "11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78" } }, + { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:54:22Z, size = 109408, hashes = { sha256 = "84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4" } }, + { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:54:23Z, size = 440889, hashes = { sha256 = "c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05" } }, + { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:54:25Z, size = 440460, hashes = { sha256 = "654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed" } }, + { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:54:26Z, size = 425267, hashes = { sha256 = "b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a" } }, + { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:54:28Z, size = 424429, hashes = { sha256 = "d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b" } }, + { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", upload-time = 2025-10-10T03:54:29Z, size = 86173, hashes = { sha256 = "cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568" } }, + { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2025-10-10T03:54:31Z, size = 206521, hashes = { sha256 = "474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657" } }, + { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:54:31Z, size = 110375, hashes = { sha256 = "a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70" } }, + { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:54:33Z, size = 456621, hashes = { sha256 = "379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df" } }, + { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:54:34Z, size = 454954, hashes = { sha256 = "cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e" } }, + { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:54:35Z, size = 440175, hashes = { sha256 = "eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274" } }, + { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:54:37Z, size = 440310, hashes = { sha256 = "f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec" } }, + { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", upload-time = 2025-10-10T03:54:38Z, size = 86875, hashes = { sha256 = "135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb" } }, + { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2025-10-10T03:54:39Z, size = 206280, hashes = { sha256 = "38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5" } }, + { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:54:40Z, size = 110004, hashes = { sha256 = "f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5" } }, + { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:54:41Z, size = 517655, hashes = { sha256 = "2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03" } }, + { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:54:42Z, size = 511440, hashes = { sha256 = "7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2" } }, + { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:54:43Z, size = 495186, hashes = { sha256 = "f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362" } }, + { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:54:45Z, size = 499192, hashes = { sha256 = "e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c" } }, + { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", upload-time = 2025-10-10T03:54:45Z, size = 86694, hashes = { sha256 = "3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321" } }, + { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2025-10-10T03:54:47Z, size = 202889, hashes = { sha256 = "6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3" } }, + { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:54:48Z, size = 108180, hashes = { sha256 = "601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca" } }, + { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:54:48Z, size = 478596, hashes = { sha256 = "04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c" } }, + { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:54:49Z, size = 473268, hashes = { sha256 = "69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66" } }, + { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:54:51Z, size = 455517, hashes = { sha256 = "44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346" } }, + { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:54:52Z, size = 458337, hashes = { sha256 = "465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650" } }, + { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", upload-time = 2025-10-10T03:54:53Z, size = 85743, hashes = { sha256 = "322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6" } }, + { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", upload-time = 2025-10-10T03:54:54Z, size = 203619, hashes = { sha256 = "c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270" } }, + { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:54:55Z, size = 108714, hashes = { sha256 = "7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3" } }, + { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:54:56Z, size = 472909, hashes = { sha256 = "0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1" } }, + { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:54:57Z, size = 470831, hashes = { sha256 = "df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b" } }, + { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:54:58Z, size = 452631, hashes = { sha256 = "f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60" } }, + { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:54:59Z, size = 452910, hashes = { sha256 = "7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca" } }, + { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", upload-time = 2025-10-10T03:55:00Z, size = 88205, hashes = { sha256 = "cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96" } }, + { url = "https://files.pythonhosted.org/packages/90/de/b1fe0e8890f0292c266117d4cd268186758a9c34e576fbd573fdf3beacff/httptools-0.7.1-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2025-10-10T03:55:01Z, size = 206454, hashes = { sha256 = "ac50afa68945df63ec7a2707c506bd02239272288add34539a2ef527254626a4" } }, + { url = "https://files.pythonhosted.org/packages/57/a7/a675c90b49e550c7635ce209c01bc61daa5b08aef17da27ef4e0e78fcf3f/httptools-0.7.1-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-10-10T03:55:02Z, size = 110260, hashes = { sha256 = "de987bb4e7ac95b99b805b99e0aae0ad51ae61df4263459d36e07cf4052d8b3a" } }, + { url = "https://files.pythonhosted.org/packages/03/44/fb5ef8136e6e97f7b020e97e40c03a999f97e68574d4998fa52b0a62b01b/httptools-0.7.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", upload-time = 2025-10-10T03:55:03Z, size = 441524, hashes = { sha256 = "d169162803a24425eb5e4d51d79cbf429fd7a491b9e570a55f495ea55b26f0bf" } }, + { url = "https://files.pythonhosted.org/packages/b4/62/8496a5425341867796d7e2419695f74a74607054e227bbaeabec8323e87f/httptools-0.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-10-10T03:55:04Z, size = 440877, hashes = { sha256 = "49794f9250188a57fa73c706b46cb21a313edb00d337ca4ce1a011fe3c760b28" } }, + { url = "https://files.pythonhosted.org/packages/e8/f1/26c2e5214106bf6ed04d03e518ff28ca0c6b5390c5da7b12bbf94b40ae43/httptools-0.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-10-10T03:55:05Z, size = 425775, hashes = { sha256 = "aeefa0648362bb97a7d6b5ff770bfb774930a327d7f65f8208394856862de517" } }, + { url = "https://files.pythonhosted.org/packages/3a/34/7500a19257139725281f7939a7d1aa3701cf1ac4601a1690f9ab6f510e15/httptools-0.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-10-10T03:55:06Z, size = 425001, hashes = { sha256 = "0d92b10dbf0b3da4823cde6a96d18e6ae358a9daa741c71448975f6a2c339cad" } }, + { url = "https://files.pythonhosted.org/packages/71/04/31a7949d645ebf33a67f56a0024109444a52a271735e0647a210264f3e61/httptools-0.7.1-cp39-cp39-win_amd64.whl", upload-time = 2025-10-10T03:55:07Z, size = 86818, hashes = { sha256 = "5ddbd045cfcb073db2449563dd479057f2c2b681ebc232380e63ef15edc9c023" } }, +] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", upload-time = 2024-12-06T15:37:23Z, size = 141406, hashes = { sha256 = "75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", upload-time = 2024-12-06T15:37:21Z, size = 73517, hashes = { sha256 = "d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" } }] -[[package]] +[[packages]] name = "huggingface-hub" -version = "0.31.1" -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/25/eb/9268c1205d19388659d5dc664f012177b752c0eef194a9159acc7227780f/huggingface_hub-0.31.1.tar.gz", hash = "sha256:492bb5f545337aa9e2f59b75ef4c5f535a371e8958a6ce90af056387e67f1180", size = 403036 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/bf/6002da17ec1c7a47bedeb216812929665927c70b6e7500b3c7bf36f01bdd/huggingface_hub-0.31.1-py3-none-any.whl", hash = "sha256:43f73124819b48b42d140cbc0d7a2e6bd15b2853b1b9d728d4d55ad1750cac5b", size = 484265 }, -] +version = "0.35.3" +sdist = { url = "https://files.pythonhosted.org/packages/10/7e/a0a97de7c73671863ca6b3f61fa12518caf35db37825e43d63a70956738c/huggingface_hub-0.35.3.tar.gz", upload-time = 2025-09-29T14:29:58Z, size = 461798, hashes = { sha256 = "350932eaa5cc6a4747efae85126ee220e4ef1b54e29d31c3b45c5612ddf0b32a" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/31/a0/651f93d154cb72323358bf2bbae3e642bdb5d2f1bfc874d096f7cb159fa0/huggingface_hub-0.35.3-py3-none-any.whl", upload-time = 2025-09-29T14:29:55Z, size = 564262, hashes = { sha256 = "0e3a01829c19d86d03793e4577816fe3bdfc1602ac62c7fb220d593d351224ba" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] +version = "3.11" +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", upload-time = 2025-10-12T14:55:20Z, size = 194582, hashes = { sha256 = "795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", upload-time = 2025-10-12T14:55:18Z, size = 71008, hashes = { sha256 = "771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", upload-time = 2025-03-19T20:09:59Z, size = 4793, hashes = { sha256 = "3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", upload-time = 2025-03-19T20:10:01Z, size = 6050, hashes = { sha256 = "9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", upload-time = 2025-03-05T20:05:02Z, size = 245115, hashes = { sha256 = "0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", upload-time = 2025-03-05T20:05:00Z, size = 134899, hashes = { sha256 = "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" } }] -[[package]] +[[packages]] name = "jiter" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/1b/4cd165c362e8f2f520fdb43245e2b414f42a255921248b4f8b9c8d871ff1/jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7", size = 308197 }, - { url = "https://files.pythonhosted.org/packages/13/aa/7a890dfe29c84c9a82064a9fe36079c7c0309c91b70c380dc138f9bea44a/jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b", size = 318160 }, - { url = "https://files.pythonhosted.org/packages/6a/38/5888b43fc01102f733f085673c4f0be5a298f69808ec63de55051754e390/jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69", size = 341259 }, - { url = "https://files.pythonhosted.org/packages/3d/5e/bbdbb63305bcc01006de683b6228cd061458b9b7bb9b8d9bc348a58e5dc2/jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103", size = 363730 }, - { url = "https://files.pythonhosted.org/packages/75/85/53a3edc616992fe4af6814c25f91ee3b1e22f7678e979b6ea82d3bc0667e/jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635", size = 405126 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/1ee26b12b2693bd3f0b71d3188e4e5d817b12e3c630a09e099e0a89e28fa/jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4", size = 393668 }, - { url = "https://files.pythonhosted.org/packages/11/87/e084ce261950c1861773ab534d49127d1517b629478304d328493f980791/jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d", size = 352350 }, - { url = "https://files.pythonhosted.org/packages/f0/06/7dca84b04987e9df563610aa0bc154ea176e50358af532ab40ffb87434df/jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3", size = 384204 }, - { url = "https://files.pythonhosted.org/packages/16/2f/82e1c6020db72f397dd070eec0c85ebc4df7c88967bc86d3ce9864148f28/jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5", size = 520322 }, - { url = "https://files.pythonhosted.org/packages/36/fd/4f0cd3abe83ce208991ca61e7e5df915aa35b67f1c0633eb7cf2f2e88ec7/jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d", size = 512184 }, - { url = "https://files.pythonhosted.org/packages/a0/3c/8a56f6d547731a0b4410a2d9d16bf39c861046f91f57c98f7cab3d2aa9ce/jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53", size = 206504 }, - { url = "https://files.pythonhosted.org/packages/f4/1c/0c996fd90639acda75ed7fa698ee5fd7d80243057185dc2f63d4c1c9f6b9/jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7", size = 204943 }, - { url = "https://files.pythonhosted.org/packages/78/0f/77a63ca7aa5fed9a1b9135af57e190d905bcd3702b36aca46a01090d39ad/jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001", size = 317281 }, - { url = "https://files.pythonhosted.org/packages/f9/39/a3a1571712c2bf6ec4c657f0d66da114a63a2e32b7e4eb8e0b83295ee034/jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a", size = 350273 }, - { url = "https://files.pythonhosted.org/packages/ee/47/3729f00f35a696e68da15d64eb9283c330e776f3b5789bac7f2c0c4df209/jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf", size = 206867 }, -] - -[[package]] +version = "0.11.0" +sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/a3bb4cc13aced219dd18191ea66e874266bd8aa7b96744e495e1c733aa2d/jiter-0.11.0.tar.gz", upload-time = 2025-09-15T09:20:38Z, size = 167094, hashes = { sha256 = "1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/21/7dd1235a19e26979be6098e87e4cced2e061752f3a40a17bbce6dea7fae1/jiter-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:18:48Z, size = 309875, hashes = { sha256 = "3893ce831e1c0094a83eeaf56c635a167d6fa8cc14393cc14298fd6fdc2a2449" } }, + { url = "https://files.pythonhosted.org/packages/71/f9/462b54708aa85b135733ccba70529dd68a18511bf367a87c5fd28676c841/jiter-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:18:51Z, size = 316505, hashes = { sha256 = "25c625b9b61b5a8725267fdf867ef2e51b429687f6a4eef211f4612e95607179" } }, + { url = "https://files.pythonhosted.org/packages/bd/40/14e2eeaac6a47bff27d213834795472355fd39769272eb53cb7aa83d5aa8/jiter-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:18:52Z, size = 337613, hashes = { sha256 = "dd4ca85fb6a62cf72e1c7f5e34ddef1b660ce4ed0886ec94a1ef9777d35eaa1f" } }, + { url = "https://files.pythonhosted.org/packages/d3/ed/a5f1f8419c92b150a7c7fb5ccba1fb1e192887ad713d780e70874f0ce996/jiter-0.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:18:54Z, size = 361438, hashes = { sha256 = "572208127034725e79c28437b82414028c3562335f2b4f451d98136d0fc5f9cd" } }, + { url = "https://files.pythonhosted.org/packages/dd/f5/70682c023dfcdd463a53faf5d30205a7d99c51d70d3e303c932d0936e5a2/jiter-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:18:56Z, size = 486180, hashes = { sha256 = "494ba627c7f550ad3dabb21862864b8f2216098dc18ff62f37b37796f2f7c325" } }, + { url = "https://files.pythonhosted.org/packages/7c/39/020d08cbab4eab48142ad88b837c41eb08a15c0767fdb7c0d3265128a44b/jiter-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:18:57Z, size = 376681, hashes = { sha256 = "b8da18a99f58bca3ecc2d2bba99cac000a924e115b6c4f0a2b98f752b6fbf39a" } }, + { url = "https://files.pythonhosted.org/packages/52/10/b86733f6e594cf51dd142f37c602d8df87c554c5844958deaab0de30eb5d/jiter-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:18:59Z, size = 348685, hashes = { sha256 = "e4ffd3b0fff3fabbb02cc09910c08144db6bb5697a98d227a074401e01ee63dd" } }, + { url = "https://files.pythonhosted.org/packages/fb/ee/8861665e83a9e703aa5f65fddddb6225428e163e6b0baa95a7f9a8fb9aae/jiter-0.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:19:00Z, size = 385573, hashes = { sha256 = "8fe6530aa738a4f7d4e4702aa8f9581425d04036a5f9e25af65ebe1f708f23be" } }, + { url = "https://files.pythonhosted.org/packages/25/74/05afec03600951f128293813b5a208c9ba1bf587c57a344c05a42a69e1b1/jiter-0.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:19:02Z, size = 516669, hashes = { sha256 = "e35d66681c133a03d7e974e7eedae89720fe8ca3bd09f01a4909b86a8adf31f5" } }, + { url = "https://files.pythonhosted.org/packages/93/d1/2e5bfe147cfbc2a5eef7f73eb75dc5c6669da4fa10fc7937181d93af9495/jiter-0.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:19:04Z, size = 508767, hashes = { sha256 = "c59459beca2fbc9718b6f1acb7bfb59ebc3eb4294fa4d40e9cb679dafdcc6c60" } }, + { url = "https://files.pythonhosted.org/packages/87/50/597f71307e10426b5c082fd05d38c615ddbdd08c3348d8502963307f0652/jiter-0.11.0-cp310-cp310-win32.whl", upload-time = 2025-09-15T09:19:05Z, size = 205476, hashes = { sha256 = "b7b0178417b0dcfc5f259edbc6db2b1f5896093ed9035ee7bab0f2be8854726d" } }, + { url = "https://files.pythonhosted.org/packages/c7/86/1e5214b3272e311754da26e63edec93a183811d4fc2e0118addec365df8b/jiter-0.11.0-cp310-cp310-win_amd64.whl", upload-time = 2025-09-15T09:19:06Z, size = 204708, hashes = { sha256 = "11df2bf99fb4754abddd7f5d940a48e51f9d11624d6313ca4314145fcad347f0" } }, + { url = "https://files.pythonhosted.org/packages/38/55/a69fefeef09c2eaabae44b935a1aa81517e49639c0a0c25d861cb18cd7ac/jiter-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:19:08Z, size = 309503, hashes = { sha256 = "cb5d9db02979c3f49071fce51a48f4b4e4cf574175fb2b11c7a535fa4867b222" } }, + { url = "https://files.pythonhosted.org/packages/bd/d5/a6aba9e6551f32f9c127184f398208e4eddb96c59ac065c8a92056089d28/jiter-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:19:09Z, size = 317688, hashes = { sha256 = "1dc6a123f3471c4730db7ca8ba75f1bb3dcb6faeb8d46dd781083e7dee88b32d" } }, + { url = "https://files.pythonhosted.org/packages/bb/f3/5e86f57c1883971cdc8535d0429c2787bf734840a231da30a3be12850562/jiter-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:19:11Z, size = 337418, hashes = { sha256 = "09858f8d230f031c7b8e557429102bf050eea29c77ad9c34c8fe253c5329acb7" } }, + { url = "https://files.pythonhosted.org/packages/5e/4f/a71d8a24c2a70664970574a8e0b766663f5ef788f7fe1cc20ee0c016d488/jiter-0.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:19:13Z, size = 361423, hashes = { sha256 = "dbe2196c4a0ce760925a74ab4456bf644748ab0979762139626ad138f6dac72d" } }, + { url = "https://files.pythonhosted.org/packages/8f/e5/b09076f4e7fd9471b91e16f9f3dc7330b161b738f3b39b2c37054a36e26a/jiter-0.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:19:14Z, size = 486367, hashes = { sha256 = "5beb56d22b63647bafd0b74979216fdee80c580c0c63410be8c11053860ffd09" } }, + { url = "https://files.pythonhosted.org/packages/fb/f1/98cb3a36f5e62f80cd860f0179f948d9eab5a316d55d3e1bab98d9767af5/jiter-0.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:19:15Z, size = 376335, hashes = { sha256 = "97025d09ef549795d8dc720a824312cee3253c890ac73c621721ddfc75066789" } }, + { url = "https://files.pythonhosted.org/packages/9f/d8/ec74886497ea393c29dbd7651ddecc1899e86404a6b1f84a3ddab0ab59fd/jiter-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:19:17Z, size = 348981, hashes = { sha256 = "d50880a6da65d8c23a2cf53c412847d9757e74cc9a3b95c5704a1d1a24667347" } }, + { url = "https://files.pythonhosted.org/packages/24/93/d22ad7fa3b86ade66c86153ceea73094fc2af8b20c59cb7fceab9fea4704/jiter-0.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:19:19Z, size = 385797, hashes = { sha256 = "452d80a1c86c095a242007bd9fc5d21b8a8442307193378f891cb8727e469648" } }, + { url = "https://files.pythonhosted.org/packages/c8/bd/e25ff4a4df226e9b885f7cb01ee4b9dc74e3000e612d6f723860d71a1f34/jiter-0.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:19:20Z, size = 516597, hashes = { sha256 = "e84e58198d4894668eec2da660ffff60e0f3e60afa790ecc50cb12b0e02ca1d4" } }, + { url = "https://files.pythonhosted.org/packages/be/fb/beda613db7d93ffa2fdd2683f90f2f5dce8daf4bc2d0d2829e7de35308c6/jiter-0.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:19:22Z, size = 508853, hashes = { sha256 = "df64edcfc5dd5279a791eea52aa113d432c933119a025b0b5739f90d2e4e75f1" } }, + { url = "https://files.pythonhosted.org/packages/20/64/c5b0d93490634e41e38e2a15de5d54fdbd2c9f64a19abb0f95305b63373c/jiter-0.11.0-cp311-cp311-win32.whl", upload-time = 2025-09-15T09:19:23Z, size = 205140, hashes = { sha256 = "144fc21337d21b1d048f7f44bf70881e1586401d405ed3a98c95a114a9994982" } }, + { url = "https://files.pythonhosted.org/packages/a1/e6/c347c0e6f5796e97d4356b7e5ff0ce336498b7f4ef848fae621a56f1ccf3/jiter-0.11.0-cp311-cp311-win_amd64.whl", upload-time = 2025-09-15T09:19:24Z, size = 204311, hashes = { sha256 = "b0f32e644d241293b892b1a6dd8f0b9cc029bfd94c97376b2681c36548aabab7" } }, + { url = "https://files.pythonhosted.org/packages/ba/b5/3009b112b8f673e568ef79af9863d8309a15f0a8cdcc06ed6092051f377e/jiter-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:19:25Z, size = 305510, hashes = { sha256 = "2fb7b377688cc3850bbe5c192a6bd493562a0bc50cbc8b047316428fbae00ada" } }, + { url = "https://files.pythonhosted.org/packages/fe/82/15514244e03b9e71e086bbe2a6de3e4616b48f07d5f834200c873956fb8c/jiter-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:19:27Z, size = 316521, hashes = { sha256 = "a1b7cbe3f25bd0d8abb468ba4302a5d45617ee61b2a7a638f63fee1dc086be99" } }, + { url = "https://files.pythonhosted.org/packages/92/94/7a2e905f40ad2d6d660e00b68d818f9e29fb87ffe82774f06191e93cbe4a/jiter-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:19:28Z, size = 338214, hashes = { sha256 = "c0a7f0ec81d5b7588c5cade1eb1925b91436ae6726dc2df2348524aeabad5de6" } }, + { url = "https://files.pythonhosted.org/packages/a8/9c/5791ed5bdc76f12110158d3316a7a3ec0b1413d018b41c5ed399549d3ad5/jiter-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:19:30Z, size = 361280, hashes = { sha256 = "07630bb46ea2a6b9c6ed986c6e17e35b26148cce2c535454b26ee3f0e8dcaba1" } }, + { url = "https://files.pythonhosted.org/packages/d4/7f/b7d82d77ff0d2cb06424141000176b53a9e6b16a1125525bb51ea4990c2e/jiter-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:19:31Z, size = 487895, hashes = { sha256 = "7764f27d28cd4a9cbc61704dfcd80c903ce3aad106a37902d3270cd6673d17f4" } }, + { url = "https://files.pythonhosted.org/packages/42/44/10a1475d46f1fc1fd5cc2e82c58e7bca0ce5852208e0fa5df2f949353321/jiter-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:19:32Z, size = 378421, hashes = { sha256 = "1d4a6c4a737d486f77f842aeb22807edecb4a9417e6700c7b981e16d34ba7c72" } }, + { url = "https://files.pythonhosted.org/packages/9a/5f/0dc34563d8164d31d07bc09d141d3da08157a68dcd1f9b886fa4e917805b/jiter-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:19:34Z, size = 347932, hashes = { sha256 = "cf408d2a0abd919b60de8c2e7bc5eeab72d4dafd18784152acc7c9adc3291591" } }, + { url = "https://files.pythonhosted.org/packages/f7/de/b68f32a4fcb7b4a682b37c73a0e5dae32180140cd1caf11aef6ad40ddbf2/jiter-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:19:35Z, size = 386959, hashes = { sha256 = "cdef53eda7d18e799625023e1e250dbc18fbc275153039b873ec74d7e8883e09" } }, + { url = "https://files.pythonhosted.org/packages/76/0a/c08c92e713b6e28972a846a81ce374883dac2f78ec6f39a0dad9f2339c3a/jiter-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:19:37Z, size = 517187, hashes = { sha256 = "53933a38ef7b551dd9c7f1064f9d7bb235bb3168d0fa5f14f0798d1b7ea0d9c5" } }, + { url = "https://files.pythonhosted.org/packages/89/b5/4a283bec43b15aad54fcae18d951f06a2ec3f78db5708d3b59a48e9c3fbd/jiter-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:19:38Z, size = 509461, hashes = { sha256 = "11840d2324c9ab5162fc1abba23bc922124fedcff0d7b7f85fffa291e2f69206" } }, + { url = "https://files.pythonhosted.org/packages/34/a5/f8bad793010534ea73c985caaeef8cc22dfb1fedb15220ecdf15c623c07a/jiter-0.11.0-cp312-cp312-win32.whl", upload-time = 2025-09-15T09:19:40Z, size = 206664, hashes = { sha256 = "4f01a744d24a5f2bb4a11657a1b27b61dc038ae2e674621a74020406e08f749b" } }, + { url = "https://files.pythonhosted.org/packages/ed/42/5823ec2b1469395a160b4bf5f14326b4a098f3b6898fbd327366789fa5d3/jiter-0.11.0-cp312-cp312-win_amd64.whl", upload-time = 2025-09-15T09:19:41Z, size = 203520, hashes = { sha256 = "29fff31190ab3a26de026da2f187814f4b9c6695361e20a9ac2123e4d4378a4c" } }, + { url = "https://files.pythonhosted.org/packages/97/c4/d530e514d0f4f29b2b68145e7b389cbc7cac7f9c8c23df43b04d3d10fa3e/jiter-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:19:43Z, size = 305021, hashes = { sha256 = "4441a91b80a80249f9a6452c14b2c24708f139f64de959943dfeaa6cb915e8eb" } }, + { url = "https://files.pythonhosted.org/packages/7a/77/796a19c567c5734cbfc736a6f987affc0d5f240af8e12063c0fb93990ffa/jiter-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:19:44Z, size = 314384, hashes = { sha256 = "ff85fc6d2a431251ad82dbd1ea953affb5a60376b62e7d6809c5cd058bb39471" } }, + { url = "https://files.pythonhosted.org/packages/14/9c/824334de0b037b91b6f3fa9fe5a191c83977c7ec4abe17795d3cb6d174cf/jiter-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:19:46Z, size = 337389, hashes = { sha256 = "c5e86126d64706fd28dfc46f910d496923c6f95b395138c02d0e252947f452bd" } }, + { url = "https://files.pythonhosted.org/packages/a2/95/ed4feab69e6cf9b2176ea29d4ef9d01a01db210a3a2c8a31a44ecdc68c38/jiter-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:19:47Z, size = 360519, hashes = { sha256 = "4ad8bd82165961867a10f52010590ce0b7a8c53da5ddd8bbb62fef68c181b921" } }, + { url = "https://files.pythonhosted.org/packages/b5/0c/2ad00f38d3e583caba3909d95b7da1c3a7cd82c0aa81ff4317a8016fb581/jiter-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:19:49Z, size = 487198, hashes = { sha256 = "b42c2cd74273455ce439fd9528db0c6e84b5623cb74572305bdd9f2f2961d3df" } }, + { url = "https://files.pythonhosted.org/packages/ea/8b/919b64cf3499b79bdfba6036da7b0cac5d62d5c75a28fb45bad7819e22f0/jiter-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:19:50Z, size = 377835, hashes = { sha256 = "f0062dab98172dd0599fcdbf90214d0dcde070b1ff38a00cc1b90e111f071982" } }, + { url = "https://files.pythonhosted.org/packages/29/7f/8ebe15b6e0a8026b0d286c083b553779b4dd63db35b43a3f171b544de91d/jiter-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:19:51Z, size = 347655, hashes = { sha256 = "bb948402821bc76d1f6ef0f9e19b816f9b09f8577844ba7140f0b6afe994bc64" } }, + { url = "https://files.pythonhosted.org/packages/8e/64/332127cef7e94ac75719dda07b9a472af6158ba819088d87f17f3226a769/jiter-0.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:19:53Z, size = 386135, hashes = { sha256 = "25a5b1110cca7329fd0daf5060faa1234be5c11e988948e4f1a1923b6a457fe1" } }, + { url = "https://files.pythonhosted.org/packages/20/c8/557b63527442f84c14774159948262a9d4fabb0d61166f11568f22fc60d2/jiter-0.11.0-cp313-cp313-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:19:54Z, size = 516063, hashes = { sha256 = "bf11807e802a214daf6c485037778843fadd3e2ec29377ae17e0706ec1a25758" } }, + { url = "https://files.pythonhosted.org/packages/86/13/4164c819df4a43cdc8047f9a42880f0ceef5afeb22e8b9675c0528ebdccd/jiter-0.11.0-cp313-cp313-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:19:55Z, size = 508139, hashes = { sha256 = "dbb57da40631c267861dd0090461222060960012d70fd6e4c799b0f62d0ba166" } }, + { url = "https://files.pythonhosted.org/packages/fa/70/6e06929b401b331d41ddb4afb9f91cd1168218e3371972f0afa51c9f3c31/jiter-0.11.0-cp313-cp313-win32.whl", upload-time = 2025-09-15T09:19:57Z, size = 206369, hashes = { sha256 = "8e36924dad32c48d3c5e188d169e71dc6e84d6cb8dedefea089de5739d1d2f80" } }, + { url = "https://files.pythonhosted.org/packages/f4/0d/8185b8e15de6dce24f6afae63380e16377dd75686d56007baa4f29723ea1/jiter-0.11.0-cp313-cp313-win_amd64.whl", upload-time = 2025-09-15T09:19:58Z, size = 202538, hashes = { sha256 = "452d13e4fd59698408087235259cebe67d9d49173b4dacb3e8d35ce4acf385d6" } }, + { url = "https://files.pythonhosted.org/packages/13/3a/d61707803260d59520721fa326babfae25e9573a88d8b7b9cb54c5423a59/jiter-0.11.0-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:19:59Z, size = 313737, hashes = { sha256 = "089f9df9f69532d1339e83142438668f52c97cd22ee2d1195551c2b1a9e6cf33" } }, + { url = "https://files.pythonhosted.org/packages/cd/cc/c9f0eec5d00f2a1da89f6bdfac12b8afdf8d5ad974184863c75060026457/jiter-0.11.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:20:01Z, size = 346183, hashes = { sha256 = "29ed1fe69a8c69bf0f2a962d8d706c7b89b50f1332cd6b9fbda014f60bd03a03" } }, + { url = "https://files.pythonhosted.org/packages/a6/87/fc632776344e7aabbab05a95a0075476f418c5d29ab0f2eec672b7a1f0ac/jiter-0.11.0-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-15T09:20:03Z, size = 204225, hashes = { sha256 = "a4d71d7ea6ea8786291423fe209acf6f8d398a0759d03e7f24094acb8ab686ba" } }, + { url = "https://files.pythonhosted.org/packages/ee/3b/e7f45be7d3969bdf2e3cd4b816a7a1d272507cd0edd2d6dc4b07514f2d9a/jiter-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:20:04Z, size = 304414, hashes = { sha256 = "9a6dff27eca70930bdbe4cbb7c1a4ba8526e13b63dc808c0670083d2d51a4a72" } }, + { url = "https://files.pythonhosted.org/packages/06/32/13e8e0d152631fcc1907ceb4943711471be70496d14888ec6e92034e2caf/jiter-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:20:05Z, size = 314223, hashes = { sha256 = "b1ae2a7593a62132c7d4c2abbee80bbbb94fdc6d157e2c6cc966250c564ef774" } }, + { url = "https://files.pythonhosted.org/packages/0c/7e/abedd5b5a20ca083f778d96bba0d2366567fcecb0e6e34ff42640d5d7a18/jiter-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:20:06Z, size = 337306, hashes = { sha256 = "7b13a431dba4b059e9e43019d3022346d009baf5066c24dcdea321a303cde9f0" } }, + { url = "https://files.pythonhosted.org/packages/ac/e2/30d59bdc1204c86aa975ec72c48c482fee6633120ee9c3ab755e4dfefea8/jiter-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:20:08Z, size = 360565, hashes = { sha256 = "af62e84ca3889604ebb645df3b0a3f3bcf6b92babbff642bd214616f57abb93a" } }, + { url = "https://files.pythonhosted.org/packages/fe/88/567288e0d2ed9fa8f7a3b425fdaf2cb82b998633c24fe0d98f5417321aa8/jiter-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:20:09Z, size = 486465, hashes = { sha256 = "c6f3b32bb723246e6b351aecace52aba78adb8eeb4b2391630322dc30ff6c773" } }, + { url = "https://files.pythonhosted.org/packages/18/6e/7b72d09273214cadd15970e91dd5ed9634bee605176107db21e1e4205eb1/jiter-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:20:10Z, size = 377581, hashes = { sha256 = "adcab442f4a099a358a7f562eaa54ed6456fb866e922c6545a717be51dbed7d7" } }, + { url = "https://files.pythonhosted.org/packages/58/52/4db456319f9d14deed325f70102577492e9d7e87cf7097bda9769a1fcacb/jiter-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:20:12Z, size = 347102, hashes = { sha256 = "c9967c2ab338ee2b2c0102fd379ec2693c496abf71ffd47e4d791d1f593b68e2" } }, + { url = "https://files.pythonhosted.org/packages/ce/b4/433d5703c38b26083aec7a733eb5be96f9c6085d0e270a87ca6482cbf049/jiter-0.11.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:20:13Z, size = 386477, hashes = { sha256 = "e7d0bed3b187af8b47a981d9742ddfc1d9b252a7235471ad6078e7e4e5fe75c2" } }, + { url = "https://files.pythonhosted.org/packages/c8/7a/a60bfd9c55b55b07c5c441c5085f06420b6d493ce9db28d069cc5b45d9f3/jiter-0.11.0-cp314-cp314-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:20:14Z, size = 516004, hashes = { sha256 = "f6fe0283e903ebc55f1a6cc569b8c1f3bf4abd026fed85e3ff8598a9e6f982f0" } }, + { url = "https://files.pythonhosted.org/packages/2e/46/f8363e5ecc179b4ed0ca6cb0a6d3bfc266078578c71ff30642ea2ce2f203/jiter-0.11.0-cp314-cp314-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:20:16Z, size = 507855, hashes = { sha256 = "4ee5821e3d66606b29ae5b497230b304f1376f38137d69e35f8d2bd5f310ff73" } }, + { url = "https://files.pythonhosted.org/packages/90/33/396083357d51d7ff0f9805852c288af47480d30dd31d8abc74909b020761/jiter-0.11.0-cp314-cp314-win32.whl", upload-time = 2025-09-15T09:20:17Z, size = 205802, hashes = { sha256 = "c2d13ba7567ca8799f17c76ed56b1d49be30df996eb7fa33e46b62800562a5e2" } }, + { url = "https://files.pythonhosted.org/packages/e7/ab/eb06ca556b2551d41de7d03bf2ee24285fa3d0c58c5f8d95c64c9c3281b1/jiter-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:20:18Z, size = 313405, hashes = { sha256 = "fb4790497369d134a07fc763cc88888c46f734abdd66f9fdf7865038bf3a8f40" } }, + { url = "https://files.pythonhosted.org/packages/af/22/7ab7b4ec3a1c1f03aef376af11d23b05abcca3fb31fbca1e7557053b1ba2/jiter-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:20:20Z, size = 347102, hashes = { sha256 = "6e2bbf24f16ba5ad4441a9845e40e4ea0cb9eed00e76ba94050664ef53ef4406" } }, + { url = "https://files.pythonhosted.org/packages/e5/d9/51cf35d92bea21f2051da8ca2328831589e67e2bf971e85b1a6e6c0d2030/jiter-0.11.0-cp39-cp39-macosx_10_12_x86_64.whl", upload-time = 2025-09-15T09:20:21Z, size = 312764, hashes = { sha256 = "719891c2fb7628a41adff4f2f54c19380a27e6fdfdb743c24680ef1a54c67bd0" } }, + { url = "https://files.pythonhosted.org/packages/da/48/eae309ce5c180faa1bb45e378a503717da22ceb2b0488f78e548f97c2b6b/jiter-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-15T09:20:22Z, size = 305861, hashes = { sha256 = "df7f1927cbdf34cb91262a5418ca06920fd42f1cf733936d863aeb29b45a14ef" } }, + { url = "https://files.pythonhosted.org/packages/83/4f/13b80e18b0331f0fecc09cb2f09f722530b9a395006941b01491fe58baea/jiter-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-15T09:20:23Z, size = 339507, hashes = { sha256 = "e71ae6d969d0c9bab336c5e9e2fabad31e74d823f19e3604eaf96d9a97f463df" } }, + { url = "https://files.pythonhosted.org/packages/97/6d/c2fd1512873d3f23d24537e97765e7090a00de466516aa442b994b064918/jiter-0.11.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-15T09:20:25Z, size = 363751, hashes = { sha256 = "5661469a7b2be25ade3a4bb6c21ffd1e142e13351a0759f264dfdd3ad99af1ab" } }, + { url = "https://files.pythonhosted.org/packages/b0/99/48d156c742e75d33b9c8be44b1142d233823be491acdb1009629e4109e6a/jiter-0.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-15T09:20:26Z, size = 488591, hashes = { sha256 = "76c15ef0d3d02f8b389066fa4c410a0b89e9cc6468a1f0674c5925d2f3c3e890" } }, + { url = "https://files.pythonhosted.org/packages/ba/fd/214452149f63847b791b1f6e9558f59e94674c47418c03e9787236ac8656/jiter-0.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-15T09:20:27Z, size = 378906, hashes = { sha256 = "63782a1350917a27817030716566ed3d5b3c731500fd42d483cbd7094e2c5b25" } }, + { url = "https://files.pythonhosted.org/packages/de/91/25e38fbbfc17111d7b70b24290a41d611cc2a27fa6cd0ed84ddae38ec3e6/jiter-0.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:20:28Z, size = 350288, hashes = { sha256 = "5a7092b699646a1ddc03a7b112622d9c066172627c7382659befb0d2996f1659" } }, + { url = "https://files.pythonhosted.org/packages/b5/d8/d6d2eefa9f0ff6ac6b725f5164a94f15bb4dee68584b5054043d735803da/jiter-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-09-15T09:20:30Z, size = 388442, hashes = { sha256 = "f637b8e818f6d75540f350a6011ce21252573c0998ea1b4365ee54b7672c23c5" } }, + { url = "https://files.pythonhosted.org/packages/ed/e4/cd7e27852de498d441a575a147ac7a15cf66768ae2cde8c43ea5b464c827/jiter-0.11.0-cp39-cp39-musllinux_1_1_aarch64.whl", upload-time = 2025-09-15T09:20:31Z, size = 518273, hashes = { sha256 = "a624d87719e1b5d09c15286eaee7e1532a40c692a096ea7ca791121365f548c1" } }, + { url = "https://files.pythonhosted.org/packages/77/a2/6681a9a503141752b33c92c58512ed8da13849ed3dbf88a3f8aba9bfb255/jiter-0.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", upload-time = 2025-09-15T09:20:32Z, size = 510254, hashes = { sha256 = "a9d0146d8d9b3995821bb586fc8256636258947c2f39da5bab709f3a28fb1a0b" } }, + { url = "https://files.pythonhosted.org/packages/38/32/df1a06f397074da35cf8fe79ec07da203358a2912b2a6349a0d4a88a1e0a/jiter-0.11.0-cp39-cp39-win32.whl", upload-time = 2025-09-15T09:20:34Z, size = 207076, hashes = { sha256 = "d067655a7cf0831eb8ec3e39cbd752995e9b69a2206df3535b3a067fac23b032" } }, + { url = "https://files.pythonhosted.org/packages/1d/91/11bc61fa76fd6197f5baa8576614852ee8586a16c2f25085edc6b47a369d/jiter-0.11.0-cp39-cp39-win_amd64.whl", upload-time = 2025-09-15T09:20:35Z, size = 206077, hashes = { sha256 = "f05d03775a11aaf132c447436983169958439f1219069abf24662a672851f94e" } }, + { url = "https://files.pythonhosted.org/packages/70/f3/ce100253c80063a7b8b406e1d1562657fd4b9b4e1b562db40e68645342fb/jiter-0.11.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-15T09:20:36Z, size = 336380, hashes = { sha256 = "902b43386c04739229076bd1c4c69de5d115553d982ab442a8ae82947c72ede7" } }, +] + +[[packages]] name = "joblib" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/08/8bd4a0250247861420a040b33ccf42f43c426ac91d99405374ef117e5872/joblib-1.5.0.tar.gz", hash = "sha256:d8757f955389a3dd7a23152e43bc297c2e0c2d3060056dad0feefc88a06939b5", size = 330234 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/d3/13ee227a148af1c693654932b8b0b02ed64af5e1f7406d56b088b57574cd/joblib-1.5.0-py3-none-any.whl", hash = "sha256:206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491", size = 307682 }, -] +version = "1.5.2" +sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", upload-time = 2025-08-27T12:15:46Z, size = 331077, hashes = { sha256 = "3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", upload-time = 2025-08-27T12:15:45Z, size = 308396, hashes = { sha256 = "4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241" } }] -[[package]] +[[packages]] name = "kombu" -version = "5.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "amqp" }, - { name = "tzdata" }, - { name = "vine" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/60/0a/128b65651ed8120460fc5af754241ad595eac74993115ec0de4f2d7bc459/kombu-5.5.3.tar.gz", hash = "sha256:021a0e11fcfcd9b0260ef1fb64088c0e92beb976eb59c1dfca7ddd4ad4562ea2", size = 461784 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/35/1407fb0b2f5b07b50cbaf97fce09ad87d3bfefbf64f7171a8651cd8d2f68/kombu-5.5.3-py3-none-any.whl", hash = "sha256:5b0dbceb4edee50aa464f59469d34b97864be09111338cfb224a10b6a163909b", size = 209921 }, -] +version = "5.5.4" +sdist = { url = "https://files.pythonhosted.org/packages/0f/d3/5ff936d8319ac86b9c409f1501b07c426e6ad41966fedace9ef1b966e23f/kombu-5.5.4.tar.gz", upload-time = 2025-06-01T10:19:22Z, size = 461992, hashes = { sha256 = "886600168275ebeada93b888e831352fe578168342f0d1d5833d88ba0d847363" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/ef/70/a07dcf4f62598c8ad579df241af55ced65bed76e42e45d3c368a6d82dbc1/kombu-5.5.4-py3-none-any.whl", upload-time = 2025-06-01T10:19:20Z, size = 210034, hashes = { sha256 = "a12ed0557c238897d8e518f1d1fdf84bd1516c5e305af2dacd85c2015115feb8" } }] -[[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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, -] +[[packages]] +name = "markdown" +version = "3.9" +sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", upload-time = 2025-09-04T20:25:22Z, size = 364585, hashes = { sha256 = "d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", upload-time = 2025-09-04T20:25:21Z, size = 107441, hashes = { sha256 = "9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280" } }] -[[package]] -name = "meshmind" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "celery", extra = ["redis"] }, - { name = "numpy" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pymgclient" }, - { name = "pytest" }, - { name = "python-dotenv" }, - { name = "rapidfuzz" }, - { name = "scikit-learn" }, - { name = "sentence-transformers" }, - { name = "tiktoken" }, - { name = "typing-extensions" }, -] - -[package.metadata] -requires-dist = [ - { name = "celery", extras = ["redis"], specifier = ">=5.5.2" }, - { name = "numpy", specifier = ">=2.2.5" }, - { name = "openai", specifier = ">=1.78.0" }, - { name = "pydantic", specifier = ">=2.11.4" }, - { name = "pydantic-settings", specifier = ">=2.9.1" }, - { name = "pymgclient", specifier = ">=1.4.0" }, - { name = "pytest", specifier = ">=8.3.5" }, - { name = "python-dotenv", specifier = ">=1.1.0" }, - { name = "rapidfuzz", specifier = ">=3.13.0" }, - { name = "scikit-learn", specifier = ">=1.6.1" }, - { name = "sentence-transformers", specifier = ">=4.1.0" }, - { name = "tiktoken", specifier = ">=0.9.0" }, - { name = "typing-extensions", specifier = ">=4.13.2" }, -] +[[packages]] +name = "markupsafe" +version = "3.0.3" +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", upload-time = 2025-09-27T18:37:40Z, size = 80313, hashes = { sha256 = "722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-09-27T18:36:05Z, size = 11631, hashes = { sha256 = "2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559" } }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:36:07Z, size = 12057, hashes = { sha256 = "e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419" } }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:36:08Z, size = 22050, hashes = { sha256 = "1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695" } }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:36:08Z, size = 20681, hashes = { sha256 = "f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591" } }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:36:10Z, size = 20705, hashes = { sha256 = "c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c" } }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:36:11Z, size = 21524, hashes = { sha256 = "0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f" } }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:36:12Z, size = 20282, hashes = { sha256 = "d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6" } }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:36:13Z, size = 20745, hashes = { sha256 = "177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1" } }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", upload-time = 2025-09-27T18:36:14Z, size = 14571, hashes = { sha256 = "2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa" } }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", upload-time = 2025-09-27T18:36:16Z, size = 15056, hashes = { sha256 = "c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8" } }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", upload-time = 2025-09-27T18:36:17Z, size = 13932, hashes = { sha256 = "e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1" } }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-27T18:36:18Z, size = 11631, hashes = { sha256 = "1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad" } }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:36:19Z, size = 12058, hashes = { sha256 = "4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a" } }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:36:20Z, size = 24287, hashes = { sha256 = "6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50" } }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:36:22Z, size = 22940, hashes = { sha256 = "0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf" } }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:36:23Z, size = 21887, hashes = { sha256 = "bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f" } }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:36:24Z, size = 23692, hashes = { sha256 = "068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a" } }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:36:25Z, size = 21471, hashes = { sha256 = "7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115" } }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:36:27Z, size = 22923, hashes = { sha256 = "f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a" } }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", upload-time = 2025-09-27T18:36:28Z, size = 14572, hashes = { sha256 = "0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19" } }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", upload-time = 2025-09-27T18:36:29Z, size = 15077, hashes = { sha256 = "de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01" } }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", upload-time = 2025-09-27T18:36:29Z, size = 13876, hashes = { sha256 = "3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c" } }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-27T18:36:30Z, size = 11615, hashes = { sha256 = "d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e" } }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:36:31Z, size = 12020, hashes = { sha256 = "1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce" } }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:36:32Z, size = 24332, hashes = { sha256 = "3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d" } }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:36:33Z, size = 22947, hashes = { sha256 = "d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d" } }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:36:35Z, size = 21962, hashes = { sha256 = "94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a" } }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:36:36Z, size = 23760, hashes = { sha256 = "be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b" } }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:36:36Z, size = 21529, hashes = { sha256 = "83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f" } }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:36:37Z, size = 23015, hashes = { sha256 = "77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b" } }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", upload-time = 2025-09-27T18:36:38Z, size = 14540, hashes = { sha256 = "d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d" } }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", upload-time = 2025-09-27T18:36:39Z, size = 15105, hashes = { sha256 = "26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c" } }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", upload-time = 2025-09-27T18:36:40Z, size = 13906, hashes = { sha256 = "35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f" } }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-27T18:36:41Z, size = 11622, hashes = { sha256 = "e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795" } }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:36:43Z, size = 12029, hashes = { sha256 = "116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219" } }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:36:44Z, size = 24374, hashes = { sha256 = "133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6" } }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:36:45Z, size = 22980, hashes = { sha256 = "ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676" } }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:36:46Z, size = 21990, hashes = { sha256 = "509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9" } }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:36:47Z, size = 23784, hashes = { sha256 = "a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1" } }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:36:48Z, size = 21588, hashes = { sha256 = "795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc" } }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:36:49Z, size = 23041, hashes = { sha256 = "8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12" } }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", upload-time = 2025-09-27T18:36:51Z, size = 14543, hashes = { sha256 = "bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed" } }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", upload-time = 2025-09-27T18:36:52Z, size = 15113, hashes = { sha256 = "9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5" } }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", upload-time = 2025-09-27T18:36:53Z, size = 13911, hashes = { sha256 = "7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485" } }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-27T18:36:54Z, size = 11658, hashes = { sha256 = "218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73" } }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:36:55Z, size = 12066, hashes = { sha256 = "3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37" } }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:36:56Z, size = 25639, hashes = { sha256 = "4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19" } }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:36:57Z, size = 23569, hashes = { sha256 = "8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025" } }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:36:58Z, size = 23284, hashes = { sha256 = "b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6" } }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:36:59Z, size = 24801, hashes = { sha256 = "9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f" } }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:37:00Z, size = 22769, hashes = { sha256 = "12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb" } }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:37:01Z, size = 23642, hashes = { sha256 = "8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009" } }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", upload-time = 2025-09-27T18:37:02Z, size = 14612, hashes = { sha256 = "69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354" } }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-27T18:37:03Z, size = 15200, hashes = { sha256 = "1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218" } }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-27T18:37:04Z, size = 13973, hashes = { sha256 = "ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287" } }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-27T18:37:06Z, size = 11619, hashes = { sha256 = "eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe" } }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:37:07Z, size = 12029, hashes = { sha256 = "c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026" } }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:37:09Z, size = 24408, hashes = { sha256 = "f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737" } }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:37:10Z, size = 23005, hashes = { sha256 = "457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97" } }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:37:11Z, size = 22048, hashes = { sha256 = "e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d" } }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:37:12Z, size = 23821, hashes = { sha256 = "ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda" } }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:37:13Z, size = 21606, hashes = { sha256 = "0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf" } }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:37:14Z, size = 23043, hashes = { sha256 = "2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe" } }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", upload-time = 2025-09-27T18:37:15Z, size = 14747, hashes = { sha256 = "729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9" } }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", upload-time = 2025-09-27T18:37:16Z, size = 15341, hashes = { sha256 = "bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581" } }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", upload-time = 2025-09-27T18:37:17Z, size = 14073, hashes = { sha256 = "5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4" } }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-27T18:37:18Z, size = 11661, hashes = { sha256 = "1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab" } }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:37:19Z, size = 12069, hashes = { sha256 = "1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175" } }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:37:20Z, size = 25670, hashes = { sha256 = "1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634" } }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:37:21Z, size = 23598, hashes = { sha256 = "fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50" } }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:37:22Z, size = 23261, hashes = { sha256 = "f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e" } }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:37:23Z, size = 24835, hashes = { sha256 = "e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5" } }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:37:24Z, size = 22733, hashes = { sha256 = "f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523" } }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:37:25Z, size = 23672, hashes = { sha256 = "5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc" } }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", upload-time = 2025-09-27T18:37:26Z, size = 14819, hashes = { sha256 = "915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d" } }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-27T18:37:27Z, size = 15426, hashes = { sha256 = "4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9" } }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-27T18:37:28Z, size = 14146, hashes = { sha256 = "32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa" } }, + { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2025-09-27T18:37:29Z, size = 11623, hashes = { sha256 = "15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26" } }, + { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:37:30Z, size = 12049, hashes = { sha256 = "f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc" } }, + { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-27T18:37:31Z, size = 21923, hashes = { sha256 = "0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c" } }, + { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-27T18:37:32Z, size = 20543, hashes = { sha256 = "e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42" } }, + { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", upload-time = 2025-09-27T18:37:33Z, size = 20585, hashes = { sha256 = "949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b" } }, + { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-09-27T18:37:34Z, size = 21387, hashes = { sha256 = "3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758" } }, + { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", upload-time = 2025-09-27T18:37:35Z, size = 20133, hashes = { sha256 = "591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2" } }, + { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-09-27T18:37:36Z, size = 20588, hashes = { sha256 = "a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d" } }, + { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", upload-time = 2025-09-27T18:37:37Z, size = 14566, hashes = { sha256 = "df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7" } }, + { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", upload-time = 2025-09-27T18:37:38Z, size = 15053, hashes = { sha256 = "7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e" } }, + { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", upload-time = 2025-09-27T18:37:39Z, size = 13928, hashes = { sha256 = "38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8" } }, +] + +[[packages]] +name = "mergedeep" +version = "1.3.4" +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", upload-time = 2021-02-05T18:55:30Z, size = 4661, hashes = { sha256 = "0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", upload-time = 2021-02-05T18:55:29Z, size = 6354, hashes = { sha256 = "70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" } }] + +[[packages]] +name = "mkdocs" +version = "1.6.1" +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", upload-time = 2024-08-30T12:24:06Z, size = 3889159, hashes = { sha256 = "7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", upload-time = 2024-08-30T12:24:05Z, size = 3864451, hashes = { sha256 = "db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e" } }] + +[[packages]] +name = "mkdocs-get-deps" +version = "0.2.0" +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", upload-time = 2023-11-20T17:51:09Z, size = 10239, hashes = { sha256 = "162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", upload-time = 2023-11-20T17:51:08Z, size = 9521, hashes = { sha256 = "2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134" } }] + +[[packages]] +name = "mkdocs-material" +version = "9.6.21" +sdist = { url = "https://files.pythonhosted.org/packages/ff/d5/ab83ca9aa314954b0a9e8849780bdd01866a3cfcb15ffb7e3a61ca06ff0b/mkdocs_material-9.6.21.tar.gz", upload-time = 2025-09-30T19:11:27Z, size = 4043097, hashes = { sha256 = "b01aa6d2731322438056f360f0e623d3faae981f8f2d8c68b1b973f4f2657870" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/cf/4f/98681c2030375fe9b057dbfb9008b68f46c07dddf583f4df09bf8075e37f/mkdocs_material-9.6.21-py3-none-any.whl", upload-time = 2025-09-30T19:11:24Z, size = 9203097, hashes = { sha256 = "aa6a5ab6fb4f6d381588ac51da8782a4d3757cb3d1b174f81a2ec126e1f22c92" } }] + +[[packages]] +name = "mkdocs-material-extensions" +version = "1.3.1" +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", upload-time = 2023-11-22T19:09:45Z, size = 11847, hashes = { sha256 = "10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", upload-time = 2023-11-22T19:09:43Z, size = 8728, hashes = { sha256 = "adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", upload-time = 2023-03-07T16:47:11Z, size = 508106, hashes = { sha256 = "7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", upload-time = 2023-03-07T16:47:09Z, size = 536198, hashes = { sha256 = "a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c" } }] + +[[packages]] +name = "neo4j" +version = "6.0.2" +sdist = { url = "https://files.pythonhosted.org/packages/eb/34/485ab7c0252bd5d9c9ff0672f61153a8007490af2069f664d8766709c7ba/neo4j-6.0.2.tar.gz", upload-time = 2025-10-02T11:31:06Z, size = 240139, hashes = { sha256 = "c98734c855b457e7a976424dc04446d652838d00907d250d6e9a595e88892378" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/75/4e/11813da186859070b0512e8071dac4796624ac4dc28e25e7c530df730d23/neo4j-6.0.2-py3-none-any.whl", upload-time = 2025-10-02T11:31:04Z, size = 325761, hashes = { sha256 = "dc3fc1c99f6da2293d9deefead1e31dd7429bbb513eccf96e4134b7dbf770243" } }] -[[package]] +[[packages]] name = "networkx" -version = "3.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, -] +version = "3.5" +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", upload-time = 2025-05-29T11:35:07Z, size = 2471065, hashes = { sha256 = "d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", upload-time = 2025-05-29T11:35:04Z, size = 2034406, hashes = { sha256 = "0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec" } }] -[[package]] -name = "numpy" -version = "2.2.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/b2/ce4b867d8cd9c0ee84938ae1e6a6f7926ebf928c9090d036fc3c6a04f946/numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", size = 20273920 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/a0/0aa7f0f4509a2e07bd7a509042967c2fab635690d4f48c6c7b3afd4f448c/numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", size = 20935102 }, - { url = "https://files.pythonhosted.org/packages/7e/e4/a6a9f4537542912ec513185396fce52cdd45bdcf3e9d921ab02a93ca5aa9/numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", size = 14191709 }, - { url = "https://files.pythonhosted.org/packages/be/65/72f3186b6050bbfe9c43cb81f9df59ae63603491d36179cf7a7c8d216758/numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", size = 5149173 }, - { url = "https://files.pythonhosted.org/packages/e5/e9/83e7a9432378dde5802651307ae5e9ea07bb72b416728202218cd4da2801/numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", size = 6684502 }, - { url = "https://files.pythonhosted.org/packages/ea/27/b80da6c762394c8ee516b74c1f686fcd16c8f23b14de57ba0cad7349d1d2/numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", size = 14084417 }, - { url = "https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", size = 16133807 }, - { url = "https://files.pythonhosted.org/packages/bf/9b/4cc171a0acbe4666f7775cfd21d4eb6bb1d36d3a0431f48a73e9212d2278/numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", size = 15575611 }, - { url = "https://files.pythonhosted.org/packages/a3/45/40f4135341850df48f8edcf949cf47b523c404b712774f8855a64c96ef29/numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", size = 17895747 }, - { url = "https://files.pythonhosted.org/packages/f8/4c/b32a17a46f0ffbde8cc82df6d3daeaf4f552e346df143e1b188a701a8f09/numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", size = 6309594 }, - { url = "https://files.pythonhosted.org/packages/13/ae/72e6276feb9ef06787365b05915bfdb057d01fceb4a43cb80978e518d79b/numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", size = 12638356 }, - { url = "https://files.pythonhosted.org/packages/79/56/be8b85a9f2adb688e7ded6324e20149a03541d2b3297c3ffc1a73f46dedb/numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", size = 20963778 }, - { url = "https://files.pythonhosted.org/packages/ff/77/19c5e62d55bff507a18c3cdff82e94fe174957bad25860a991cac719d3ab/numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", size = 14207279 }, - { url = "https://files.pythonhosted.org/packages/75/22/aa11f22dc11ff4ffe4e849d9b63bbe8d4ac6d5fae85ddaa67dfe43be3e76/numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", size = 5199247 }, - { url = "https://files.pythonhosted.org/packages/4f/6c/12d5e760fc62c08eded0394f62039f5a9857f758312bf01632a81d841459/numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", size = 6711087 }, - { url = "https://files.pythonhosted.org/packages/ef/94/ece8280cf4218b2bee5cec9567629e61e51b4be501e5c6840ceb593db945/numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", size = 14059964 }, - { url = "https://files.pythonhosted.org/packages/39/41/c5377dac0514aaeec69115830a39d905b1882819c8e65d97fc60e177e19e/numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", size = 16121214 }, - { url = "https://files.pythonhosted.org/packages/db/54/3b9f89a943257bc8e187145c6bc0eb8e3d615655f7b14e9b490b053e8149/numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", size = 15575788 }, - { url = "https://files.pythonhosted.org/packages/b1/c4/2e407e85df35b29f79945751b8f8e671057a13a376497d7fb2151ba0d290/numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", size = 17893672 }, - { url = "https://files.pythonhosted.org/packages/29/7e/d0b44e129d038dba453f00d0e29ebd6eaf2f06055d72b95b9947998aca14/numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", size = 6377102 }, - { url = "https://files.pythonhosted.org/packages/63/be/b85e4aa4bf42c6502851b971f1c326d583fcc68227385f92089cf50a7b45/numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", size = 12750096 }, -] +[[packages]] +name = "nodeenv" +version = "1.9.1" +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", upload-time = 2024-06-04T18:44:11Z, size = 47437, hashes = { sha256 = "6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", upload-time = 2024-06-04T18:44:08Z, size = 22314, hashes = { sha256 = "ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" } }] -[[package]] +[[packages]] +name = "numpy" +version = "2.3.3" +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", upload-time = 2025-09-09T16:54:12Z, size = 20576648, hashes = { sha256 = "ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-09T15:56:02Z, size = 21259253, hashes = { sha256 = "0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d" } }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:56:05Z, size = 14450980, hashes = { sha256 = "e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569" } }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:56:07Z, size = 5379709, hashes = { sha256 = "cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f" } }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:56:09Z, size = 6913923, hashes = { sha256 = "f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125" } }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:56:11Z, size = 14589591, hashes = { sha256 = "afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48" } }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:56:14Z, size = 16938714, hashes = { sha256 = "bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6" } }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:56:17Z, size = 16370592, hashes = { sha256 = "7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa" } }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:56:20Z, size = 18884474, hashes = { sha256 = "433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30" } }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", upload-time = 2025-09-09T15:56:23Z, size = 6599794, hashes = { sha256 = "eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57" } }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", upload-time = 2025-09-09T15:56:25Z, size = 13088104, hashes = { sha256 = "ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa" } }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", upload-time = 2025-09-09T15:56:27Z, size = 10460772, hashes = { sha256 = "74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7" } }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T15:56:29Z, size = 20957014, hashes = { sha256 = "cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf" } }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:56:32Z, size = 14185220, hashes = { sha256 = "cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25" } }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:56:34Z, size = 5113918, hashes = { sha256 = "396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe" } }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:56:36Z, size = 6647922, hashes = { sha256 = "067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b" } }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:56:40Z, size = 14281991, hashes = { sha256 = "1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8" } }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:56:43Z, size = 16641643, hashes = { sha256 = "d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20" } }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:56:46Z, size = 16056787, hashes = { sha256 = "cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea" } }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:56:49Z, size = 18579598, hashes = { sha256 = "93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7" } }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", upload-time = 2025-09-09T15:56:52Z, size = 6320800, hashes = { sha256 = "5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf" } }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", upload-time = 2025-09-09T15:56:54Z, size = 12786615, hashes = { sha256 = "497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb" } }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", upload-time = 2025-09-09T15:56:56Z, size = 10195936, hashes = { sha256 = "ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5" } }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T15:56:59Z, size = 20949588, hashes = { sha256 = "f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf" } }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:57:01Z, size = 14177802, hashes = { sha256 = "d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7" } }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:57:03Z, size = 5106537, hashes = { sha256 = "9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6" } }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:57:07Z, size = 6640743, hashes = { sha256 = "d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7" } }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:57:11Z, size = 14278881, hashes = { sha256 = "952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c" } }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:57:14Z, size = 16636301, hashes = { sha256 = "5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93" } }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:57:16Z, size = 16053645, hashes = { sha256 = "b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae" } }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:57:18Z, size = 18578179, hashes = { sha256 = "8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86" } }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", upload-time = 2025-09-09T15:57:21Z, size = 6312250, hashes = { sha256 = "9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8" } }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", upload-time = 2025-09-09T15:57:23Z, size = 12783269, hashes = { sha256 = "f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf" } }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", upload-time = 2025-09-09T15:57:25Z, size = 10195314, hashes = { sha256 = "3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5" } }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T15:57:27Z, size = 21048025, hashes = { sha256 = "eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc" } }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:57:30Z, size = 14301053, hashes = { sha256 = "823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc" } }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:57:32Z, size = 5229444, hashes = { sha256 = "40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b" } }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:57:34Z, size = 6738039, hashes = { sha256 = "6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19" } }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:57:36Z, size = 14352314, hashes = { sha256 = "94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30" } }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:57:38Z, size = 16701722, hashes = { sha256 = "da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e" } }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:57:41Z, size = 16132755, hashes = { sha256 = "2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3" } }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:57:43Z, size = 18651560, hashes = { sha256 = "ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea" } }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", upload-time = 2025-09-09T15:57:45Z, size = 6442776, hashes = { sha256 = "a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd" } }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-09T15:57:47Z, size = 12927281, hashes = { sha256 = "4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d" } }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-09T15:57:49Z, size = 10265275, hashes = { sha256 = "75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1" } }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T15:57:52Z, size = 20951527, hashes = { sha256 = "cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593" } }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:57:54Z, size = 14186159, hashes = { sha256 = "414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652" } }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:57:56Z, size = 5114624, hashes = { sha256 = "50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7" } }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:57:58Z, size = 6642627, hashes = { sha256 = "b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a" } }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:58:00Z, size = 14296926, hashes = { sha256 = "9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe" } }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:58:02Z, size = 16638958, hashes = { sha256 = "ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421" } }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:58:05Z, size = 16071920, hashes = { sha256 = "e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021" } }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:58:07Z, size = 18577076, hashes = { sha256 = "d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf" } }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", upload-time = 2025-09-09T15:58:10Z, size = 6366952, hashes = { sha256 = "cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0" } }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", upload-time = 2025-09-09T15:58:12Z, size = 12919322, hashes = { sha256 = "691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8" } }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", upload-time = 2025-09-09T15:58:14Z, size = 10478630, hashes = { sha256 = "9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe" } }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T15:58:16Z, size = 21047987, hashes = { sha256 = "9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00" } }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:58:20Z, size = 14301076, hashes = { sha256 = "901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a" } }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:58:22Z, size = 5229491, hashes = { sha256 = "7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d" } }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:58:24Z, size = 6737913, hashes = { sha256 = "533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a" } }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:58:26Z, size = 14352811, hashes = { sha256 = "0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54" } }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:58:28Z, size = 16702689, hashes = { sha256 = "367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e" } }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-09T15:58:31Z, size = 16133855, hashes = { sha256 = "8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097" } }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-09T15:58:33Z, size = 18652520, hashes = { sha256 = "179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970" } }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", upload-time = 2025-09-09T15:58:36Z, size = 6515371, hashes = { sha256 = "1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5" } }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-09T15:58:37Z, size = 13112576, hashes = { sha256 = "b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f" } }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-09T15:58:40Z, size = 10545953, hashes = { sha256 = "78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b" } }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-09-09T15:58:42Z, size = 21131019, hashes = { sha256 = "1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e" } }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", upload-time = 2025-09-09T15:58:45Z, size = 14376288, hashes = { sha256 = "dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150" } }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", upload-time = 2025-09-09T15:58:48Z, size = 5305425, hashes = { sha256 = "99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3" } }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", upload-time = 2025-09-09T15:58:50Z, size = 6819053, hashes = { sha256 = "d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0" } }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T15:58:52Z, size = 14420354, hashes = { sha256 = "8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e" } }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-09T15:58:55Z, size = 16760413, hashes = { sha256 = "e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db" } }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", upload-time = 2025-09-09T15:58:57Z, size = 12971844, hashes = { sha256 = "2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc" } }, +] + +[[packages]] name = "nvidia-cublas-cu12" -version = "12.6.4.1" -source = { registry = "https://pypi.org/simple" } +version = "12.8.4.1" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/29/99/db44d685f0e257ff0e213ade1964fc459b4a690a73293220e98feb3307cf/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", upload-time = 2025-03-07T01:43:53Z, size = 590537124, hashes = { sha256 = "b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0" } }, + { url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", upload-time = 2025-03-07T01:44:31Z, size = 594346921, hashes = { sha256 = "8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142" } }, + { url = "https://files.pythonhosted.org/packages/70/61/7d7b3c70186fb651d0fbd35b01dbfc8e755f69fd58f817f3d0f642df20c3/nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:53:30Z, size = 567544208, hashes = { sha256 = "47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af" } }, ] -[[package]] +[[packages]] name = "nvidia-cuda-cupti-cu12" -version = "12.6.80" -source = { registry = "https://pypi.org/simple" } +version = "12.8.90" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/d5/1f/b3bd73445e5cb342727fd24fe1f7b748f690b460acadc27ea22f904502c8/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:40:10Z, size = 9533318, hashes = { sha256 = "4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed" } }, + { url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:40:21Z, size = 10248621, hashes = { sha256 = "ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182" } }, + { url = "https://files.pythonhosted.org/packages/41/bc/83f5426095d93694ae39fe1311431b5d5a9bb82e48bf0dd8e19be2765942/nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:51:11Z, size = 7015759, hashes = { sha256 = "bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e" } }, ] -[[package]] +[[packages]] name = "nvidia-cuda-nvrtc-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } +version = "12.8.93" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", upload-time = 2025-03-07T01:42:13Z, size = 88040029, hashes = { sha256 = "a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994" } }, + { url = "https://files.pythonhosted.org/packages/eb/d1/e50d0acaab360482034b84b6e27ee83c6738f7d32182b987f9c7a4e32962/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:41:59Z, size = 43106076, hashes = { sha256 = "fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8" } }, + { url = "https://files.pythonhosted.org/packages/45/51/52a3d84baa2136cc8df15500ad731d74d3a1114d4c123e043cb608d4a32b/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:52:13Z, size = 73586838, hashes = { sha256 = "7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909" } }, ] -[[package]] +[[packages]] name = "nvidia-cuda-runtime-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } +version = "12.8.90" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/7c/75/f865a3b236e4647605ea34cc450900854ba123834a5f1598e160b9530c3a/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:39:43Z, size = 965265, hashes = { sha256 = "52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d" } }, + { url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:40:01Z, size = 954765, hashes = { sha256 = "adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90" } }, + { url = "https://files.pythonhosted.org/packages/30/a5/a515b7600ad361ea14bfa13fb4d6687abf500adc270f19e89849c0590492/nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:51:01Z, size = 944318, hashes = { sha256 = "c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8" } }, ] -[[package]] +[[packages]] name = "nvidia-cudnn-cu12" -version = "9.5.1.17" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas-cu12" }, -] +version = "9.10.2.21" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/fa/41/e79269ce215c857c935fd86bcfe91a451a584dfc27f1e068f568b9ad1ab7/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", upload-time = 2025-06-06T21:52:51Z, size = 705026878, hashes = { sha256 = "c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8" } }, + { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", upload-time = 2025-06-06T21:54:08Z, size = 706758467, hashes = { sha256 = "949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8" } }, + { url = "https://files.pythonhosted.org/packages/3d/90/0bd6e586701b3a890fd38aa71c387dab4883d619d6e5ad912ccbd05bfd67/nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", upload-time = 2025-06-06T21:55:18Z, size = 692992268, hashes = { sha256 = "c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e" } }, ] -[[package]] +[[packages]] name = "nvidia-cufft-cu12" -version = "11.3.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] +version = "11.3.3.83" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/60/bc/7771846d3a0272026c416fbb7e5f4c1f146d6d80704534d0b187dd6f4800/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:44:56Z, size = 193109211, hashes = { sha256 = "848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a" } }, + { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:45:27Z, size = 193118695, hashes = { sha256 = "4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74" } }, + { url = "https://files.pythonhosted.org/packages/7d/ec/ce1629f1e478bb5ccd208986b5f9e0316a78538dd6ab1d0484f012f8e2a1/nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:53:57Z, size = 192216559, hashes = { sha256 = "7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7" } }, ] -[[package]] +[[packages]] name = "nvidia-cufile-cu12" -version = "1.11.1.6" -source = { registry = "https://pypi.org/simple" } +version = "1.13.1.3" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:45:50Z, size = 1197834, hashes = { sha256 = "1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc" } }, + { url = "https://files.pythonhosted.org/packages/1e/f5/5607710447a6fe9fd9b3283956fceeee8a06cda1d2f56ce31371f595db2a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", upload-time = 2025-03-07T01:45:41Z, size = 1120705, hashes = { sha256 = "4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a" } }, ] -[[package]] +[[packages]] name = "nvidia-curand-cu12" -version = "10.3.7.77" -source = { registry = "https://pypi.org/simple" } +version = "10.3.9.90" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/45/5e/92aa15eca622a388b80fbf8375d4760738df6285b1e92c43d37390a33a9a/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", upload-time = 2025-03-07T01:46:10Z, size = 63625754, hashes = { sha256 = "dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd" } }, + { url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", upload-time = 2025-03-07T01:46:23Z, size = 63619976, hashes = { sha256 = "b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9" } }, + { url = "https://files.pythonhosted.org/packages/b9/75/70c05b2f3ed5be3bb30b7102b6eb78e100da4bbf6944fd6725c012831cab/nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:54:20Z, size = 62765309, hashes = { sha256 = "f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec" } }, ] -[[package]] +[[packages]] 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" }, -] +version = "11.7.3.90" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/c8/32/f7cd6ce8a7690544d084ea21c26e910a97e077c9b7f07bf5de623ee19981/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", upload-time = 2025-03-07T01:46:54Z, size = 267229841, hashes = { sha256 = "db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0" } }, + { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", upload-time = 2025-03-07T01:47:16Z, size = 267506905, hashes = { sha256 = "4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450" } }, + { url = "https://files.pythonhosted.org/packages/13/c0/76ca8551b8a84146ffa189fec81c26d04adba4bc0dbe09cd6e6fd9b7de04/nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:54:39Z, size = 256720438, hashes = { sha256 = "4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34" } }, ] -[[package]] +[[packages]] name = "nvidia-cusparse-cu12" -version = "12.5.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, -] +version = "12.5.8.93" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/bc/f7/cd777c4109681367721b00a106f491e0d0d15cfa1fd59672ce580ce42a97/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:47:40Z, size = 288117129, hashes = { sha256 = "9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc" } }, + { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:48:13Z, size = 288216466, hashes = { sha256 = "1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b" } }, + { url = "https://files.pythonhosted.org/packages/62/07/f3b2ad63f8e3d257a599f422ae34eb565e70c41031aecefa3d18b62cabd1/nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:55:07Z, size = 284937404, hashes = { sha256 = "9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd" } }, ] -[[package]] +[[packages]] name = "nvidia-cusparselt-cu12" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } +version = "0.7.1" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/73/b9/598f6ff36faaece4b3c50d26f50e38661499ff34346f00e057760b35cc9d/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", upload-time = 2025-02-26T00:16:54Z, size = 283835557, hashes = { sha256 = "8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5" } }, + { url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", upload-time = 2025-02-26T00:15:44Z, size = 287193691, hashes = { sha256 = "f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623" } }, + { url = "https://files.pythonhosted.org/packages/2f/d8/a6b0d0d0c2435e9310f3e2bb0d9c9dd4c33daef86aa5f30b3681defd37ea/nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", upload-time = 2025-02-26T00:14:47Z, size = 271020911, hashes = { sha256 = "f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075" } }, ] -[[package]] +[[packages]] name = "nvidia-nccl-cu12" -version = "2.26.2" -source = { registry = "https://pypi.org/simple" } +version = "2.27.3" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/4b/7b/8354b784cf73b0ba51e566b4baba3ddd44fe8288a3d39ef1e06cd5417226/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-06-03T21:57:30Z, size = 322397768, hashes = { sha256 = "9ddf1a245abc36c550870f26d537a9b6087fb2e2e3d6e0ef03374c6fd19d984f" } }, + { url = "https://files.pythonhosted.org/packages/5c/5b/4e4fff7bad39adf89f735f2bc87248c81db71205b62bcc0d5ca5b606b3c3/nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-06-03T21:58:04Z, size = 322364134, hashes = { sha256 = "adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039" } }, ] -[[package]] +[[packages]] name = "nvidia-nvjitlink-cu12" -version = "12.6.85" -source = { registry = "https://pypi.org/simple" } +version = "12.8.93" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, + { url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", upload-time = 2025-03-07T01:49:55Z, size = 39254836, hashes = { sha256 = "81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88" } }, + { url = "https://files.pythonhosted.org/packages/2a/a2/8cee5da30d13430e87bf99bb33455d2724d0a4a9cb5d7926d80ccb96d008/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:49:43Z, size = 38386204, hashes = { sha256 = "adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7" } }, + { url = "https://files.pythonhosted.org/packages/ed/d7/34f02dad2e30c31b10a51f6b04e025e5dd60e5f936af9045a9b858a05383/nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:56:24Z, size = 268553710, hashes = { sha256 = "bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f" } }, ] -[[package]] +[[packages]] name = "nvidia-nvtx-cu12" -version = "12.6.77" -source = { registry = "https://pypi.org/simple" } +version = "12.8.90" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" 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 }, - { 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 }, + { url = "https://files.pythonhosted.org/packages/10/c0/1b303feea90d296f6176f32a2a70b5ef230f9bdeb3a72bddb0dc922dc137/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-03-07T01:42:23Z, size = 91161, hashes = { sha256 = "d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615" } }, + { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-03-07T01:42:44Z, size = 89954, hashes = { sha256 = "5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f" } }, + { url = "https://files.pythonhosted.org/packages/9f/99/4c9c0c329bf9fc125008c3b54c7c94c0023518d06fc025ae36431375e1fe/nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", upload-time = 2025-03-07T01:52:24Z, size = 56492, hashes = { sha256 = "619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e" } }, ] -[[package]] +[[packages]] name = "openai" -version = "1.78.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/d1/7c/7c48bac9be52680e41e99ae7649d5da3a0184cd94081e028897f9005aa03/openai-1.78.0.tar.gz", hash = "sha256:254aef4980688468e96cbddb1f348ed01d274d02c64c6c69b0334bf001fb62b3", size = 442652 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/41/d64a6c56d0ec886b834caff7a07fc4d43e1987895594b144757e7a6b90d7/openai-1.78.0-py3-none-any.whl", hash = "sha256:1ade6a48cd323ad8a7715e7e1669bb97a17e1a5b8a916644261aaef4bf284778", size = 680407 }, -] +version = "2.3.0" +sdist = { url = "https://files.pythonhosted.org/packages/de/90/8f26554d24d63ed4f94d33c24271559863223a67e624f4d2e65ba8e48dca/openai-2.3.0.tar.gz", upload-time = 2025-10-10T01:12:50Z, size = 589616, hashes = { sha256 = "8d213ee5aaf91737faea2d7fc1cd608657a5367a18966372a3756ceaabfbd812" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/9c/5b/4be258ff072ed8ee15f6bfd8d5a1a4618aa4704b127c0c5959212ad177d6/openai-2.3.0-py3-none-any.whl", upload-time = 2025-10-10T01:12:48Z, size = 999768, hashes = { sha256 = "a7aa83be6f7b0ab2e4d4d7bcaf36e3d790874c0167380c5d0afd0ed99a86bd7b" } }] -[[package]] +[[packages]] name = "packaging" version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, -] - -[[package]] +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", upload-time = 2025-04-19T11:48:59Z, size = 165727, hashes = { sha256 = "d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", upload-time = 2025-04-19T11:48:57Z, size = 66469, hashes = { sha256 = "29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484" } }] + +[[packages]] +name = "paginate" +version = "0.5.7" +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", upload-time = 2024-08-25T14:17:24Z, size = 19252, hashes = { sha256 = "22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", upload-time = 2024-08-25T14:17:22Z, size = 13746, hashes = { sha256 = "b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591" } }] + +[[packages]] +name = "pathspec" +version = "0.12.1" +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", upload-time = 2023-12-10T22:30:45Z, size = 51043, hashes = { sha256 = "a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", upload-time = 2023-12-10T22:30:43Z, size = 31191, hashes = { sha256 = "a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08" } }] + +[[packages]] name = "pillow" -version = "11.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/9c/447528ee3776e7ab8897fe33697a7ff3f0475bb490c5ac1456a03dc57956/pillow-11.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28", size = 3190098 }, - { url = "https://files.pythonhosted.org/packages/b5/09/29d5cd052f7566a63e5b506fac9c60526e9ecc553825551333e1e18a4858/pillow-11.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830", size = 3030166 }, - { url = "https://files.pythonhosted.org/packages/71/5d/446ee132ad35e7600652133f9c2840b4799bbd8e4adba881284860da0a36/pillow-11.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0", size = 4408674 }, - { url = "https://files.pythonhosted.org/packages/69/5f/cbe509c0ddf91cc3a03bbacf40e5c2339c4912d16458fcb797bb47bcb269/pillow-11.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1", size = 4496005 }, - { url = "https://files.pythonhosted.org/packages/f9/b3/dd4338d8fb8a5f312021f2977fb8198a1184893f9b00b02b75d565c33b51/pillow-11.2.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f", size = 4518707 }, - { url = "https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155", size = 4610008 }, - { url = "https://files.pythonhosted.org/packages/72/d1/924ce51bea494cb6e7959522d69d7b1c7e74f6821d84c63c3dc430cbbf3b/pillow-11.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14", size = 4585420 }, - { url = "https://files.pythonhosted.org/packages/43/ab/8f81312d255d713b99ca37479a4cb4b0f48195e530cdc1611990eb8fd04b/pillow-11.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b", size = 4667655 }, - { url = "https://files.pythonhosted.org/packages/94/86/8f2e9d2dc3d308dfd137a07fe1cc478df0a23d42a6c4093b087e738e4827/pillow-11.2.1-cp313-cp313-win32.whl", hash = "sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2", size = 2332329 }, - { url = "https://files.pythonhosted.org/packages/6d/ec/1179083b8d6067a613e4d595359b5fdea65d0a3b7ad623fee906e1b3c4d2/pillow-11.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691", size = 2676388 }, - { url = "https://files.pythonhosted.org/packages/23/f1/2fc1e1e294de897df39fa8622d829b8828ddad938b0eaea256d65b84dd72/pillow-11.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c", size = 2414950 }, - { url = "https://files.pythonhosted.org/packages/c4/3e/c328c48b3f0ead7bab765a84b4977acb29f101d10e4ef57a5e3400447c03/pillow-11.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22", size = 3192759 }, - { url = "https://files.pythonhosted.org/packages/18/0e/1c68532d833fc8b9f404d3a642991441d9058eccd5606eab31617f29b6d4/pillow-11.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7", size = 3033284 }, - { url = "https://files.pythonhosted.org/packages/b7/cb/6faf3fb1e7705fd2db74e070f3bf6f88693601b0ed8e81049a8266de4754/pillow-11.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16", size = 4445826 }, - { url = "https://files.pythonhosted.org/packages/07/94/8be03d50b70ca47fb434a358919d6a8d6580f282bbb7af7e4aa40103461d/pillow-11.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b", size = 4527329 }, - { url = "https://files.pythonhosted.org/packages/fd/a4/bfe78777076dc405e3bd2080bc32da5ab3945b5a25dc5d8acaa9de64a162/pillow-11.2.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406", size = 4549049 }, - { url = "https://files.pythonhosted.org/packages/65/4d/eaf9068dc687c24979e977ce5677e253624bd8b616b286f543f0c1b91662/pillow-11.2.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91", size = 4635408 }, - { url = "https://files.pythonhosted.org/packages/1d/26/0fd443365d9c63bc79feb219f97d935cd4b93af28353cba78d8e77b61719/pillow-11.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751", size = 4614863 }, - { url = "https://files.pythonhosted.org/packages/49/65/dca4d2506be482c2c6641cacdba5c602bc76d8ceb618fd37de855653a419/pillow-11.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9", size = 4692938 }, - { url = "https://files.pythonhosted.org/packages/b3/92/1ca0c3f09233bd7decf8f7105a1c4e3162fb9142128c74adad0fb361b7eb/pillow-11.2.1-cp313-cp313t-win32.whl", hash = "sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd", size = 2335774 }, - { url = "https://files.pythonhosted.org/packages/a5/ac/77525347cb43b83ae905ffe257bbe2cc6fd23acb9796639a1f56aa59d191/pillow-11.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e", size = 2681895 }, - { url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234 }, -] - -[[package]] +version = "11.3.0" +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", upload-time = 2025-07-01T09:16:30Z, size = 47113069, hashes = { sha256 = "3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", upload-time = 2025-07-01T09:13:39Z, size = 5316554, hashes = { sha256 = "1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860" } }, + { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:13:41Z, size = 4686548, hashes = { sha256 = "65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad" } }, + { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:09:47Z, size = 5859742, hashes = { sha256 = "7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0" } }, + { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:09:51Z, size = 7633087, hashes = { sha256 = "cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b" } }, + { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:13:43Z, size = 5963350, hashes = { sha256 = "f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50" } }, + { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:13:46Z, size = 6631840, hashes = { sha256 = "4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae" } }, + { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:13:47Z, size = 6074005, hashes = { sha256 = "71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9" } }, + { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:13:52Z, size = 6708372, hashes = { sha256 = "040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e" } }, + { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", upload-time = 2025-07-01T09:13:53Z, size = 6277090, hashes = { sha256 = "89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6" } }, + { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", upload-time = 2025-07-01T09:13:55Z, size = 6985988, hashes = { sha256 = "19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f" } }, + { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", upload-time = 2025-07-01T09:13:57Z, size = 2422899, hashes = { sha256 = "819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f" } }, + { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", upload-time = 2025-07-01T09:13:59Z, size = 5316531, hashes = { sha256 = "1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722" } }, + { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:14:01Z, size = 4686560, hashes = { sha256 = "9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288" } }, + { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:09:55Z, size = 5870978, hashes = { sha256 = "7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d" } }, + { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:00Z, size = 7641168, hashes = { sha256 = "91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494" } }, + { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:14:04Z, size = 5973053, hashes = { sha256 = "643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58" } }, + { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:14:06Z, size = 6640273, hashes = { sha256 = "106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f" } }, + { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:14:07Z, size = 6082043, hashes = { sha256 = "cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e" } }, + { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:14:10Z, size = 6715516, hashes = { sha256 = "932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94" } }, + { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", upload-time = 2025-07-01T09:14:11Z, size = 6274768, hashes = { sha256 = "b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0" } }, + { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", upload-time = 2025-07-01T09:14:13Z, size = 6986055, hashes = { sha256 = "1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac" } }, + { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", upload-time = 2025-07-01T09:14:15Z, size = 2423079, hashes = { sha256 = "30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd" } }, + { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-07-01T09:14:17Z, size = 5278800, hashes = { sha256 = "fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4" } }, + { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:14:19Z, size = 4686296, hashes = { sha256 = "921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69" } }, + { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:10:04Z, size = 5871726, hashes = { sha256 = "eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d" } }, + { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:10Z, size = 7644652, hashes = { sha256 = "67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6" } }, + { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:14:21Z, size = 5977787, hashes = { sha256 = "97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7" } }, + { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:14:23Z, size = 6645236, hashes = { sha256 = "676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024" } }, + { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:14:25Z, size = 6086950, hashes = { sha256 = "3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809" } }, + { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:14:27Z, size = 6723358, hashes = { sha256 = "6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d" } }, + { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", upload-time = 2025-07-01T09:14:30Z, size = 6275079, hashes = { sha256 = "7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149" } }, + { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", upload-time = 2025-07-01T09:14:31Z, size = 6986324, hashes = { sha256 = "a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d" } }, + { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", upload-time = 2025-07-01T09:14:33Z, size = 2423067, hashes = { sha256 = "2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542" } }, + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", upload-time = 2025-07-01T09:14:35Z, size = 2128328, hashes = { sha256 = "1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd" } }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", upload-time = 2025-07-01T09:14:37Z, size = 2170652, hashes = { sha256 = "30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8" } }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", upload-time = 2025-07-01T09:14:39Z, size = 2227443, hashes = { sha256 = "7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f" } }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-07-01T09:14:41Z, size = 5278474, hashes = { sha256 = "ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c" } }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:14:44Z, size = 4686038, hashes = { sha256 = "7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd" } }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:10:15Z, size = 5864407, hashes = { sha256 = "2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e" } }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:21Z, size = 7639094, hashes = { sha256 = "f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1" } }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:14:45Z, size = 5973503, hashes = { sha256 = "c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805" } }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:14:47Z, size = 6642574, hashes = { sha256 = "13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8" } }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:14:49Z, size = 6084060, hashes = { sha256 = "023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2" } }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:14:51Z, size = 6721407, hashes = { sha256 = "45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b" } }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", upload-time = 2025-07-01T09:14:54Z, size = 6273841, hashes = { sha256 = "a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3" } }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", upload-time = 2025-07-01T09:14:56Z, size = 6978450, hashes = { sha256 = "0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51" } }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", upload-time = 2025-07-01T09:14:58Z, size = 2423055, hashes = { sha256 = "1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580" } }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-07-01T09:14:59Z, size = 5281110, hashes = { sha256 = "4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e" } }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:15:01Z, size = 4689547, hashes = { sha256 = "5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d" } }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:10:27Z, size = 5901554, hashes = { sha256 = "1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced" } }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:33Z, size = 7669132, hashes = { sha256 = "f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c" } }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:15:03Z, size = 6005001, hashes = { sha256 = "1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8" } }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:15:05Z, size = 6668814, hashes = { sha256 = "05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59" } }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:15:07Z, size = 6113124, hashes = { sha256 = "a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe" } }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:15:09Z, size = 6747186, hashes = { sha256 = "83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c" } }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", upload-time = 2025-07-01T09:15:11Z, size = 6277546, hashes = { sha256 = "2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788" } }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", upload-time = 2025-07-01T09:15:13Z, size = 6985102, hashes = { sha256 = "857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31" } }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", upload-time = 2025-07-01T09:15:15Z, size = 2424803, hashes = { sha256 = "8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e" } }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-07-01T09:15:17Z, size = 5278520, hashes = { sha256 = "d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12" } }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:15:19Z, size = 4686116, hashes = { sha256 = "0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a" } }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:10:38Z, size = 5864597, hashes = { sha256 = "0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632" } }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:44Z, size = 7638246, hashes = { sha256 = "2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673" } }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:15:21Z, size = 5973336, hashes = { sha256 = "41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027" } }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:15:23Z, size = 6642699, hashes = { sha256 = "93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77" } }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:15:25Z, size = 6083789, hashes = { sha256 = "7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874" } }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:15:27Z, size = 6720386, hashes = { sha256 = "98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a" } }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", upload-time = 2025-07-01T09:15:29Z, size = 6370911, hashes = { sha256 = "02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214" } }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", upload-time = 2025-07-01T09:15:31Z, size = 7117383, hashes = { sha256 = "a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635" } }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", upload-time = 2025-07-01T09:15:33Z, size = 2511385, hashes = { sha256 = "155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6" } }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-07-01T09:15:35Z, size = 5281129, hashes = { sha256 = "59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae" } }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:15:37Z, size = 4689580, hashes = { sha256 = "f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653" } }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:10:50Z, size = 5902860, hashes = { sha256 = "ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6" } }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:10:56Z, size = 7670694, hashes = { sha256 = "c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36" } }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:15:39Z, size = 6005888, hashes = { sha256 = "4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b" } }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:15:41Z, size = 6670330, hashes = { sha256 = "41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477" } }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:15:43Z, size = 6114089, hashes = { sha256 = "068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50" } }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:15:44Z, size = 6748206, hashes = { sha256 = "a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b" } }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", upload-time = 2025-07-01T09:15:46Z, size = 6377370, hashes = { sha256 = "118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12" } }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", upload-time = 2025-07-01T09:15:48Z, size = 7121500, hashes = { sha256 = "8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db" } }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", upload-time = 2025-07-01T09:15:50Z, size = 2512835, hashes = { sha256 = "79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa" } }, + { url = "https://files.pythonhosted.org/packages/9e/8e/9c089f01677d1264ab8648352dcb7773f37da6ad002542760c80107da816/pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", upload-time = 2025-07-01T09:15:52Z, size = 5316478, hashes = { sha256 = "48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f" } }, + { url = "https://files.pythonhosted.org/packages/b5/a9/5749930caf674695867eb56a581e78eb5f524b7583ff10b01b6e5048acb3/pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:15:54Z, size = 4686522, hashes = { sha256 = "7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081" } }, + { url = "https://files.pythonhosted.org/packages/43/46/0b85b763eb292b691030795f9f6bb6fcaf8948c39413c81696a01c3577f7/pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:11:01Z, size = 5853376, hashes = { sha256 = "23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4" } }, + { url = "https://files.pythonhosted.org/packages/5e/c6/1a230ec0067243cbd60bc2dad5dc3ab46a8a41e21c15f5c9b52b26873069/pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:11:06Z, size = 7626020, hashes = { sha256 = "6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc" } }, + { url = "https://files.pythonhosted.org/packages/63/dd/f296c27ffba447bfad76c6a0c44c1ea97a90cb9472b9304c94a732e8dbfb/pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:15:56Z, size = 5956732, hashes = { sha256 = "092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06" } }, + { url = "https://files.pythonhosted.org/packages/a5/a0/98a3630f0b57f77bae67716562513d3032ae70414fcaf02750279c389a9e/pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:15:58Z, size = 6624404, hashes = { sha256 = "cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a" } }, + { url = "https://files.pythonhosted.org/packages/de/e6/83dfba5646a290edd9a21964da07674409e410579c341fc5b8f7abd81620/pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-07-01T09:16:00Z, size = 6067760, hashes = { sha256 = "6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978" } }, + { url = "https://files.pythonhosted.org/packages/bc/41/15ab268fe6ee9a2bc7391e2bbb20a98d3974304ab1a406a992dcb297a370/pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-07-01T09:16:02Z, size = 6700534, hashes = { sha256 = "97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d" } }, + { url = "https://files.pythonhosted.org/packages/64/79/6d4f638b288300bed727ff29f2a3cb63db054b33518a95f27724915e3fbc/pillow-11.3.0-cp39-cp39-win32.whl", upload-time = 2025-07-01T09:16:04Z, size = 6277091, hashes = { sha256 = "ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71" } }, + { url = "https://files.pythonhosted.org/packages/46/05/4106422f45a05716fd34ed21763f8ec182e8ea00af6e9cb05b93a247361a/pillow-11.3.0-cp39-cp39-win_amd64.whl", upload-time = 2025-07-01T09:16:06Z, size = 6986091, hashes = { sha256 = "e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada" } }, + { url = "https://files.pythonhosted.org/packages/63/c6/287fd55c2c12761d0591549d48885187579b7c257bef0c6660755b0b59ae/pillow-11.3.0-cp39-cp39-win_arm64.whl", upload-time = 2025-07-01T09:16:08Z, size = 2422632, hashes = { sha256 = "6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb" } }, + { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-07-01T09:16:09Z, size = 5270556, hashes = { sha256 = "3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967" } }, + { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:16:11Z, size = 4654625, hashes = { sha256 = "b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe" } }, + { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:11:10Z, size = 4874207, hashes = { sha256 = "e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c" } }, + { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:11:15Z, size = 6583939, hashes = { sha256 = "d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25" } }, + { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:16:13Z, size = 4957166, hashes = { sha256 = "527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27" } }, + { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:16:16Z, size = 5581482, hashes = { sha256 = "be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a" } }, + { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", upload-time = 2025-07-01T09:16:18Z, size = 6984596, hashes = { sha256 = "8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f" } }, + { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-07-01T09:16:19Z, size = 5270566, hashes = { sha256 = "7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6" } }, + { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", upload-time = 2025-07-01T09:16:21Z, size = 4654618, hashes = { sha256 = "9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438" } }, + { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-07-03T13:11:20Z, size = 4874248, hashes = { sha256 = "fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3" } }, + { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-07-03T13:11:26Z, size = 6583963, hashes = { sha256 = "465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c" } }, + { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-07-01T09:16:23Z, size = 4957170, hashes = { sha256 = "5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361" } }, + { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-01T09:16:25Z, size = 5581505, hashes = { sha256 = "504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7" } }, + { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", upload-time = 2025-07-01T09:16:27Z, size = 6984598, hashes = { sha256 = "c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8" } }, +] + +[[packages]] +name = "platformdirs" +version = "4.5.0" +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", upload-time = 2025-10-08T17:44:48Z, size = 21632, hashes = { sha256 = "70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", upload-time = 2025-10-08T17:44:47Z, size = 18651, hashes = { sha256 = "e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3" } }] + +[[packages]] name = "pluggy" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, -] +version = "1.6.0" +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", upload-time = 2025-05-15T12:30:07Z, size = 69412, hashes = { sha256 = "7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", upload-time = 2025-05-15T12:30:06Z, size = 20538, hashes = { sha256 = "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" } }] -[[package]] +[[packages]] name = "prompt-toolkit" -version = "3.0.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810 }, -] +version = "3.0.52" +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", upload-time = 2025-08-27T15:24:02Z, size = 434198, hashes = { sha256 = "28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", upload-time = 2025-08-27T15:23:59Z, size = 391431, hashes = { sha256 = "9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955" } }] -[[package]] +[[packages]] name = "pycparser" -version = "2.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, -] +version = "2.23" +marker = "python_full_version >= '3.9' and implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'" +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", upload-time = 2025-09-09T13:23:47Z, size = 173734, hashes = { sha256 = "78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", upload-time = 2025-09-09T13:23:46Z, size = 118140, hashes = { sha256 = "e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934" } }] -[[package]] +[[packages]] name = "pydantic" -version = "2.11.4" -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/77/ab/5250d56ad03884ab5efd07f734203943c8a8ab40d551e208af81d0257bf2/pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d", size = 786540 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900 }, -] +version = "2.12.2" +sdist = { url = "https://files.pythonhosted.org/packages/8d/35/d319ed522433215526689bad428a94058b6dd12190ce7ddd78618ac14b28/pydantic-2.12.2.tar.gz", upload-time = 2025-10-14T15:02:21Z, size = 816358, hashes = { sha256 = "7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/6c/98/468cb649f208a6f1279448e6e5247b37ae79cf5e4041186f1e2ef3d16345/pydantic-2.12.2-py3-none-any.whl", upload-time = 2025-10-14T15:02:19Z, size = 460628, hashes = { sha256 = "25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, -] - -[[package]] +version = "2.41.4" +sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", upload-time = 2025-10-14T10:23:47Z, size = 457557, hashes = { sha256 = "70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/3d/9b8ca77b0f76fcdbf8bc6b72474e264283f461284ca84ac3fde570c6c49a/pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:19:43Z, size = 2111197, hashes = { sha256 = "2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e" } }, + { url = "https://files.pythonhosted.org/packages/59/92/b7b0fe6ed4781642232755cb7e56a86e2041e1292f16d9ae410a0ccee5ac/pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:19:45Z, size = 1917909, hashes = { sha256 = "30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b" } }, + { url = "https://files.pythonhosted.org/packages/52/8c/3eb872009274ffa4fb6a9585114e161aa1a0915af2896e2d441642929fe4/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:19:46Z, size = 1969905, hashes = { sha256 = "d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd" } }, + { url = "https://files.pythonhosted.org/packages/f4/21/35adf4a753bcfaea22d925214a0c5b880792e3244731b3f3e6fec0d124f7/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:19:48Z, size = 2051938, hashes = { sha256 = "e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945" } }, + { url = "https://files.pythonhosted.org/packages/7d/d0/cdf7d126825e36d6e3f1eccf257da8954452934ede275a8f390eac775e89/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:19:49Z, size = 2250710, hashes = { sha256 = "1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706" } }, + { url = "https://files.pythonhosted.org/packages/2e/1c/af1e6fd5ea596327308f9c8d1654e1285cc3d8de0d584a3c9d7705bf8a7c/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:19:51Z, size = 2367445, hashes = { sha256 = "62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba" } }, + { url = "https://files.pythonhosted.org/packages/d3/81/8cece29a6ef1b3a92f956ea6da6250d5b2d2e7e4d513dd3b4f0c7a83dfea/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:19:52Z, size = 2072875, hashes = { sha256 = "2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b" } }, + { url = "https://files.pythonhosted.org/packages/e3/37/a6a579f5fc2cd4d5521284a0ab6a426cc6463a7b3897aeb95b12f1ba607b/pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:19:54Z, size = 2191329, hashes = { sha256 = "ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d" } }, + { url = "https://files.pythonhosted.org/packages/ae/03/505020dc5c54ec75ecba9f41119fd1e48f9e41e4629942494c4a8734ded1/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:19:55Z, size = 2151658, hashes = { sha256 = "e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700" } }, + { url = "https://files.pythonhosted.org/packages/cb/5d/2c0d09fb53aa03bbd2a214d89ebfa6304be7df9ed86ee3dc7770257f41ee/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:19:57Z, size = 2316777, hashes = { sha256 = "1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6" } }, + { url = "https://files.pythonhosted.org/packages/ea/4b/c2c9c8f5e1f9c864b57d08539d9d3db160e00491c9f5ee90e1bfd905e644/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:19:59Z, size = 2320705, hashes = { sha256 = "a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9" } }, + { url = "https://files.pythonhosted.org/packages/28/c3/a74c1c37f49c0a02c89c7340fafc0ba816b29bd495d1a31ce1bdeacc6085/pydantic_core-2.41.4-cp310-cp310-win32.whl", upload-time = 2025-10-14T10:20:00Z, size = 1975464, hashes = { sha256 = "0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57" } }, + { url = "https://files.pythonhosted.org/packages/d6/23/5dd5c1324ba80303368f7569e2e2e1a721c7d9eb16acb7eb7b7f85cb1be2/pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", upload-time = 2025-10-14T10:20:03Z, size = 2024497, hashes = { sha256 = "a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc" } }, + { url = "https://files.pythonhosted.org/packages/62/4c/f6cbfa1e8efacd00b846764e8484fe173d25b8dab881e277a619177f3384/pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:20:04Z, size = 2109062, hashes = { sha256 = "28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80" } }, + { url = "https://files.pythonhosted.org/packages/21/f8/40b72d3868896bfcd410e1bd7e516e762d326201c48e5b4a06446f6cf9e8/pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:20:06Z, size = 1916301, hashes = { sha256 = "61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae" } }, + { url = "https://files.pythonhosted.org/packages/94/4d/d203dce8bee7faeca791671c88519969d98d3b4e8f225da5b96dad226fc8/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:20:08Z, size = 1968728, hashes = { sha256 = "eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827" } }, + { url = "https://files.pythonhosted.org/packages/65/f5/6a66187775df87c24d526985b3a5d78d861580ca466fbd9d4d0e792fcf6c/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:20:09Z, size = 2050238, hashes = { sha256 = "ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f" } }, + { url = "https://files.pythonhosted.org/packages/5e/b9/78336345de97298cf53236b2f271912ce11f32c1e59de25a374ce12f9cce/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:20:11Z, size = 2249424, hashes = { sha256 = "15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def" } }, + { url = "https://files.pythonhosted.org/packages/99/bb/a4584888b70ee594c3d374a71af5075a68654d6c780369df269118af7402/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:20:13Z, size = 2366047, hashes = { sha256 = "3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2" } }, + { url = "https://files.pythonhosted.org/packages/5f/8d/17fc5de9d6418e4d2ae8c675f905cdafdc59d3bf3bf9c946b7ab796a992a/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:20:15Z, size = 2071163, hashes = { sha256 = "6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8" } }, + { url = "https://files.pythonhosted.org/packages/54/e7/03d2c5c0b8ed37a4617430db68ec5e7dbba66358b629cd69e11b4d564367/pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:20:17Z, size = 2190585, hashes = { sha256 = "5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265" } }, + { url = "https://files.pythonhosted.org/packages/be/fc/15d1c9fe5ad9266a5897d9b932b7f53d7e5cfc800573917a2c5d6eea56ec/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:20:19Z, size = 2150109, hashes = { sha256 = "7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c" } }, + { url = "https://files.pythonhosted.org/packages/26/ef/e735dd008808226c83ba56972566138665b71477ad580fa5a21f0851df48/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:20:20Z, size = 2315078, hashes = { sha256 = "37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a" } }, + { url = "https://files.pythonhosted.org/packages/90/00/806efdcf35ff2ac0f938362350cd9827b8afb116cc814b6b75cf23738c7c/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:20:22Z, size = 2318737, hashes = { sha256 = "0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e" } }, + { url = "https://files.pythonhosted.org/packages/41/7e/6ac90673fe6cb36621a2283552897838c020db343fa86e513d3f563b196f/pydantic_core-2.41.4-cp311-cp311-win32.whl", upload-time = 2025-10-14T10:20:23Z, size = 1974160, hashes = { sha256 = "09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03" } }, + { url = "https://files.pythonhosted.org/packages/e0/9d/7c5e24ee585c1f8b6356e1d11d40ab807ffde44d2db3b7dfd6d20b09720e/pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", upload-time = 2025-10-14T10:20:25Z, size = 2021883, hashes = { sha256 = "711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e" } }, + { url = "https://files.pythonhosted.org/packages/33/90/5c172357460fc28b2871eb4a0fb3843b136b429c6fa827e4b588877bf115/pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", upload-time = 2025-10-14T10:20:27Z, size = 1968026, hashes = { sha256 = "6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db" } }, + { url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:20:28Z, size = 2099043, hashes = { sha256 = "ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887" } }, + { url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:20:30Z, size = 1910699, hashes = { sha256 = "c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2" } }, + { url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:20:32Z, size = 1952121, hashes = { sha256 = "304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999" } }, + { url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:20:34Z, size = 2041590, hashes = { sha256 = "025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4" } }, + { url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:20:35Z, size = 2219869, hashes = { sha256 = "b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f" } }, + { url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:20:37Z, size = 2345169, hashes = { sha256 = "dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b" } }, + { url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:20:39Z, size = 2070165, hashes = { sha256 = "98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47" } }, + { url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:20:41Z, size = 2189067, hashes = { sha256 = "ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970" } }, + { url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:20:43Z, size = 2132997, hashes = { sha256 = "3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed" } }, + { url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:20:44Z, size = 2307187, hashes = { sha256 = "f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8" } }, + { url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:20:46Z, size = 2305204, hashes = { sha256 = "84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431" } }, + { url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", upload-time = 2025-10-14T10:20:48Z, size = 1972536, hashes = { sha256 = "9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd" } }, + { url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", upload-time = 2025-10-14T10:20:50Z, size = 2031132, hashes = { sha256 = "d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff" } }, + { url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", upload-time = 2025-10-14T10:20:52Z, size = 1969483, hashes = { sha256 = "833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8" } }, + { url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:20:54Z, size = 2105688, hashes = { sha256 = "85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746" } }, + { url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:20:56Z, size = 1910807, hashes = { sha256 = "e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced" } }, + { url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:20:57Z, size = 1956669, hashes = { sha256 = "94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a" } }, + { url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:21:00Z, size = 2051629, hashes = { sha256 = "de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02" } }, + { url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:21:01Z, size = 2224049, hashes = { sha256 = "664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1" } }, + { url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:21:03Z, size = 2342409, hashes = { sha256 = "d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2" } }, + { url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:21:05Z, size = 2069635, hashes = { sha256 = "a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84" } }, + { url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:21:07Z, size = 2194284, hashes = { sha256 = "1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d" } }, + { url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:21:08Z, size = 2137566, hashes = { sha256 = "7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d" } }, + { url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:21:10Z, size = 2316809, hashes = { sha256 = "285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2" } }, + { url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:21:12Z, size = 2311119, hashes = { sha256 = "f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab" } }, + { url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", upload-time = 2025-10-14T10:21:14Z, size = 1981398, hashes = { sha256 = "ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c" } }, + { url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", upload-time = 2025-10-14T10:21:16Z, size = 2030735, hashes = { sha256 = "d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4" } }, + { url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", upload-time = 2025-10-14T10:21:18Z, size = 1973209, hashes = { sha256 = "f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564" } }, + { url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:21:20Z, size = 1877324, hashes = { sha256 = "ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4" } }, + { url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:21:22Z, size = 1884515, hashes = { sha256 = "a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2" } }, + { url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:21:26Z, size = 2042819, hashes = { sha256 = "d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf" } }, + { url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", upload-time = 2025-10-14T10:21:28Z, size = 1995866, hashes = { sha256 = "d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2" } }, + { url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", upload-time = 2025-10-14T10:21:30Z, size = 1970034, hashes = { sha256 = "19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89" } }, + { url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:21:32Z, size = 2102022, hashes = { sha256 = "e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1" } }, + { url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:21:34Z, size = 1905495, hashes = { sha256 = "82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac" } }, + { url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:21:36Z, size = 1956131, hashes = { sha256 = "fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554" } }, + { url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:21:38Z, size = 2052236, hashes = { sha256 = "b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e" } }, + { url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:21:41Z, size = 2223573, hashes = { sha256 = "e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616" } }, + { url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:21:44Z, size = 2342467, hashes = { sha256 = "31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af" } }, + { url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:21:46Z, size = 2063754, hashes = { sha256 = "a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12" } }, + { url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:21:48Z, size = 2196754, hashes = { sha256 = "d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d" } }, + { url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:21:50Z, size = 2137115, hashes = { sha256 = "c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad" } }, + { url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:21:52Z, size = 2317400, hashes = { sha256 = "b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a" } }, + { url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:21:55Z, size = 2312070, hashes = { sha256 = "6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025" } }, + { url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", upload-time = 2025-10-14T10:21:57Z, size = 1982277, hashes = { sha256 = "5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e" } }, + { url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", upload-time = 2025-10-14T10:21:59Z, size = 2024608, hashes = { sha256 = "557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894" } }, + { url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", upload-time = 2025-10-14T10:22:01Z, size = 1967614, hashes = { sha256 = "3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d" } }, + { url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:22:04Z, size = 1876904, hashes = { sha256 = "6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da" } }, + { url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:22:06Z, size = 1882538, hashes = { sha256 = "523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e" } }, + { url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:22:08Z, size = 2041183, hashes = { sha256 = "5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa" } }, + { url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", upload-time = 2025-10-14T10:22:11Z, size = 1993542, hashes = { sha256 = "de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d" } }, + { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", upload-time = 2025-10-14T10:22:13Z, size = 1973897, hashes = { sha256 = "d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0" } }, + { url = "https://files.pythonhosted.org/packages/2c/36/f86d582be5fb47d4014506cd9ddd10a3979b6d0f2d237aa6ad3e7033b3ea/pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:22:16Z, size = 2112444, hashes = { sha256 = "646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062" } }, + { url = "https://files.pythonhosted.org/packages/ba/e5/63c521dc2dd106ba6b5941c080617ea9db252f8a7d5625231e9d761bc28c/pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:22:19Z, size = 1938218, hashes = { sha256 = "cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338" } }, + { url = "https://files.pythonhosted.org/packages/30/56/c84b638a3e6e9f5a612b9f5abdad73182520423de43669d639ed4f14b011/pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:22:21Z, size = 1971449, hashes = { sha256 = "692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d" } }, + { url = "https://files.pythonhosted.org/packages/99/c6/e974aade34fc7a0248fdfd0a373d62693502a407c596ab3470165e38183c/pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T10:22:24Z, size = 2054023, hashes = { sha256 = "d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7" } }, + { url = "https://files.pythonhosted.org/packages/4f/91/2507dda801f50980a38d1353c313e8f51349a42b008e63a4e45bf4620562/pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T10:22:26Z, size = 2251614, hashes = { sha256 = "e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166" } }, + { url = "https://files.pythonhosted.org/packages/b2/ad/05d886bc96938f4d31bed24e8d3fc3496d9aea7e77bcff6e4b93127c6de7/pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T10:22:28Z, size = 2378807, hashes = { sha256 = "df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e" } }, + { url = "https://files.pythonhosted.org/packages/6a/0a/d26e1bb9a80b9fc12cc30d9288193fbc9e60a799e55843804ee37bd38a9c/pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:22:30Z, size = 2076891, hashes = { sha256 = "66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891" } }, + { url = "https://files.pythonhosted.org/packages/d9/66/af014e3a294d9933ebfecf11a5d858709014bd2315fa9616195374dd82f0/pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:22:33Z, size = 2192179, hashes = { sha256 = "fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb" } }, + { url = "https://files.pythonhosted.org/packages/e7/3e/79783f97024037d0ea6e1b3ebcd761463a925199e04ce2625727e9f27d06/pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:22:35Z, size = 2153067, hashes = { sha256 = "6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514" } }, + { url = "https://files.pythonhosted.org/packages/b3/97/ea83b0f87d9e742405fb687d5682e7a26334eef2c82a2de06bfbdc305fab/pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:22:38Z, size = 2319048, hashes = { sha256 = "44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005" } }, + { url = "https://files.pythonhosted.org/packages/64/4a/36d8c966a0b086362ac10a7ee75978ed15c5f2dfdfc02a1578d19d3802fb/pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:22:40Z, size = 2321830, hashes = { sha256 = "170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8" } }, + { url = "https://files.pythonhosted.org/packages/a2/6e/d80cc4909dde5f6842861288aa1a7181e7afbfc50940c862ed2848df15bd/pydantic_core-2.41.4-cp39-cp39-win32.whl", upload-time = 2025-10-14T10:22:42Z, size = 1976706, hashes = { sha256 = "3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb" } }, + { url = "https://files.pythonhosted.org/packages/29/ee/5bda8d960d4a8b24a7eeb8a856efa9c865a7a6cab714ed387b29507dc278/pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", upload-time = 2025-10-14T10:22:44Z, size = 2027640, hashes = { sha256 = "a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332" } }, + { url = "https://files.pythonhosted.org/packages/b0/12/5ba58daa7f453454464f92b3ca7b9d7c657d8641c48e370c3ebc9a82dd78/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:22:47Z, size = 2122139, hashes = { sha256 = "a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b" } }, + { url = "https://files.pythonhosted.org/packages/21/fb/6860126a77725c3108baecd10fd3d75fec25191d6381b6eb2ac660228eac/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:22:49Z, size = 1936674, hashes = { sha256 = "d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42" } }, + { url = "https://files.pythonhosted.org/packages/de/be/57dcaa3ed595d81f8757e2b44a38240ac5d37628bce25fb20d02c7018776/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:22:52Z, size = 1956398, hashes = { sha256 = "0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee" } }, + { url = "https://files.pythonhosted.org/packages/2f/1d/679a344fadb9695f1a6a294d739fbd21d71fa023286daeea8c0ed49e7c2b/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:22:54Z, size = 2138674, hashes = { sha256 = "1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c" } }, + { url = "https://files.pythonhosted.org/packages/c4/48/ae937e5a831b7c0dc646b2ef788c27cd003894882415300ed21927c21efa/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:22:56Z, size = 2112087, hashes = { sha256 = "4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537" } }, + { url = "https://files.pythonhosted.org/packages/5e/db/6db8073e3d32dae017da7e0d16a9ecb897d0a4d92e00634916e486097961/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:22:59Z, size = 1920387, hashes = { sha256 = "4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94" } }, + { url = "https://files.pythonhosted.org/packages/0d/c1/dd3542d072fcc336030d66834872f0328727e3b8de289c662faa04aa270e/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T10:23:02Z, size = 1951495, hashes = { sha256 = "e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c" } }, + { url = "https://files.pythonhosted.org/packages/2b/c6/db8d13a1f8ab3f1eb08c88bd00fd62d44311e3456d1e85c0e59e0a0376e7/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:23:04Z, size = 2139008, hashes = { sha256 = "bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335" } }, + { url = "https://files.pythonhosted.org/packages/5d/d4/912e976a2dd0b49f31c98a060ca90b353f3b73ee3ea2fd0030412f6ac5ec/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:23:06Z, size = 2106739, hashes = { sha256 = "1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00" } }, + { url = "https://files.pythonhosted.org/packages/71/f0/66ec5a626c81eba326072d6ee2b127f8c139543f1bf609b4842978d37833/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:23:09Z, size = 1932549, hashes = { sha256 = "3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9" } }, + { url = "https://files.pythonhosted.org/packages/c4/af/625626278ca801ea0a658c2dcf290dc9f21bb383098e99e7c6a029fccfc0/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:23:11Z, size = 2135093, hashes = { sha256 = "7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2" } }, + { url = "https://files.pythonhosted.org/packages/20/f6/2fba049f54e0f4975fef66be654c597a1d005320fa141863699180c7697d/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:23:14Z, size = 2187971, hashes = { sha256 = "b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258" } }, + { url = "https://files.pythonhosted.org/packages/0e/80/65ab839a2dfcd3b949202f9d920c34f9de5a537c3646662bdf2f7d999680/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:23:16Z, size = 2147939, hashes = { sha256 = "6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347" } }, + { url = "https://files.pythonhosted.org/packages/44/58/627565d3d182ce6dfda18b8e1c841eede3629d59c9d7cbc1e12a03aeb328/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:23:19Z, size = 2311400, hashes = { sha256 = "4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa" } }, + { url = "https://files.pythonhosted.org/packages/24/06/8a84711162ad5a5f19a88cead37cca81b4b1f294f46260ef7334ae4f24d3/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:23:21Z, size = 2316840, hashes = { sha256 = "b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a" } }, + { url = "https://files.pythonhosted.org/packages/aa/8b/b7bb512a4682a2f7fbfae152a755d37351743900226d29bd953aaf870eaa/pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", upload-time = 2025-10-14T10:23:24Z, size = 2149135, hashes = { sha256 = "3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d" } }, + { url = "https://files.pythonhosted.org/packages/7e/7d/138e902ed6399b866f7cfe4435d22445e16fff888a1c00560d9dc79a780f/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T10:23:26Z, size = 2104721, hashes = { sha256 = "491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5" } }, + { url = "https://files.pythonhosted.org/packages/47/13/0525623cf94627f7b53b4c2034c81edc8491cbfc7c28d5447fa318791479/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", upload-time = 2025-10-14T10:23:29Z, size = 1931608, hashes = { sha256 = "54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2" } }, + { url = "https://files.pythonhosted.org/packages/d6/f9/744bc98137d6ef0a233f808bfc9b18cf94624bf30836a18d3b05d08bf418/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T10:23:32Z, size = 2132986, hashes = { sha256 = "eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd" } }, + { url = "https://files.pythonhosted.org/packages/17/c8/629e88920171173f6049386cc71f893dff03209a9ef32b4d2f7e7c264bcf/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-10-14T10:23:34Z, size = 2187516, hashes = { sha256 = "6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c" } }, + { url = "https://files.pythonhosted.org/packages/2e/0f/4f2734688d98488782218ca61bcc118329bf5de05bb7fe3adc7dd79b0b86/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T10:23:37Z, size = 2146146, hashes = { sha256 = "26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405" } }, + { url = "https://files.pythonhosted.org/packages/ed/f2/ab385dbd94a052c62224b99cf99002eee99dbec40e10006c78575aead256/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", upload-time = 2025-10-14T10:23:40Z, size = 2311296, hashes = { sha256 = "ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8" } }, + { url = "https://files.pythonhosted.org/packages/fc/8e/e4f12afe1beeb9823bba5375f8f258df0cc61b056b0195fb1cf9f62a1a58/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T10:23:42Z, size = 2315386, hashes = { sha256 = "5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308" } }, + { url = "https://files.pythonhosted.org/packages/48/f7/925f65d930802e3ea2eb4d5afa4cb8730c8dc0d2cb89a59dc4ed2fcb2d74/pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", upload-time = 2025-10-14T10:23:45Z, size = 2147775, hashes = { sha256 = "c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f" } }, +] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, -] - -[[package]] -name = "pyjwt" -version = "2.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, -] - -[[package]] +version = "2.11.0" +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", upload-time = 2025-09-24T14:19:11Z, size = 188394, hashes = { sha256 = "d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", upload-time = 2025-09-24T14:19:10Z, size = 48608, hashes = { sha256 = "fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c" } }] + +[[packages]] +name = "pygments" +version = "2.19.2" +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", upload-time = 2025-06-21T13:39:12Z, size = 4968631, hashes = { sha256 = "636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", upload-time = 2025-06-21T13:39:07Z, size = 1225217, hashes = { sha256 = "86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" } }] + +[[packages]] +name = "pymdown-extensions" +version = "10.16.1" +sdist = { url = "https://files.pythonhosted.org/packages/55/b3/6d2b3f149bc5413b0a29761c2c5832d8ce904a1d7f621e86616d96f505cc/pymdown_extensions-10.16.1.tar.gz", upload-time = 2025-07-28T16:19:34Z, size = 853277, hashes = { sha256 = "aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/e4/06/43084e6cbd4b3bc0e80f6be743b2e79fbc6eed8de9ad8c629939fa55d972/pymdown_extensions-10.16.1-py3-none-any.whl", upload-time = 2025-07-28T16:19:31Z, size = 266178, hashes = { sha256 = "d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d" } }] + +[[packages]] name = "pymgclient" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "networkx" }, - { name = "pyopenssl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6e/58/5da9c14122ce01cd428f729206c79458f7f6d7ff997a80938fa4c9b63ea3/pymgclient-1.4.0.tar.gz", hash = "sha256:6dfe026ffbfa9a81ad9b042589a536a3fab8bcb630b6c44f3d33ef97ed1bd1b4", size = 127094 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/3e/ea749505e1b32350158ebd3a4b8b3f565426c833ce52adffd4149e0c902d/pymgclient-1.4.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4be6217832b778d7d9a20391de2c96259cac5ca152844191464a0be9b2e9630c", size = 2458110 }, - { url = "https://files.pythonhosted.org/packages/45/a7/4269f217744924633cb6624a2855c6fe9e8cfcca243d0d17a2eb41a76029/pymgclient-1.4.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a87eec1fde175924a061d0f3ea8e4acedad444b33b82b1da0b7b320fc46e91e3", size = 2455399 }, - { url = "https://files.pythonhosted.org/packages/66/e3/15f242f8515ff4485dc6d19c6991f462d48838535e535e5f70c446f8c51c/pymgclient-1.4.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:3a0c7fa17dc046a82c3f48125cde82fdf20a3803cf361774fdac709072ed4201", size = 2461777 }, - { url = "https://files.pythonhosted.org/packages/70/08/0ba18b451b70fe76bf691b16f7db1dcce3cd2edd9d3bee941e688bb7c882/pymgclient-1.4.0-cp313-cp313-manylinux_2_38_x86_64.manylinux_2_39_x86_64.whl", hash = "sha256:bf701b47a1d0c1eee2a8a16bde0d96a26669d2ef652ea1a32f9c1d1a6e8b5393", size = 2369362 }, - { url = "https://files.pythonhosted.org/packages/bb/f4/098b11a1ab25498161a09f5e44fbc43ba8ec41ba5df2b100422627d247db/pymgclient-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ee8d8205537291b8d5e6193823bdd48d2db9992075b0565f7c4f789146624a5", size = 2203872 }, -] - -[[package]] +version = "1.5.1" +sdist = { url = "https://files.pythonhosted.org/packages/d2/78/15d68f3389b4a28f61fe385a499d71ac00019b449785d937f649e20e2a46/pymgclient-1.5.1.tar.gz", upload-time = 2025-08-20T13:51:43Z, size = 129855, hashes = { sha256 = "defce8fc712ed2ca32ca75acfb3787b43a89592d8cef10b0f3e2aa7092e3d140" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/51/20b8b6cf1a04dd6a4be3d842039619c60d7c125ba9a3a47dfb67686117f2/pymgclient-1.5.1-cp310-cp310-macosx_14_0_arm64.whl", upload-time = 2025-08-20T13:51:20Z, size = 2463744, hashes = { sha256 = "36fc48656c3b28887495f7f3301fba5791a0ffa505caf07ee225f147c3e28162" } }, + { url = "https://files.pythonhosted.org/packages/53/05/e6547655f01484305fbe79f080a66acdb770e80eb18799c3bfc9af58dd96/pymgclient-1.5.1-cp310-cp310-macosx_15_0_arm64.whl", upload-time = 2025-08-20T13:51:22Z, size = 2471065, hashes = { sha256 = "db43fbe52ed283f1d7f5db4687f29b6a0481b0fb3057d7da05104a9c669569c3" } }, + { url = "https://files.pythonhosted.org/packages/67/0b/63f6fe96ffde22449a600e8ab703aa25b289645d6d9f3569da761a6ac9e2/pymgclient-1.5.1-cp310-cp310-manylinux_2_38_x86_64.manylinux_2_39_x86_64.whl", upload-time = 2025-08-20T13:51:24Z, size = 2367184, hashes = { sha256 = "9d9b13774e996544cdae12757d7ae5530d78a112fd4b62cd583a22a508786b45" } }, + { url = "https://files.pythonhosted.org/packages/ad/aa/4ed8b48189e2c7898b6858ad0aa4fab22cbc8eb1e9d9e97e2aa6a91f7830/pymgclient-1.5.1-cp310-cp310-win_amd64.whl", upload-time = 2025-08-20T13:51:26Z, size = 2345604, hashes = { sha256 = "53e9b6cd3742d4c9ebf25fe6fb646c8ca96f7b8836a5f6fdb7b8614624b81b3a" } }, + { url = "https://files.pythonhosted.org/packages/57/20/010d02afc24e445647ca511259b58c1c1e80b5f2ad1d5e48f5694d818919/pymgclient-1.5.1-cp311-cp311-macosx_14_0_arm64.whl", upload-time = 2025-08-20T13:51:27Z, size = 2463638, hashes = { sha256 = "f953f690fa4d6343bba31f192b6740fb8e4dd570e4d6c08b51abfcf528a991fc" } }, + { url = "https://files.pythonhosted.org/packages/c8/83/0a3e70fcbd7f5dcafdcd095ab1a00d45172d89a00a4aac76d56a9326ffaa/pymgclient-1.5.1-cp311-cp311-macosx_15_0_arm64.whl", upload-time = 2025-08-20T13:51:29Z, size = 2471060, hashes = { sha256 = "f7463d60e2c4ce1492be8bf2a7e9cc89759b64e5ba98d9efa9e58b47c127aebd" } }, + { url = "https://files.pythonhosted.org/packages/53/75/984b8b02e02861d57dcaf5cc766611f4848356026305655cd110cd6b8bd7/pymgclient-1.5.1-cp311-cp311-manylinux_2_38_x86_64.manylinux_2_39_x86_64.whl", upload-time = 2025-08-20T13:51:30Z, size = 2370255, hashes = { sha256 = "a95df69dfe5eac4c66ea5974f401ea9cf635de7adb04c0a97ec8c1cb3909e615" } }, + { url = "https://files.pythonhosted.org/packages/61/f6/ddc2be9ac5daa5654c3d13eaa0321392fe39986a79796e281fb70912ef9b/pymgclient-1.5.1-cp311-cp311-win_amd64.whl", upload-time = 2025-08-20T13:51:31Z, size = 2345614, hashes = { sha256 = "ce5cb8fc6de966cdf7d7c0a58a742321f50cda6ff84ec20ad8e6123bb9344494" } }, + { url = "https://files.pythonhosted.org/packages/d5/27/c1045f7d32e20e7b29a522e8c1458fc8c1bcc84c799e9804ea342915a8da/pymgclient-1.5.1-cp312-cp312-macosx_14_0_arm64.whl", upload-time = 2025-08-20T13:51:33Z, size = 2463072, hashes = { sha256 = "504d0c3c3417d029a6376b810c66aa8b2299357c067d0f6292a78b1e1c5f89df" } }, + { url = "https://files.pythonhosted.org/packages/fa/41/9d4a67cc207300f6bf454948cc7ca6da6dc1ef9717ac4c7a0fc83d913d5b/pymgclient-1.5.1-cp312-cp312-macosx_15_0_arm64.whl", upload-time = 2025-08-20T13:51:34Z, size = 2470601, hashes = { sha256 = "e543f9328f45254bdbc497a0afb03f75be25641595ef38faa27931a1bb143ac6" } }, + { url = "https://files.pythonhosted.org/packages/6d/9f/89ab956fc0ef084242bda5a62a2e9d213a8af6ef27d353d7c6a31e26429c/pymgclient-1.5.1-cp312-cp312-manylinux_2_38_x86_64.manylinux_2_39_x86_64.whl", upload-time = 2025-08-20T13:51:36Z, size = 2374887, hashes = { sha256 = "21af7e4eac4d3d6a68dc9fee9d9598c13d9700842ebb9c51c65d67add8bbfba1" } }, + { url = "https://files.pythonhosted.org/packages/1b/68/cbd7eefa98f6a46a3a8d27009530d431f005c90c74eca205b6cb71be33a4/pymgclient-1.5.1-cp312-cp312-win_amd64.whl", upload-time = 2025-08-20T13:51:37Z, size = 2346293, hashes = { sha256 = "729c4a025c2eda7673bd7eeb60c3273194b63acbf0397ebbf7f33c272937ba8d" } }, + { url = "https://files.pythonhosted.org/packages/33/33/d5b68864a5f19a36eadfe195884202e547bbc858f76beb92aa761d8148b0/pymgclient-1.5.1-cp313-cp313-macosx_14_0_arm64.whl", upload-time = 2025-08-20T13:51:38Z, size = 2463067, hashes = { sha256 = "7ca3ca6a00c57638f141a20990fedf26f3558903ce92c8f9c1b5e3709280699f" } }, + { url = "https://files.pythonhosted.org/packages/42/58/7b40481fe99a4f187ea3eda9ad325b4c2a35c3fbfad15563f469b801f5fe/pymgclient-1.5.1-cp313-cp313-macosx_15_0_arm64.whl", upload-time = 2025-08-20T13:51:40Z, size = 2470605, hashes = { sha256 = "cde56666e314406b7eace394b3f9cd067e15f73f0d55e1a3892ab02f8985ede2" } }, + { url = "https://files.pythonhosted.org/packages/76/c9/5f1dd1af7d4c7e5897619ffd4e190deb0d866de567b4cd0f2ea00df7bdb9/pymgclient-1.5.1-cp313-cp313-manylinux_2_38_x86_64.manylinux_2_39_x86_64.whl", upload-time = 2025-08-20T13:51:41Z, size = 2374943, hashes = { sha256 = "3b8b6c36a74970ba2e93b8329fddf9728c562c1a6f3df846ffbc833eea525052" } }, + { url = "https://files.pythonhosted.org/packages/f6/4d/d02cd10cdfc907c74441081896c52dec65c50310d001c26394195efec535/pymgclient-1.5.1-cp313-cp313-win_amd64.whl", upload-time = 2025-08-20T13:51:42Z, size = 2346729, hashes = { sha256 = "b8330095b124a783d2de7a57913e25928737cdf88f28fd72a5682abfd49e8eb4" } }, +] + +[[packages]] name = "pyopenssl" -version = "25.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/26/e25b4a374b4639e0c235527bbe31c0524f26eda701d79456a7e1877f4cc5/pyopenssl-25.0.0.tar.gz", hash = "sha256:cd2cef799efa3936bb08e8ccb9433a575722b9dd986023f1cabc4ae64e9dac16", size = 179573 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/d7/eb76863d2060dcbe7c7e6cccfd95ac02ea0b9acc37745a0d99ff6457aefb/pyOpenSSL-25.0.0-py3-none-any.whl", hash = "sha256:424c247065e46e76a37411b9ab1782541c23bb658bf003772c3405fbaa128e90", size = 56453 }, -] +version = "25.3.0" +sdist = { url = "https://files.pythonhosted.org/packages/80/be/97b83a464498a79103036bc74d1038df4a7ef0e402cfaf4d5e113fb14759/pyopenssl-25.3.0.tar.gz", upload-time = 2025-09-17T00:32:21Z, size = 184073, hashes = { sha256 = "c981cb0a3fd84e8602d7afc209522773b94c1c2446a3c710a75b06fe1beae329" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/d1/81/ef2b1dfd1862567d573a4fdbc9f969067621764fbb74338496840a1d2977/pyopenssl-25.3.0-py3-none-any.whl", upload-time = 2025-09-17T00:32:19Z, size = 57268, hashes = { sha256 = "1fda6fc034d5e3d179d39e59c1895c9faeaf40a79de5fc4cbbfbe0d36f4a77b6" } }] -[[package]] +[[packages]] +name = "pyright" +version = "1.1.406" +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/6b4fbdd1fef59a0292cbb99f790b44983e390321eccbc5921b4d161da5d1/pyright-1.1.406.tar.gz", upload-time = 2025-10-02T01:04:45Z, size = 4113151, hashes = { sha256 = "c4872bc58c9643dac09e8a2e74d472c62036910b3bd37a32813989ef7576ea2c" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/f6/a2/e309afbb459f50507103793aaef85ca4348b66814c86bc73908bdeb66d12/pyright-1.1.406-py3-none-any.whl", upload-time = 2025-10-02T01:04:43Z, size = 5980982, hashes = { sha256 = "1d81fb43c2407bf566e97e57abb01c811973fdb21b2df8df59f870f688bdca71" } }] + +[[packages]] name = "pytest" -version = "8.3.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, -] +version = "8.4.2" +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", upload-time = 2025-09-04T14:34:22Z, size = 1519618, hashes = { sha256 = "86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", upload-time = 2025-09-04T14:34:20Z, size = 365750, hashes = { sha256 = "872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79" } }] + +[[packages]] +name = "pytest-cov" +version = "7.0.0" +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", upload-time = 2025-09-09T10:57:02Z, size = 54328, hashes = { sha256 = "33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", upload-time = 2025-09-09T10:57:00Z, size = 22424, hashes = { sha256 = "3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861" } }] -[[package]] +[[packages]] name = "python-dateutil" version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } -wheels = [ - { 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 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", upload-time = 2024-03-01T18:36:20Z, size = 342432, hashes = { sha256 = "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", upload-time = 2024-03-01T18:36:18Z, size = 229892, hashes = { sha256 = "a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, -] +version = "1.1.1" +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", upload-time = 2025-06-24T04:21:07Z, size = 41978, hashes = { sha256 = "a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", upload-time = 2025-06-24T04:21:06Z, size = 20556, hashes = { sha256 = "31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc" } }] -[[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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, -] +[[packages]] +name = "pytz" +version = "2025.2" +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", upload-time = 2025-03-25T02:25:00Z, size = 320884, hashes = { sha256 = "360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", upload-time = 2025-03-25T02:24:58Z, size = 509225, hashes = { sha256 = "5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" } }] -[[package]] +[[packages]] +name = "pyyaml" +version = "6.0.3" +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", upload-time = 2025-09-25T21:33:16Z, size = 130960, hashes = { sha256 = "d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/a2/09f67a3589cb4320fb5ce90d3fd4c9752636b8b6ad8f34b54d76c5a54693/PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", upload-time = 2025-09-29T20:27:35Z, size = 186824, hashes = { sha256 = "c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f" } }, + { url = "https://files.pythonhosted.org/packages/02/72/d972384252432d57f248767556ac083793292a4adf4e2d85dfe785ec2659/PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-29T20:27:38Z, size = 795069, hashes = { sha256 = "9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4" } }, + { url = "https://files.pythonhosted.org/packages/a7/3b/6c58ac0fa7c4e1b35e48024eb03d00817438310447f93ef4431673c24138/PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-29T20:27:39Z, size = 862585, hashes = { sha256 = "efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3" } }, + { url = "https://files.pythonhosted.org/packages/25/a2/b725b61ac76a75583ae7104b3209f75ea44b13cfd026aa535ece22b7f22e/PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-29T20:27:41Z, size = 806018, hashes = { sha256 = "22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6" } }, + { url = "https://files.pythonhosted.org/packages/6f/b0/b2227677b2d1036d84f5ee95eb948e7af53d59fe3e4328784e4d290607e0/PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", upload-time = 2025-09-29T20:27:42Z, size = 802822, hashes = { sha256 = "6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369" } }, + { url = "https://files.pythonhosted.org/packages/99/a5/718a8ea22521e06ef19f91945766a892c5ceb1855df6adbde67d997ea7ed/PyYAML-6.0.3-cp38-cp38-win32.whl", upload-time = 2025-09-29T20:27:44Z, size = 143744, hashes = { sha256 = "3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295" } }, + { url = "https://files.pythonhosted.org/packages/76/b2/2b69cee94c9eb215216fc05778675c393e3aa541131dc910df8e52c83776/PyYAML-6.0.3-cp38-cp38-win_amd64.whl", upload-time = 2025-09-29T20:27:46Z, size = 160082, hashes = { sha256 = "5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b" } }, + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:31:46Z, size = 184227, hashes = { sha256 = "214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b" } }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:31:47Z, size = 174019, hashes = { sha256 = "02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956" } }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-25T21:31:49Z, size = 740646, hashes = { sha256 = "b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8" } }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-25T21:31:50Z, size = 840793, hashes = { sha256 = "66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198" } }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-25T21:31:51Z, size = 770293, hashes = { sha256 = "9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b" } }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:31:53Z, size = 732872, hashes = { sha256 = "418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0" } }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:31:54Z, size = 758828, hashes = { sha256 = "5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69" } }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", upload-time = 2025-09-25T21:31:55Z, size = 142415, hashes = { sha256 = "28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e" } }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", upload-time = 2025-09-25T21:31:57Z, size = 158561, hashes = { sha256 = "bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c" } }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:31:58Z, size = 185826, hashes = { sha256 = "44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" } }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:32:00Z, size = 175577, hashes = { sha256 = "652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" } }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-25T21:32:01Z, size = 775556, hashes = { sha256 = "10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c" } }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-25T21:32:03Z, size = 882114, hashes = { sha256 = "850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00" } }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-25T21:32:04Z, size = 806638, hashes = { sha256 = "b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" } }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:32:06Z, size = 767463, hashes = { sha256 = "1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" } }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:32:07Z, size = 794986, hashes = { sha256 = "37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" } }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", upload-time = 2025-09-25T21:32:08Z, size = 142543, hashes = { sha256 = "8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" } }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", upload-time = 2025-09-25T21:32:09Z, size = 158763, hashes = { sha256 = "9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" } }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:32:11Z, size = 182063, hashes = { sha256 = "7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" } }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:32:12Z, size = 173973, hashes = { sha256 = "fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" } }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-25T21:32:13Z, size = 775116, hashes = { sha256 = "9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28" } }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-25T21:32:15Z, size = 844011, hashes = { sha256 = "5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c" } }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-25T21:32:16Z, size = 807870, hashes = { sha256 = "ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc" } }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:32:17Z, size = 761089, hashes = { sha256 = "8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" } }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:32:18Z, size = 790181, hashes = { sha256 = "41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" } }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", upload-time = 2025-09-25T21:32:20Z, size = 137658, hashes = { sha256 = "96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" } }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", upload-time = 2025-09-25T21:32:21Z, size = 154003, hashes = { sha256 = "5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" } }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", upload-time = 2025-09-25T21:32:22Z, size = 140344, hashes = { sha256 = "64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" } }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:32:23Z, size = 181669, hashes = { sha256 = "8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" } }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:32:25Z, size = 173252, hashes = { sha256 = "2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" } }, + { 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", upload-time = 2025-09-25T21:32:26Z, size = 767081, hashes = { sha256 = "ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c" } }, + { 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", upload-time = 2025-09-25T21:32:27Z, size = 841159, hashes = { sha256 = "a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5" } }, + { 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", upload-time = 2025-09-25T21:32:28Z, size = 801626, hashes = { sha256 = "0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6" } }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:32:30Z, size = 753613, hashes = { sha256 = "f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" } }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:32:31Z, size = 794115, hashes = { sha256 = "eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" } }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", upload-time = 2025-09-25T21:32:32Z, size = 137427, hashes = { sha256 = "d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" } }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", upload-time = 2025-09-25T21:32:33Z, size = 154090, hashes = { sha256 = "79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" } }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", upload-time = 2025-09-25T21:32:34Z, size = 140246, hashes = { sha256 = "5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" } }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:32:35Z, size = 181814, hashes = { sha256 = "8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" } }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:32:36Z, size = 173809, hashes = { sha256 = "34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" } }, + { 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", upload-time = 2025-09-25T21:32:37Z, size = 766454, hashes = { sha256 = "501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7" } }, + { 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", upload-time = 2025-09-25T21:32:39Z, size = 836355, hashes = { sha256 = "b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788" } }, + { 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", upload-time = 2025-09-25T21:32:40Z, size = 794175, hashes = { sha256 = "c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5" } }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:32:42Z, size = 755228, hashes = { sha256 = "7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" } }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:32:43Z, size = 789194, hashes = { sha256 = "5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" } }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", upload-time = 2025-09-25T21:32:57Z, size = 156429, hashes = { sha256 = "4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" } }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", upload-time = 2025-09-25T21:32:59Z, size = 143912, hashes = { sha256 = "93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" } }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:32:44Z, size = 189108, hashes = { sha256 = "02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" } }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:32:45Z, size = 183641, hashes = { sha256 = "c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" } }, + { 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", upload-time = 2025-09-25T21:32:48Z, size = 831901, hashes = { sha256 = "6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c" } }, + { 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", upload-time = 2025-09-25T21:32:50Z, size = 861132, hashes = { sha256 = "a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702" } }, + { 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", upload-time = 2025-09-25T21:32:51Z, size = 839261, hashes = { sha256 = "00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c" } }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:32:52Z, size = 805272, hashes = { sha256 = "66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" } }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:32:54Z, size = 829923, hashes = { sha256 = "16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" } }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-25T21:32:55Z, size = 174062, hashes = { sha256 = "4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" } }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-25T21:32:56Z, size = 149341, hashes = { sha256 = "ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" } }, + { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", upload-time = 2025-09-25T21:33:00Z, size = 184450, hashes = { sha256 = "b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da" } }, + { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-25T21:33:02Z, size = 174319, hashes = { sha256 = "c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917" } }, + { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-25T21:33:03Z, size = 737631, hashes = { sha256 = "3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9" } }, + { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-25T21:33:05Z, size = 836795, hashes = { sha256 = "5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5" } }, + { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-25T21:33:06Z, size = 750767, hashes = { sha256 = "0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a" } }, + { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-09-25T21:33:08Z, size = 727982, hashes = { sha256 = "fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926" } }, + { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-09-25T21:33:09Z, size = 755677, hashes = { sha256 = "27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7" } }, + { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", upload-time = 2025-09-25T21:33:10Z, size = 142592, hashes = { sha256 = "1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0" } }, + { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", upload-time = 2025-09-25T21:33:15Z, size = 158777, hashes = { sha256 = "2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007" } }, +] + +[[packages]] +name = "pyyaml-env-tag" +version = "1.1" +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", upload-time = 2025-05-13T15:24:01Z, size = 5737, hashes = { sha256 = "2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", upload-time = 2025-05-13T15:23:59Z, size = 4722, hashes = { sha256 = "17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04" } }] + +[[packages]] name = "rapidfuzz" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/6895abc3a3d056b9698da3199b04c0e56226d530ae44a470edabf8b664f0/rapidfuzz-3.13.0.tar.gz", hash = "sha256:d2eaf3839e52cbcc0accbe9817a67b4b0fcf70aaeb229cfddc1c28061f9ce5d8", size = 57904226 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/76/606e71e4227790750f1646f3c5c873e18d6cfeb6f9a77b2b8c4dec8f0f66/rapidfuzz-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09e908064d3684c541d312bd4c7b05acb99a2c764f6231bd507d4b4b65226c23", size = 1982282 }, - { url = "https://files.pythonhosted.org/packages/0a/f5/d0b48c6b902607a59fd5932a54e3518dae8223814db8349b0176e6e9444b/rapidfuzz-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57c390336cb50d5d3bfb0cfe1467478a15733703af61f6dffb14b1cd312a6fae", size = 1439274 }, - { url = "https://files.pythonhosted.org/packages/59/cf/c3ac8c80d8ced6c1f99b5d9674d397ce5d0e9d0939d788d67c010e19c65f/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0da54aa8547b3c2c188db3d1c7eb4d1bb6dd80baa8cdaeaec3d1da3346ec9caa", size = 1399854 }, - { url = "https://files.pythonhosted.org/packages/09/5d/ca8698e452b349c8313faf07bfa84e7d1c2d2edf7ccc67bcfc49bee1259a/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8e8c21e67afb9d7fbe18f42c6111fe155e801ab103c81109a61312927cc611", size = 5308962 }, - { url = "https://files.pythonhosted.org/packages/66/0a/bebada332854e78e68f3d6c05226b23faca79d71362509dbcf7b002e33b7/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:461fd13250a2adf8e90ca9a0e1e166515cbcaa5e9c3b1f37545cbbeff9e77f6b", size = 1625016 }, - { url = "https://files.pythonhosted.org/packages/de/0c/9e58d4887b86d7121d1c519f7050d1be5eb189d8a8075f5417df6492b4f5/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2b3dd5d206a12deca16870acc0d6e5036abeb70e3cad6549c294eff15591527", size = 1600414 }, - { url = "https://files.pythonhosted.org/packages/9b/df/6096bc669c1311568840bdcbb5a893edc972d1c8d2b4b4325c21d54da5b1/rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1343d745fbf4688e412d8f398c6e6d6f269db99a54456873f232ba2e7aeb4939", size = 3053179 }, - { url = "https://files.pythonhosted.org/packages/f9/46/5179c583b75fce3e65a5cd79a3561bd19abd54518cb7c483a89b284bf2b9/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b1b065f370d54551dcc785c6f9eeb5bd517ae14c983d2784c064b3aa525896df", size = 2456856 }, - { url = "https://files.pythonhosted.org/packages/6b/64/e9804212e3286d027ac35bbb66603c9456c2bce23f823b67d2f5cabc05c1/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:11b125d8edd67e767b2295eac6eb9afe0b1cdc82ea3d4b9257da4b8e06077798", size = 7567107 }, - { url = "https://files.pythonhosted.org/packages/8a/f2/7d69e7bf4daec62769b11757ffc31f69afb3ce248947aadbb109fefd9f65/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c33f9c841630b2bb7e69a3fb5c84a854075bb812c47620978bddc591f764da3d", size = 2854192 }, - { url = "https://files.pythonhosted.org/packages/05/21/ab4ad7d7d0f653e6fe2e4ccf11d0245092bef94cdff587a21e534e57bda8/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae4574cb66cf1e85d32bb7e9ec45af5409c5b3970b7ceb8dea90168024127566", size = 3398876 }, - { url = "https://files.pythonhosted.org/packages/0f/a8/45bba94c2489cb1ee0130dcb46e1df4fa2c2b25269e21ffd15240a80322b/rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e05752418b24bbd411841b256344c26f57da1148c5509e34ea39c7eb5099ab72", size = 4377077 }, - { url = "https://files.pythonhosted.org/packages/0c/f3/5e0c6ae452cbb74e5436d3445467447e8c32f3021f48f93f15934b8cffc2/rapidfuzz-3.13.0-cp313-cp313-win32.whl", hash = "sha256:0e1d08cb884805a543f2de1f6744069495ef527e279e05370dd7c83416af83f8", size = 1822066 }, - { url = "https://files.pythonhosted.org/packages/96/e3/a98c25c4f74051df4dcf2f393176b8663bfd93c7afc6692c84e96de147a2/rapidfuzz-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9a7c6232be5f809cd39da30ee5d24e6cadd919831e6020ec6c2391f4c3bc9264", size = 1615100 }, - { url = "https://files.pythonhosted.org/packages/60/b1/05cd5e697c00cd46d7791915f571b38c8531f714832eff2c5e34537c49ee/rapidfuzz-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:3f32f15bacd1838c929b35c84b43618481e1b3d7a61b5ed2db0291b70ae88b53", size = 858976 }, -] - -[[package]] +version = "3.14.1" +sdist = { url = "https://files.pythonhosted.org/packages/ed/fc/a98b616db9a42dcdda7c78c76bdfdf6fe290ac4c5ffbb186f73ec981ad5b/rapidfuzz-3.14.1.tar.gz", upload-time = 2025-09-08T21:08:15Z, size = 57869570, hashes = { sha256 = "b02850e7f7152bd1edff27e9d584505b84968cacedee7a734ec4050c655a803c" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/b9/4e35178f405a1a95abd37cce4dc09d4a5bbc5e098687680b5ba796d3115b/rapidfuzz-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-09-08T21:05:16Z, size = 1939645, hashes = { sha256 = "489440e4b5eea0d150a31076eb183bed0ec84f934df206c72ae4fc3424501758" } }, + { url = "https://files.pythonhosted.org/packages/51/af/fd7b8662a3b6952559af322dcf1c9d4eb5ec6be2697c30ae8ed3c44876ca/rapidfuzz-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:05:18Z, size = 1393620, hashes = { sha256 = "eff22cc938c3f74d194df03790a6c3325d213b28cf65cdefd6fdeae759b745d5" } }, + { url = "https://files.pythonhosted.org/packages/c5/5b/5715445e29c1c6ba364b3d27278da3fdffb18d9147982e977c6638dcecbf/rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-08T21:05:20Z, size = 1387721, hashes = { sha256 = "e0307f018b16feaa36074bcec2496f6f120af151a098910296e72e233232a62f" } }, + { url = "https://files.pythonhosted.org/packages/19/49/83a14a6a90982b090257c4b2e96b9b9c423a89012b8504d5a14d92a4f8c2/rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-08T21:05:22Z, size = 1694545, hashes = { sha256 = "bc133652da143aca1ab72de235446432888b2b7f44ee332d006f8207967ecb8a" } }, + { url = "https://files.pythonhosted.org/packages/99/f7/94618fcaaac8c04abf364f405c6811a02bc9edef209f276dc513a9a50f7c/rapidfuzz-3.14.1-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-08T21:05:23Z, size = 2237075, hashes = { sha256 = "e9e71b3fe7e4a1590843389a90fe2a8684649fc74b9b7446e17ee504ddddb7de" } }, + { url = "https://files.pythonhosted.org/packages/58/f6/a5ee2db25f36b0e5e06502fb77449b7718cd9f92ad36d598e669ba91db7b/rapidfuzz-3.14.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-08T21:05:25Z, size = 3168778, hashes = { sha256 = "6c51519eb2f20b52eba6fc7d857ae94acc6c2a1f5d0f2d794b9d4977cdc29dd7" } }, + { url = "https://files.pythonhosted.org/packages/0f/e8/c9620e358805c099e6755b7d2827b1e711b5e61914d6112ce2faa2c2af79/rapidfuzz-3.14.1-cp310-cp310-manylinux_2_31_armv7l.whl", upload-time = 2025-09-08T21:05:27Z, size = 1223827, hashes = { sha256 = "fe87d94602624f8f25fff9a0a7b47f33756c4d9fc32b6d3308bb142aa483b8a4" } }, + { url = "https://files.pythonhosted.org/packages/84/08/24916c3c3d55d6236474c9da0a595641d0013d3604de0625e8a8974371c3/rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T21:05:28Z, size = 2408366, hashes = { sha256 = "2d665380503a575dda52eb712ea521f789e8f8fd629c7a8e6c0f8ff480febc78" } }, + { url = "https://files.pythonhosted.org/packages/40/d4/4152e8821b5c548443a6c46568fccef13de5818a5ab370d553ea3d5955b3/rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", upload-time = 2025-09-08T21:05:30Z, size = 2530148, hashes = { sha256 = "c0f0dd022b8a7cbf3c891f6de96a80ab6a426f1069a085327816cea749e096c2" } }, + { url = "https://files.pythonhosted.org/packages/bd/af/6587c6d590abe232c530ad43fbfbcaec899bff7204e237f1fd21e2e44b81/rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-08T21:05:32Z, size = 2810628, hashes = { sha256 = "bf1ba22d36858b265c95cd774ba7fe8991e80a99cd86fe4f388605b01aee81a3" } }, + { url = "https://files.pythonhosted.org/packages/d7/90/a99e6cfd90feb9d770654f1f39321099bbbf7f85d2832f2ef48d3f4ebc5f/rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", upload-time = 2025-09-08T21:05:34Z, size = 3314406, hashes = { sha256 = "ca1c1494ac9f9386d37f0e50cbaf4d07d184903aed7691549df1b37e9616edc9" } }, + { url = "https://files.pythonhosted.org/packages/5f/b3/eba5a6c217200fd1d3615997930a9e5db6a74e3002b7867b54545f9b5cbb/rapidfuzz-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T21:05:36Z, size = 4280030, hashes = { sha256 = "9e4b12e921b0fa90d7c2248742a536f21eae5562174090b83edd0b4ab8b557d7" } }, + { url = "https://files.pythonhosted.org/packages/04/6f/d2e060a2094cfb7f3cd487c376e098abb22601e0eea178e51a59ce0a3158/rapidfuzz-3.14.1-cp310-cp310-win32.whl", upload-time = 2025-09-08T21:05:38Z, size = 1727070, hashes = { sha256 = "5e1c1f2292baa4049535b07e9e81feb29e3650d2ba35ee491e64aca7ae4cb15e" } }, + { url = "https://files.pythonhosted.org/packages/73/0a/ca231464ec689f2aabf9547a52cbc76a10affe960bddde8660699ba3de33/rapidfuzz-3.14.1-cp310-cp310-win_amd64.whl", upload-time = 2025-09-08T21:05:40Z, size = 1545335, hashes = { sha256 = "59a8694beb9a13c4090ab3d1712cabbd896c6949706d1364e2a2e1713c413760" } }, + { url = "https://files.pythonhosted.org/packages/59/c5/1e0b17f20fd3d701470548a6db8f36d589fb1a8a65d3828968547d987486/rapidfuzz-3.14.1-cp310-cp310-win_arm64.whl", upload-time = 2025-09-08T21:05:42Z, size = 816960, hashes = { sha256 = "e94cee93faa792572c574a615abe12912124b4ffcf55876b72312914ab663345" } }, + { url = "https://files.pythonhosted.org/packages/5c/c7/c3c860d512606225c11c8ee455b4dc0b0214dbcfac90a2c22dddf55320f3/rapidfuzz-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-08T21:05:44Z, size = 1938398, hashes = { sha256 = "4d976701060886a791c8a9260b1d4139d14c1f1e9a6ab6116b45a1acf3baff67" } }, + { url = "https://files.pythonhosted.org/packages/c0/f3/67f5c5cd4d728993c48c1dcb5da54338d77c03c34b4903cc7839a3b89faf/rapidfuzz-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:05:45Z, size = 1392819, hashes = { sha256 = "5e6ba7e6eb2ab03870dcab441d707513db0b4264c12fba7b703e90e8b4296df2" } }, + { url = "https://files.pythonhosted.org/packages/d5/06/400d44842f4603ce1bebeaeabe776f510e329e7dbf6c71b6f2805e377889/rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-08T21:05:47Z, size = 1391798, hashes = { sha256 = "1e532bf46de5fd3a1efde73a16a4d231d011bce401c72abe3c6ecf9de681003f" } }, + { url = "https://files.pythonhosted.org/packages/90/97/a6944955713b47d88e8ca4305ca7484940d808c4e6c4e28b6fa0fcbff97e/rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-08T21:05:48Z, size = 1699136, hashes = { sha256 = "f9b6a6fb8ed9b951e5f3b82c1ce6b1665308ec1a0da87f799b16e24fc59e4662" } }, + { url = "https://files.pythonhosted.org/packages/a8/1e/f311a5c95ddf922db6dd8666efeceb9ac69e1319ed098ac80068a4041732/rapidfuzz-3.14.1-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-08T21:05:50Z, size = 2236238, hashes = { sha256 = "5b6ac3f9810949caef0e63380b11a3c32a92f26bacb9ced5e32c33560fcdf8d1" } }, + { url = "https://files.pythonhosted.org/packages/85/27/e14e9830255db8a99200f7111b158ddef04372cf6332a415d053fe57cc9c/rapidfuzz-3.14.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-08T21:05:52Z, size = 3183685, hashes = { sha256 = "e52e4c34fd567f77513e886b66029c1ae02f094380d10eba18ba1c68a46d8b90" } }, + { url = "https://files.pythonhosted.org/packages/61/b2/42850c9616ddd2887904e5dd5377912cbabe2776fdc9fd4b25e6e12fba32/rapidfuzz-3.14.1-cp311-cp311-manylinux_2_31_armv7l.whl", upload-time = 2025-09-08T21:05:53Z, size = 1231523, hashes = { sha256 = "2ef72e41b1a110149f25b14637f1cedea6df192462120bea3433980fe9d8ac05" } }, + { url = "https://files.pythonhosted.org/packages/de/b5/6b90ed7127a1732efef39db46dd0afc911f979f215b371c325a2eca9cb15/rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T21:05:55Z, size = 2415209, hashes = { sha256 = "fb654a35b373d712a6b0aa2a496b2b5cdd9d32410cfbaecc402d7424a90ba72a" } }, + { url = "https://files.pythonhosted.org/packages/70/60/af51c50d238c82f2179edc4b9f799cc5a50c2c0ebebdcfaa97ded7d02978/rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", upload-time = 2025-09-08T21:05:57Z, size = 2532957, hashes = { sha256 = "2b2c12e5b9eb8fe9a51b92fe69e9ca362c0970e960268188a6d295e1dec91e6d" } }, + { url = "https://files.pythonhosted.org/packages/50/92/29811d2ba7c984251a342c4f9ccc7cc4aa09d43d800af71510cd51c36453/rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-08T21:05:58Z, size = 2815720, hashes = { sha256 = "4f069dec5c450bd987481e752f0a9979e8fdf8e21e5307f5058f5c4bb162fa56" } }, + { url = "https://files.pythonhosted.org/packages/78/69/cedcdee16a49e49d4985eab73b59447f211736c5953a58f1b91b6c53a73f/rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", upload-time = 2025-09-08T21:06:00Z, size = 3323704, hashes = { sha256 = "4d0d9163725b7ad37a8c46988cae9ebab255984db95ad01bf1987ceb9e3058dd" } }, + { url = "https://files.pythonhosted.org/packages/76/3e/5a3f9a5540f18e0126e36f86ecf600145344acb202d94b63ee45211a18b8/rapidfuzz-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T21:06:02Z, size = 4287341, hashes = { sha256 = "db656884b20b213d846f6bc990c053d1f4a60e6d4357f7211775b02092784ca1" } }, + { url = "https://files.pythonhosted.org/packages/46/26/45db59195929dde5832852c9de8533b2ac97dcc0d852d1f18aca33828122/rapidfuzz-3.14.1-cp311-cp311-win32.whl", upload-time = 2025-09-08T21:06:04Z, size = 1726574, hashes = { sha256 = "4b42f7b9c58cbcfbfaddc5a6278b4ca3b6cd8983e7fd6af70ca791dff7105fb9" } }, + { url = "https://files.pythonhosted.org/packages/01/5c/a4caf76535f35fceab25b2aaaed0baecf15b3d1fd40746f71985d20f8c4b/rapidfuzz-3.14.1-cp311-cp311-win_amd64.whl", upload-time = 2025-09-08T21:06:06Z, size = 1547124, hashes = { sha256 = "e5847f30d7d4edefe0cb37294d956d3495dd127c1c56e9128af3c2258a520bb4" } }, + { url = "https://files.pythonhosted.org/packages/c6/66/aa93b52f95a314584d71fa0b76df00bdd4158aafffa76a350f1ae416396c/rapidfuzz-3.14.1-cp311-cp311-win_arm64.whl", upload-time = 2025-09-08T21:06:07Z, size = 816958, hashes = { sha256 = "5087d8ad453092d80c042a08919b1cb20c8ad6047d772dc9312acd834da00f75" } }, + { url = "https://files.pythonhosted.org/packages/df/77/2f4887c9b786f203e50b816c1cde71f96642f194e6fa752acfa042cf53fd/rapidfuzz-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T21:06:09Z, size = 1932216, hashes = { sha256 = "809515194f628004aac1b1b280c3734c5ea0ccbd45938c9c9656a23ae8b8f553" } }, + { url = "https://files.pythonhosted.org/packages/de/bd/b5e445d156cb1c2a87d36d8da53daf4d2a1d1729b4851660017898b49aa0/rapidfuzz-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:06:10Z, size = 1393414, hashes = { sha256 = "0afcf2d6cb633d0d4260d8df6a40de2d9c93e9546e2c6b317ab03f89aa120ad7" } }, + { url = "https://files.pythonhosted.org/packages/de/bd/98d065dd0a4479a635df855616980eaae1a1a07a876db9400d421b5b6371/rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-08T21:06:12Z, size = 1377194, hashes = { sha256 = "5c1c3d07d53dcafee10599da8988d2b1f39df236aee501ecbd617bd883454fcd" } }, + { url = "https://files.pythonhosted.org/packages/d3/8a/1265547b771128b686f3c431377ff1db2fa073397ed082a25998a7b06d4e/rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-08T21:06:14Z, size = 1669573, hashes = { sha256 = "6e9ee3e1eb0a027717ee72fe34dc9ac5b3e58119f1bd8dd15bc19ed54ae3e62b" } }, + { url = "https://files.pythonhosted.org/packages/a8/57/e73755c52fb451f2054196404ccc468577f8da023b3a48c80bce29ee5d4a/rapidfuzz-3.14.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-08T21:06:15Z, size = 2217833, hashes = { sha256 = "70c845b64a033a20c44ed26bc890eeb851215148cc3e696499f5f65529afb6cb" } }, + { url = "https://files.pythonhosted.org/packages/20/14/7399c18c460e72d1b754e80dafc9f65cb42a46cc8f29cd57d11c0c4acc94/rapidfuzz-3.14.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-08T21:06:17Z, size = 3159012, hashes = { sha256 = "26db0e815213d04234298dea0d884d92b9cb8d4ba954cab7cf67a35853128a33" } }, + { url = "https://files.pythonhosted.org/packages/f8/5e/24f0226ddb5440cabd88605d2491f99ae3748a6b27b0bc9703772892ced7/rapidfuzz-3.14.1-cp312-cp312-manylinux_2_31_armv7l.whl", upload-time = 2025-09-08T21:06:21Z, size = 1227032, hashes = { sha256 = "6ad3395a416f8b126ff11c788531f157c7debeb626f9d897c153ff8980da10fb" } }, + { url = "https://files.pythonhosted.org/packages/40/43/1d54a4ad1a5fac2394d5f28a3108e2bf73c26f4f23663535e3139cfede9b/rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T21:06:23Z, size = 2395054, hashes = { sha256 = "61c5b9ab6f730e6478aa2def566223712d121c6f69a94c7cc002044799442afd" } }, + { url = "https://files.pythonhosted.org/packages/0c/71/e9864cd5b0f086c4a03791f5dfe0155a1b132f789fe19b0c76fbabd20513/rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", upload-time = 2025-09-08T21:06:26Z, size = 2524741, hashes = { sha256 = "13e0ea3d0c533969158727d1bb7a08c2cc9a816ab83f8f0dcfde7e38938ce3e6" } }, + { url = "https://files.pythonhosted.org/packages/b2/0c/53f88286b912faf4a3b2619a60df4f4a67bd0edcf5970d7b0c1143501f0c/rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-08T21:06:29Z, size = 2785311, hashes = { sha256 = "6325ca435b99f4001aac919ab8922ac464999b100173317defb83eae34e82139" } }, + { url = "https://files.pythonhosted.org/packages/53/9a/229c26dc4f91bad323f07304ee5ccbc28f0d21c76047a1e4f813187d0bad/rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", upload-time = 2025-09-08T21:06:31Z, size = 3303630, hashes = { sha256 = "07a9fad3247e68798424bdc116c1094e88ecfabc17b29edf42a777520347648e" } }, + { url = "https://files.pythonhosted.org/packages/05/de/20e330d6d58cbf83da914accd9e303048b7abae2f198886f65a344b69695/rapidfuzz-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T21:06:32Z, size = 4262364, hashes = { sha256 = "f8ff5dbe78db0a10c1f916368e21d328935896240f71f721e073cf6c4c8cdedd" } }, + { url = "https://files.pythonhosted.org/packages/1f/10/2327f83fad3534a8d69fe9cd718f645ec1fe828b60c0e0e97efc03bf12f8/rapidfuzz-3.14.1-cp312-cp312-win32.whl", upload-time = 2025-09-08T21:06:34Z, size = 1711927, hashes = { sha256 = "9c83270e44a6ae7a39fc1d7e72a27486bccc1fa5f34e01572b1b90b019e6b566" } }, + { url = "https://files.pythonhosted.org/packages/78/8d/199df0370133fe9f35bc72f3c037b53c93c5c1fc1e8d915cf7c1f6bb8557/rapidfuzz-3.14.1-cp312-cp312-win_amd64.whl", upload-time = 2025-09-08T21:06:36Z, size = 1542045, hashes = { sha256 = "e06664c7fdb51c708e082df08a6888fce4c5c416d7e3cc2fa66dd80eb76a149d" } }, + { url = "https://files.pythonhosted.org/packages/b3/c6/cc5d4bd1b16ea2657c80b745d8b1c788041a31fad52e7681496197b41562/rapidfuzz-3.14.1-cp312-cp312-win_arm64.whl", upload-time = 2025-09-08T21:06:38Z, size = 813170, hashes = { sha256 = "6c7c26025f7934a169a23dafea6807cfc3fb556f1dd49229faf2171e5d8101cc" } }, + { url = "https://files.pythonhosted.org/packages/0d/f2/0024cc8eead108c4c29337abe133d72ddf3406ce9bbfbcfc110414a7ea07/rapidfuzz-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T21:06:39Z, size = 1926515, hashes = { sha256 = "8d69f470d63ee824132ecd80b1974e1d15dd9df5193916901d7860cef081a260" } }, + { url = "https://files.pythonhosted.org/packages/12/ae/6cb211f8930bea20fa989b23f31ee7f92940caaf24e3e510d242a1b28de4/rapidfuzz-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:06:41Z, size = 1388431, hashes = { sha256 = "6f571d20152fc4833b7b5e781b36d5e4f31f3b5a596a3d53cf66a1bd4436b4f4" } }, + { url = "https://files.pythonhosted.org/packages/39/88/bfec24da0607c39e5841ced5594ea1b907d20f83adf0e3ee87fa454a425b/rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-08T21:06:43Z, size = 1375664, hashes = { sha256 = "61d77e09b2b6bc38228f53b9ea7972a00722a14a6048be9a3672fb5cb08bad3a" } }, + { url = "https://files.pythonhosted.org/packages/f4/43/9f282ba539e404bdd7052c7371d3aaaa1a9417979d2a1d8332670c7f385a/rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-08T21:06:45Z, size = 1668113, hashes = { sha256 = "8b41d95ef86a6295d353dc3bb6c80550665ba2c3bef3a9feab46074d12a9af8f" } }, + { url = "https://files.pythonhosted.org/packages/7f/2f/0b3153053b1acca90969eb0867922ac8515b1a8a48706a3215c2db60e87c/rapidfuzz-3.14.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-08T21:06:47Z, size = 2212875, hashes = { sha256 = "0591df2e856ad583644b40a2b99fb522f93543c65e64b771241dda6d1cfdc96b" } }, + { url = "https://files.pythonhosted.org/packages/f8/9b/623001dddc518afaa08ed1fbbfc4005c8692b7a32b0f08b20c506f17a770/rapidfuzz-3.14.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-08T21:06:49Z, size = 3161181, hashes = { sha256 = "f277801f55b2f3923ef2de51ab94689a0671a4524bf7b611de979f308a54cd6f" } }, + { url = "https://files.pythonhosted.org/packages/ce/b7/d8404ed5ad56eb74463e5ebf0a14f0019d7eb0e65e0323f709fe72e0884c/rapidfuzz-3.14.1-cp313-cp313-manylinux_2_31_armv7l.whl", upload-time = 2025-09-08T21:06:51Z, size = 1225495, hashes = { sha256 = "893fdfd4f66ebb67f33da89eb1bd1674b7b30442fdee84db87f6cb9074bf0ce9" } }, + { url = "https://files.pythonhosted.org/packages/2c/6c/b96af62bc7615d821e3f6b47563c265fd7379d7236dfbc1cbbcce8beb1d2/rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T21:06:53Z, size = 2396294, hashes = { sha256 = "fe2651258c1f1afa9b66f44bf82f639d5f83034f9804877a1bbbae2120539ad1" } }, + { url = "https://files.pythonhosted.org/packages/7f/b7/c60c9d22a7debed8b8b751f506a4cece5c22c0b05e47a819d6b47bc8c14e/rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", upload-time = 2025-09-08T21:06:55Z, size = 2529629, hashes = { sha256 = "ace21f7a78519d8e889b1240489cd021c5355c496cb151b479b741a4c27f0a25" } }, + { url = "https://files.pythonhosted.org/packages/25/94/a9ec7ccb28381f14de696ffd51c321974762f137679df986f5375d35264f/rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-08T21:06:57Z, size = 2782960, hashes = { sha256 = "cb5acf24590bc5e57027283b015950d713f9e4d155fda5cfa71adef3b3a84502" } }, + { url = "https://files.pythonhosted.org/packages/68/80/04e5276d223060eca45250dbf79ea39940c0be8b3083661d58d57572c2c5/rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", upload-time = 2025-09-08T21:06:59Z, size = 3298427, hashes = { sha256 = "67ea46fa8cc78174bad09d66b9a4b98d3068e85de677e3c71ed931a1de28171f" } }, + { url = "https://files.pythonhosted.org/packages/4a/63/24759b2a751562630b244e68ccaaf7a7525c720588fcc77c964146355aee/rapidfuzz-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T21:07:01Z, size = 4267736, hashes = { sha256 = "44e741d785de57d1a7bae03599c1cbc7335d0b060a35e60c44c382566e22782e" } }, + { url = "https://files.pythonhosted.org/packages/18/a4/73f1b1f7f44d55f40ffbffe85e529eb9d7e7f7b2ffc0931760eadd163995/rapidfuzz-3.14.1-cp313-cp313-win32.whl", upload-time = 2025-09-08T21:07:03Z, size = 1710515, hashes = { sha256 = "b1fe6001baa9fa36bcb565e24e88830718f6c90896b91ceffcb48881e3adddbc" } }, + { url = "https://files.pythonhosted.org/packages/6a/8b/a8fe5a6ee4d06fd413aaa9a7e0a23a8630c4b18501509d053646d18c2aa7/rapidfuzz-3.14.1-cp313-cp313-win_amd64.whl", upload-time = 2025-09-08T21:07:05Z, size = 1540081, hashes = { sha256 = "83b8cc6336709fa5db0579189bfd125df280a554af544b2dc1c7da9cdad7e44d" } }, + { url = "https://files.pythonhosted.org/packages/ac/fe/4b0ac16c118a2367d85450b45251ee5362661e9118a1cef88aae1765ffff/rapidfuzz-3.14.1-cp313-cp313-win_arm64.whl", upload-time = 2025-09-08T21:07:07Z, size = 812725, hashes = { sha256 = "cf75769662eadf5f9bd24e865c19e5ca7718e879273dce4e7b3b5824c4da0eb4" } }, + { url = "https://files.pythonhosted.org/packages/e2/cb/1ad9a76d974d153783f8e0be8dbe60ec46488fac6e519db804e299e0da06/rapidfuzz-3.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T21:07:08Z, size = 1945173, hashes = { sha256 = "d937dbeda71c921ef6537c6d41a84f1b8112f107589c9977059de57a1d726dd6" } }, + { url = "https://files.pythonhosted.org/packages/d9/61/959ed7460941d8a81cbf6552b9c45564778a36cf5e5aa872558b30fc02b2/rapidfuzz-3.14.1-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:07:11Z, size = 1413949, hashes = { sha256 = "7a2d80cc1a4fcc7e259ed4f505e70b36433a63fa251f1bb69ff279fe376c5efd" } }, + { url = "https://files.pythonhosted.org/packages/7b/a0/f46fca44457ca1f25f23cc1f06867454fc3c3be118cd10b552b0ab3e58a2/rapidfuzz-3.14.1-cp313-cp313t-win32.whl", upload-time = 2025-09-08T21:07:12Z, size = 1760666, hashes = { sha256 = "40875e0c06f1a388f1cab3885744f847b557e0b1642dfc31ff02039f9f0823ef" } }, + { url = "https://files.pythonhosted.org/packages/9b/d0/7a5d9c04446f8b66882b0fae45b36a838cf4d31439b5d1ab48a9d17c8e57/rapidfuzz-3.14.1-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-08T21:07:14Z, size = 1579760, hashes = { sha256 = "876dc0c15552f3d704d7fb8d61bdffc872ff63bedf683568d6faad32e51bbce8" } }, + { url = "https://files.pythonhosted.org/packages/4e/aa/2c03ae112320d0746f2c869cae68c413f3fe3b6403358556f2b747559723/rapidfuzz-3.14.1-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-08T21:07:17Z, size = 832088, hashes = { sha256 = "61458e83b0b3e2abc3391d0953c47d6325e506ba44d6a25c869c4401b3bc222c" } }, + { url = "https://files.pythonhosted.org/packages/d6/36/53debca45fbe693bd6181fb05b6a2fd561c87669edb82ec0d7c1961a43f0/rapidfuzz-3.14.1-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T21:07:18Z, size = 1926336, hashes = { sha256 = "e84d9a844dc2e4d5c4cabd14c096374ead006583304333c14a6fbde51f612a44" } }, + { url = "https://files.pythonhosted.org/packages/ae/32/b874f48609665fcfeaf16cbaeb2bbc210deef2b88e996c51cfc36c3eb7c3/rapidfuzz-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:07:20Z, size = 1389653, hashes = { sha256 = "40301b93b99350edcd02dbb22e37ca5f2a75d0db822e9b3c522da451a93d6f27" } }, + { url = "https://files.pythonhosted.org/packages/97/25/f6c5a1ff4ec11edadacb270e70b8415f51fa2f0d5730c2c552b81651fbe3/rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-08T21:07:22Z, size = 1380911, hashes = { sha256 = "fedd5097a44808dddf341466866e5c57a18a19a336565b4ff50aa8f09eb528f6" } }, + { url = "https://files.pythonhosted.org/packages/d8/f3/d322202ef8fab463759b51ebfaa33228100510c82e6153bd7a922e150270/rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-08T21:07:24Z, size = 1673515, hashes = { sha256 = "2e3e61c9e80d8c26709d8aa5c51fdd25139c81a4ab463895f8a567f8347b0548" } }, + { url = "https://files.pythonhosted.org/packages/8d/b9/6b2a97f4c6be96cac3749f32301b8cdf751ce5617b1c8934c96586a0662b/rapidfuzz-3.14.1-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-08T21:07:26Z, size = 2219394, hashes = { sha256 = "da011a373722fac6e64687297a1d17dc8461b82cb12c437845d5a5b161bc24b9" } }, + { url = "https://files.pythonhosted.org/packages/11/bf/afb76adffe4406e6250f14ce48e60a7eb05d4624945bd3c044cfda575fbc/rapidfuzz-3.14.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-08T21:07:28Z, size = 3163582, hashes = { sha256 = "5967d571243cfb9ad3710e6e628ab68c421a237b76e24a67ac22ee0ff12784d6" } }, + { url = "https://files.pythonhosted.org/packages/42/34/e6405227560f61e956cb4c5de653b0f874751c5ada658d3532d6c1df328e/rapidfuzz-3.14.1-cp314-cp314-manylinux_2_31_armv7l.whl", upload-time = 2025-09-08T21:07:30Z, size = 1221116, hashes = { sha256 = "474f416cbb9099676de54aa41944c154ba8d25033ee460f87bb23e54af6d01c9" } }, + { url = "https://files.pythonhosted.org/packages/55/e6/5b757e2e18de384b11d1daf59608453f0baf5d5d8d1c43e1a964af4dc19a/rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-08T21:07:32Z, size = 2402670, hashes = { sha256 = "ae2d57464b59297f727c4e201ea99ec7b13935f1f056c753e8103da3f2fc2404" } }, + { url = "https://files.pythonhosted.org/packages/43/c4/d753a415fe54531aa882e288db5ed77daaa72e05c1a39e1cbac00d23024f/rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", upload-time = 2025-09-08T21:07:35Z, size = 2521659, hashes = { sha256 = "57047493a1f62f11354c7143c380b02f1b355c52733e6b03adb1cb0fe8fb8816" } }, + { url = "https://files.pythonhosted.org/packages/cd/28/d4e7fe1515430db98f42deb794c7586a026d302fe70f0216b638d89cf10f/rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-08T21:07:37Z, size = 2788552, hashes = { sha256 = "4acc20776f225ee37d69517a237c090b9fa7e0836a0b8bc58868e9168ba6ef6f" } }, + { url = "https://files.pythonhosted.org/packages/4f/00/eab05473af7a2cafb4f3994bc6bf408126b8eec99a569aac6254ac757db4/rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", upload-time = 2025-09-08T21:07:39Z, size = 3306261, hashes = { sha256 = "4373f914ff524ee0146919dea96a40a8200ab157e5a15e777a74a769f73d8a4a" } }, + { url = "https://files.pythonhosted.org/packages/d1/31/2feb8dfcfcff6508230cd2ccfdde7a8bf988c6fda142fe9ce5d3eb15704d/rapidfuzz-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-08T21:07:41Z, size = 4269522, hashes = { sha256 = "37017b84953927807847016620d61251fe236bd4bcb25e27b6133d955bb9cafb" } }, + { url = "https://files.pythonhosted.org/packages/a3/99/250538d73c8fbab60597c3d131a11ef2a634d38b44296ca11922794491ac/rapidfuzz-3.14.1-cp314-cp314-win32.whl", upload-time = 2025-09-08T21:07:44Z, size = 1745018, hashes = { sha256 = "c8d1dd1146539e093b84d0805e8951475644af794ace81d957ca612e3eb31598" } }, + { url = "https://files.pythonhosted.org/packages/c5/15/d50839d20ad0743aded25b08a98ffb872f4bfda4e310bac6c111fcf6ea1f/rapidfuzz-3.14.1-cp314-cp314-win_amd64.whl", upload-time = 2025-09-08T21:07:46Z, size = 1587666, hashes = { sha256 = "f51c7571295ea97387bac4f048d73cecce51222be78ed808263b45c79c40a440" } }, + { url = "https://files.pythonhosted.org/packages/a3/ff/d73fec989213fb6f0b6f15ee4bbdf2d88b0686197951a06b036111cd1c7d/rapidfuzz-3.14.1-cp314-cp314-win_arm64.whl", upload-time = 2025-09-08T21:07:49Z, size = 835780, hashes = { sha256 = "01eab10ec90912d7d28b3f08f6c91adbaf93458a53f849ff70776ecd70dd7a7a" } }, + { url = "https://files.pythonhosted.org/packages/b7/e7/f0a242687143cebd33a1fb165226b73bd9496d47c5acfad93de820a18fa8/rapidfuzz-3.14.1-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-08T21:07:51Z, size = 1945182, hashes = { sha256 = "60879fcae2f7618403c4c746a9a3eec89327d73148fb6e89a933b78442ff0669" } }, + { url = "https://files.pythonhosted.org/packages/96/29/ca8a3f8525e3d0e7ab49cb927b5fb4a54855f794c9ecd0a0b60a6c96a05f/rapidfuzz-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:07:53Z, size = 1413946, hashes = { sha256 = "f94d61e44db3fc95a74006a394257af90fa6e826c900a501d749979ff495d702" } }, + { url = "https://files.pythonhosted.org/packages/b5/ef/6fd10aa028db19c05b4ac7fe77f5613e4719377f630c709d89d7a538eea2/rapidfuzz-3.14.1-cp314-cp314t-win32.whl", upload-time = 2025-09-08T21:07:55Z, size = 1795851, hashes = { sha256 = "93b6294a3ffab32a9b5f9b5ca048fa0474998e7e8bb0f2d2b5e819c64cb71ec7" } }, + { url = "https://files.pythonhosted.org/packages/e4/30/acd29ebd906a50f9e0f27d5f82a48cf5e8854637b21489bd81a2459985cf/rapidfuzz-3.14.1-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-08T21:07:58Z, size = 1626748, hashes = { sha256 = "6cb56b695421538fdbe2c0c85888b991d833b8637d2f2b41faa79cea7234c000" } }, + { url = "https://files.pythonhosted.org/packages/c1/f4/dfc7b8c46b1044a47f7ca55deceb5965985cff3193906cb32913121e6652/rapidfuzz-3.14.1-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-08T21:08:00Z, size = 853771, hashes = { sha256 = "7cd312c380d3ce9d35c3ec9726b75eee9da50e8a38e89e229a03db2262d3d96b" } }, + { url = "https://files.pythonhosted.org/packages/6d/10/0ed838b296fdac08ecbaa3a220fb4f1d887ff41b0be44fe8eade45bb650e/rapidfuzz-3.14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-09-08T21:08:02Z, size = 1860246, hashes = { sha256 = "673ce55a9be5b772dade911909e42382c0828b8a50ed7f9168763fa6b9f7054d" } }, + { url = "https://files.pythonhosted.org/packages/a4/70/a08f4a86387dec97508ead51cc7a4b3130d4e62ac0eae938a6d8e1feff14/rapidfuzz-3.14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:08:04Z, size = 1336749, hashes = { sha256 = "45c62ada1980ebf4c64c4253993cc8daa018c63163f91db63bb3af69cb74c2e3" } }, + { url = "https://files.pythonhosted.org/packages/d4/39/c12f76f69184bcfb9977d6404b2c5dac7dd4d70ee6803e61556e539d0097/rapidfuzz-3.14.1-pp310-pypy310_pp73-win_amd64.whl", upload-time = 2025-09-08T21:08:06Z, size = 1512629, hashes = { sha256 = "4d51efb29c0df0d4f7f64f672a7624c2146527f0745e3572098d753676538800" } }, + { url = "https://files.pythonhosted.org/packages/05/c7/1b17347e30f2b50dd976c54641aa12003569acb1bdaabf45a5cc6f471c58/rapidfuzz-3.14.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-09-08T21:08:09Z, size = 1862602, hashes = { sha256 = "4a21ccdf1bd7d57a1009030527ba8fae1c74bf832d0a08f6b67de8f5c506c96f" } }, + { url = "https://files.pythonhosted.org/packages/09/cf/95d0dacac77eda22499991bd5f304c77c5965fb27348019a48ec3fe4a3f6/rapidfuzz-3.14.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", upload-time = 2025-09-08T21:08:11Z, size = 1339548, hashes = { sha256 = "589fb0af91d3aff318750539c832ea1100dbac2c842fde24e42261df443845f6" } }, + { url = "https://files.pythonhosted.org/packages/b6/58/f515c44ba8c6fa5daa35134b94b99661ced852628c5505ead07b905c3fc7/rapidfuzz-3.14.1-pp311-pypy311_pp73-win_amd64.whl", upload-time = 2025-09-08T21:08:13Z, size = 1513859, hashes = { sha256 = "a4f18092db4825f2517d135445015b40033ed809a41754918a03ef062abe88a0" } }, +] + +[[packages]] name = "redis" -version = "5.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyjwt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/dd/2b37032f4119dff2a2f9bbcaade03221b100ba26051bb96e275de3e5db7a/redis-5.3.0.tar.gz", hash = "sha256:8d69d2dde11a12dc85d0dbf5c45577a5af048e2456f7077d87ad35c1c81c310e", size = 4626288 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/b0/aa601efe12180ba492b02e270554877e68467e66bda5d73e51eaa8ecc78a/redis-5.3.0-py3-none-any.whl", hash = "sha256:f1deeca1ea2ef25c1e4e46b07f4ea1275140526b1feea4c6459c0ec27a10ef83", size = 272836 }, -] +version = "5.2.1" +sdist = { url = "https://files.pythonhosted.org/packages/47/da/d283a37303a995cd36f8b92db85135153dc4f7a8e4441aa827721b442cfb/redis-5.2.1.tar.gz", upload-time = 2024-12-06T09:50:41Z, size = 4608355, hashes = { sha256 = "16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/3c/5f/fa26b9b2672cbe30e07d9a5bdf39cf16e3b80b42916757c5f92bca88e4ba/redis-5.2.1-py3-none-any.whl", upload-time = 2024-12-06T09:50:39Z, size = 261502, hashes = { sha256 = "ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, -] - -[[package]] +version = "2025.9.18" +sdist = { url = "https://files.pythonhosted.org/packages/49/d3/eaa0d28aba6ad1827ad1e716d9a93e1ba963ada61887498297d3da715133/regex-2025.9.18.tar.gz", upload-time = 2025-09-19T00:38:35Z, size = 400917, hashes = { sha256 = "c5ba23274c61c6fef447ba6a39333297d0c247f53059dba0bca415cac511edc4" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d8/7e06171db8e55f917c5b8e89319cea2d86982e3fc46b677f40358223dece/regex-2025.9.18-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2025-09-19T00:35:05Z, size = 484829, hashes = { sha256 = "12296202480c201c98a84aecc4d210592b2f55e200a1d193235c4db92b9f6788" } }, + { url = "https://files.pythonhosted.org/packages/8d/70/bf91bb39e5bedf75ce730ffbaa82ca585584d13335306d637458946b8b9f/regex-2025.9.18-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-09-19T00:35:08Z, size = 288993, hashes = { sha256 = "220381f1464a581f2ea988f2220cf2a67927adcef107d47d6897ba5a2f6d51a4" } }, + { url = "https://files.pythonhosted.org/packages/fe/89/69f79b28365eda2c46e64c39d617d5f65a2aa451a4c94de7d9b34c2dc80f/regex-2025.9.18-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:35:09Z, size = 286624, hashes = { sha256 = "87f681bfca84ebd265278b5daa1dcb57f4db315da3b5d044add7c30c10442e61" } }, + { url = "https://files.pythonhosted.org/packages/44/31/81e62955726c3a14fcc1049a80bc716765af6c055706869de5e880ddc783/regex-2025.9.18-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:35:11Z, size = 780473, hashes = { sha256 = "34d674cbba70c9398074c8a1fcc1a79739d65d1105de2a3c695e2b05ea728251" } }, + { url = "https://files.pythonhosted.org/packages/fb/23/07072b7e191fbb6e213dc03b2f5b96f06d3c12d7deaded84679482926fc7/regex-2025.9.18-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:35:12Z, size = 849290, hashes = { sha256 = "385c9b769655cb65ea40b6eea6ff763cbb6d69b3ffef0b0db8208e1833d4e746" } }, + { url = "https://files.pythonhosted.org/packages/b3/f0/aec7f6a01f2a112210424d77c6401b9015675fb887ced7e18926df4ae51e/regex-2025.9.18-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:35:14Z, size = 897335, hashes = { sha256 = "8900b3208e022570ae34328712bef6696de0804c122933414014bae791437ab2" } }, + { url = "https://files.pythonhosted.org/packages/cc/90/2e5f9da89d260de7d0417ead91a1bc897f19f0af05f4f9323313b76c47f2/regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:35:15Z, size = 789946, hashes = { sha256 = "c204e93bf32cd7a77151d44b05eb36f469d0898e3fba141c026a26b79d9914a0" } }, + { url = "https://files.pythonhosted.org/packages/2b/d5/1c712c7362f2563d389be66bae131c8bab121a3fabfa04b0b5bfc9e73c51/regex-2025.9.18-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-19T00:35:17Z, size = 780787, hashes = { sha256 = "3acc471d1dd7e5ff82e6cacb3b286750decd949ecd4ae258696d04f019817ef8" } }, + { url = "https://files.pythonhosted.org/packages/4f/92/c54cdb4aa41009632e69817a5aa452673507f07e341076735a2f6c46a37c/regex-2025.9.18-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:35:18Z, size = 773632, hashes = { sha256 = "6479d5555122433728760e5f29edb4c2b79655a8deb681a141beb5c8a025baea" } }, + { url = "https://files.pythonhosted.org/packages/db/99/75c996dc6a2231a8652d7ad0bfbeaf8a8c77612d335580f520f3ec40e30b/regex-2025.9.18-cp310-cp310-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:35:20Z, size = 844104, hashes = { sha256 = "431bd2a8726b000eb6f12429c9b438a24062a535d06783a93d2bcbad3698f8a8" } }, + { url = "https://files.pythonhosted.org/packages/1c/f7/25aba34cc130cb6844047dbfe9716c9b8f9629fee8b8bec331aa9241b97b/regex-2025.9.18-cp310-cp310-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:35:22Z, size = 834794, hashes = { sha256 = "0cc3521060162d02bd36927e20690129200e5ac9d2c6d32b70368870b122db25" } }, + { url = "https://files.pythonhosted.org/packages/51/eb/64e671beafa0ae29712268421597596d781704973551312b2425831d4037/regex-2025.9.18-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:35:23Z, size = 778535, hashes = { sha256 = "a021217b01be2d51632ce056d7a837d3fa37c543ede36e39d14063176a26ae29" } }, + { url = "https://files.pythonhosted.org/packages/26/33/c0ebc0b07bd0bf88f716cca240546b26235a07710ea58e271cfe390ae273/regex-2025.9.18-cp310-cp310-win32.whl", upload-time = 2025-09-19T00:35:25Z, size = 264115, hashes = { sha256 = "4a12a06c268a629cb67cc1d009b7bb0be43e289d00d5111f86a2efd3b1949444" } }, + { url = "https://files.pythonhosted.org/packages/59/39/aeb11a4ae68faaec2498512cadae09f2d8a91f1f65730fe62b9bffeea150/regex-2025.9.18-cp310-cp310-win_amd64.whl", upload-time = 2025-09-19T00:35:26Z, size = 276143, hashes = { sha256 = "47acd811589301298c49db2c56bde4f9308d6396da92daf99cba781fa74aa450" } }, + { url = "https://files.pythonhosted.org/packages/29/04/37f2d3fc334a1031fc2767c9d89cec13c2e72207c7e7f6feae8a47f4e149/regex-2025.9.18-cp310-cp310-win_arm64.whl", upload-time = 2025-09-19T00:35:28Z, size = 268473, hashes = { sha256 = "16bd2944e77522275e5ee36f867e19995bcaa533dcb516753a26726ac7285442" } }, + { url = "https://files.pythonhosted.org/packages/58/61/80eda662fc4eb32bfedc331f42390974c9e89c7eac1b79cd9eea4d7c458c/regex-2025.9.18-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2025-09-19T00:35:30Z, size = 484832, hashes = { sha256 = "51076980cd08cd13c88eb7365427ae27f0d94e7cebe9ceb2bb9ffdae8fc4d82a" } }, + { url = "https://files.pythonhosted.org/packages/a6/d9/33833d9abddf3f07ad48504ddb53fe3b22f353214bbb878a72eee1e3ddbf/regex-2025.9.18-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-19T00:35:31Z, size = 288994, hashes = { sha256 = "828446870bd7dee4e0cbeed767f07961aa07f0ea3129f38b3ccecebc9742e0b8" } }, + { url = "https://files.pythonhosted.org/packages/2a/b3/526ee96b0d70ea81980cbc20c3496fa582f775a52e001e2743cc33b2fa75/regex-2025.9.18-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:35:33Z, size = 286619, hashes = { sha256 = "c28821d5637866479ec4cc23b8c990f5bc6dd24e5e4384ba4a11d38a526e1414" } }, + { url = "https://files.pythonhosted.org/packages/65/4f/c2c096b02a351b33442aed5895cdd8bf87d372498d2100927c5a053d7ba3/regex-2025.9.18-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:35:35Z, size = 792454, hashes = { sha256 = "726177ade8e481db669e76bf99de0b278783be8acd11cef71165327abd1f170a" } }, + { url = "https://files.pythonhosted.org/packages/24/15/b562c9d6e47c403c4b5deb744f8b4bf6e40684cf866c7b077960a925bdff/regex-2025.9.18-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:35:36Z, size = 858723, hashes = { sha256 = "f5cca697da89b9f8ea44115ce3130f6c54c22f541943ac8e9900461edc2b8bd4" } }, + { url = "https://files.pythonhosted.org/packages/f2/01/dba305409849e85b8a1a681eac4c03ed327d8de37895ddf9dc137f59c140/regex-2025.9.18-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:35:38Z, size = 905899, hashes = { sha256 = "dfbde38f38004703c35666a1e1c088b778e35d55348da2b7b278914491698d6a" } }, + { url = "https://files.pythonhosted.org/packages/fe/d0/c51d1e6a80eab11ef96a4cbad17fc0310cf68994fb01a7283276b7e5bbd6/regex-2025.9.18-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:35:40Z, size = 798981, hashes = { sha256 = "f2f422214a03fab16bfa495cfec72bee4aaa5731843b771860a471282f1bf74f" } }, + { url = "https://files.pythonhosted.org/packages/c4/5e/72db90970887bbe02296612bd61b0fa31e6d88aa24f6a4853db3e96c575e/regex-2025.9.18-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:35:42Z, size = 781900, hashes = { sha256 = "a295916890f4df0902e4286bc7223ee7f9e925daa6dcdec4192364255b70561a" } }, + { url = "https://files.pythonhosted.org/packages/50/ff/596be45eea8e9bc31677fde243fa2904d00aad1b32c31bce26c3dbba0b9e/regex-2025.9.18-cp311-cp311-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:35:43Z, size = 852952, hashes = { sha256 = "5db95ff632dbabc8c38c4e82bf545ab78d902e81160e6e455598014f0abe66b9" } }, + { url = "https://files.pythonhosted.org/packages/e5/1b/2dfa348fa551e900ed3f5f63f74185b6a08e8a76bc62bc9c106f4f92668b/regex-2025.9.18-cp311-cp311-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:35:45Z, size = 844355, hashes = { sha256 = "fb967eb441b0f15ae610b7069bdb760b929f267efbf522e814bbbfffdf125ce2" } }, + { url = "https://files.pythonhosted.org/packages/f4/bf/aefb1def27fe33b8cbbb19c75c13aefccfbef1c6686f8e7f7095705969c7/regex-2025.9.18-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:35:46Z, size = 787254, hashes = { sha256 = "f04d2f20da4053d96c08f7fde6e1419b7ec9dbcee89c96e3d731fca77f411b95" } }, + { url = "https://files.pythonhosted.org/packages/e3/4e/8ef042e7cf0dbbb401e784e896acfc1b367b95dfbfc9ada94c2ed55a081f/regex-2025.9.18-cp311-cp311-win32.whl", upload-time = 2025-09-19T00:35:48Z, size = 264129, hashes = { sha256 = "895197241fccf18c0cea7550c80e75f185b8bd55b6924fcae269a1a92c614a07" } }, + { url = "https://files.pythonhosted.org/packages/b4/7d/c4fcabf80dcdd6821c0578ad9b451f8640b9110fb3dcb74793dd077069ff/regex-2025.9.18-cp311-cp311-win_amd64.whl", upload-time = 2025-09-19T00:36:00Z, size = 276160, hashes = { sha256 = "7e2b414deae99166e22c005e154a5513ac31493db178d8aec92b3269c9cce8c9" } }, + { url = "https://files.pythonhosted.org/packages/64/f8/0e13c8ae4d6df9d128afaba138342d532283d53a4c1e7a8c93d6756c8f4a/regex-2025.9.18-cp311-cp311-win_arm64.whl", upload-time = 2025-09-19T00:36:02Z, size = 268471, hashes = { sha256 = "fb137ec7c5c54f34a25ff9b31f6b7b0c2757be80176435bf367111e3f71d72df" } }, + { url = "https://files.pythonhosted.org/packages/b0/99/05859d87a66ae7098222d65748f11ef7f2dff51bfd7482a4e2256c90d72b/regex-2025.9.18-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2025-09-19T00:36:03Z, size = 486335, hashes = { sha256 = "436e1b31d7efd4dcd52091d076482031c611dde58bf9c46ca6d0a26e33053a7e" } }, + { url = "https://files.pythonhosted.org/packages/97/7e/d43d4e8b978890932cf7b0957fce58c5b08c66f32698f695b0c2c24a48bf/regex-2025.9.18-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-19T00:36:05Z, size = 289720, hashes = { sha256 = "c190af81e5576b9c5fdc708f781a52ff20f8b96386c6e2e0557a78402b029f4a" } }, + { url = "https://files.pythonhosted.org/packages/bb/3b/ff80886089eb5dcf7e0d2040d9aaed539e25a94300403814bb24cc775058/regex-2025.9.18-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:36:07Z, size = 287257, hashes = { sha256 = "e4121f1ce2b2b5eec4b397cc1b277686e577e658d8f5870b7eb2d726bd2300ab" } }, + { url = "https://files.pythonhosted.org/packages/ee/66/243edf49dd8720cba8d5245dd4d6adcb03a1defab7238598c0c97cf549b8/regex-2025.9.18-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:36:08Z, size = 797463, hashes = { sha256 = "300e25dbbf8299d87205e821a201057f2ef9aa3deb29caa01cd2cac669e508d5" } }, + { url = "https://files.pythonhosted.org/packages/df/71/c9d25a1142c70432e68bb03211d4a82299cd1c1fbc41db9409a394374ef5/regex-2025.9.18-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:36:10Z, size = 862670, hashes = { sha256 = "7b47fcf9f5316c0bdaf449e879407e1b9937a23c3b369135ca94ebc8d74b1742" } }, + { url = "https://files.pythonhosted.org/packages/f8/8f/329b1efc3a64375a294e3a92d43372bf1a351aa418e83c21f2f01cf6ec41/regex-2025.9.18-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:36:12Z, size = 910881, hashes = { sha256 = "57a161bd3acaa4b513220b49949b07e252165e6b6dc910ee7617a37ff4f5b425" } }, + { url = "https://files.pythonhosted.org/packages/35/9e/a91b50332a9750519320ed30ec378b74c996f6befe282cfa6bb6cea7e9fd/regex-2025.9.18-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:36:13Z, size = 802011, hashes = { sha256 = "4f130c3a7845ba42de42f380fff3c8aebe89a810747d91bcf56d40a069f15352" } }, + { url = "https://files.pythonhosted.org/packages/a4/1d/6be3b8d7856b6e0d7ee7f942f437d0a76e0d5622983abbb6d21e21ab9a17/regex-2025.9.18-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:36:15Z, size = 786668, hashes = { sha256 = "5f96fa342b6f54dcba928dd452e8d8cb9f0d63e711d1721cd765bb9f73bb048d" } }, + { url = "https://files.pythonhosted.org/packages/cb/ce/4a60e53df58bd157c5156a1736d3636f9910bdcc271d067b32b7fcd0c3a8/regex-2025.9.18-cp312-cp312-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:36:16Z, size = 856578, hashes = { sha256 = "0f0d676522d68c207828dcd01fb6f214f63f238c283d9f01d85fc664c7c85b56" } }, + { url = "https://files.pythonhosted.org/packages/86/e8/162c91bfe7217253afccde112868afb239f94703de6580fb235058d506a6/regex-2025.9.18-cp312-cp312-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:36:18Z, size = 849017, hashes = { sha256 = "40532bff8a1a0621e7903ae57fce88feb2e8a9a9116d341701302c9302aef06e" } }, + { url = "https://files.pythonhosted.org/packages/35/34/42b165bc45289646ea0959a1bc7531733e90b47c56a72067adfe6b3251f6/regex-2025.9.18-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:36:20Z, size = 788150, hashes = { sha256 = "039f11b618ce8d71a1c364fdee37da1012f5a3e79b1b2819a9f389cd82fd6282" } }, + { url = "https://files.pythonhosted.org/packages/79/5d/cdd13b1f3c53afa7191593a7ad2ee24092a5a46417725ffff7f64be8342d/regex-2025.9.18-cp312-cp312-win32.whl", upload-time = 2025-09-19T00:36:21Z, size = 264536, hashes = { sha256 = "e1dd06f981eb226edf87c55d523131ade7285137fbde837c34dc9d1bf309f459" } }, + { url = "https://files.pythonhosted.org/packages/e0/f5/4a7770c9a522e7d2dc1fa3ffc83ab2ab33b0b22b447e62cffef186805302/regex-2025.9.18-cp312-cp312-win_amd64.whl", upload-time = 2025-09-19T00:36:23Z, size = 275501, hashes = { sha256 = "3d86b5247bf25fa3715e385aa9ff272c307e0636ce0c9595f64568b41f0a9c77" } }, + { url = "https://files.pythonhosted.org/packages/df/05/9ce3e110e70d225ecbed455b966003a3afda5e58e8aec2964042363a18f4/regex-2025.9.18-cp312-cp312-win_arm64.whl", upload-time = 2025-09-19T00:36:25Z, size = 268601, hashes = { sha256 = "032720248cbeeae6444c269b78cb15664458b7bb9ed02401d3da59fe4d68c3a5" } }, + { url = "https://files.pythonhosted.org/packages/d2/c7/5c48206a60ce33711cf7dcaeaed10dd737733a3569dc7e1dce324dd48f30/regex-2025.9.18-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2025-09-19T00:36:26Z, size = 485955, hashes = { sha256 = "2a40f929cd907c7e8ac7566ac76225a77701a6221bca937bdb70d56cb61f57b2" } }, + { url = "https://files.pythonhosted.org/packages/e9/be/74fc6bb19a3c491ec1ace943e622b5a8539068771e8705e469b2da2306a7/regex-2025.9.18-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-19T00:36:28Z, size = 289583, hashes = { sha256 = "c90471671c2cdf914e58b6af62420ea9ecd06d1554d7474d50133ff26ae88feb" } }, + { url = "https://files.pythonhosted.org/packages/25/c4/9ceaa433cb5dc515765560f22a19578b95b92ff12526e5a259321c4fc1a0/regex-2025.9.18-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:36:30Z, size = 287000, hashes = { sha256 = "1a351aff9e07a2dabb5022ead6380cff17a4f10e4feb15f9100ee56c4d6d06af" } }, + { url = "https://files.pythonhosted.org/packages/7d/e6/68bc9393cb4dc68018456568c048ac035854b042bc7c33cb9b99b0680afa/regex-2025.9.18-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:36:31Z, size = 797535, hashes = { sha256 = "bc4b8e9d16e20ddfe16430c23468a8707ccad3365b06d4536142e71823f3ca29" } }, + { url = "https://files.pythonhosted.org/packages/6a/1c/ebae9032d34b78ecfe9bd4b5e6575b55351dc8513485bb92326613732b8c/regex-2025.9.18-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:36:33Z, size = 862603, hashes = { sha256 = "4b8cdbddf2db1c5e80338ba2daa3cfa3dec73a46fff2a7dda087c8efbf12d62f" } }, + { url = "https://files.pythonhosted.org/packages/3b/74/12332c54b3882557a4bcd2b99f8be581f5c6a43cf1660a85b460dd8ff468/regex-2025.9.18-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:36:34Z, size = 910829, hashes = { sha256 = "a276937d9d75085b2c91fb48244349c6954f05ee97bba0963ce24a9d915b8b68" } }, + { url = "https://files.pythonhosted.org/packages/86/70/ba42d5ed606ee275f2465bfc0e2208755b06cdabd0f4c7c4b614d51b57ab/regex-2025.9.18-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:36:36Z, size = 802059, hashes = { sha256 = "92a8e375ccdc1256401c90e9dc02b8642894443d549ff5e25e36d7cf8a80c783" } }, + { url = "https://files.pythonhosted.org/packages/da/c5/fcb017e56396a7f2f8357412638d7e2963440b131a3ca549be25774b3641/regex-2025.9.18-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:36:38Z, size = 786781, hashes = { sha256 = "0dc6893b1f502d73037cf807a321cdc9be29ef3d6219f7970f842475873712ac" } }, + { url = "https://files.pythonhosted.org/packages/c6/ee/21c4278b973f630adfb3bcb23d09d83625f3ab1ca6e40ebdffe69901c7a1/regex-2025.9.18-cp313-cp313-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:36:40Z, size = 856578, hashes = { sha256 = "a61e85bfc63d232ac14b015af1261f826260c8deb19401c0597dbb87a864361e" } }, + { url = "https://files.pythonhosted.org/packages/87/0b/de51550dc7274324435c8f1539373ac63019b0525ad720132866fff4a16a/regex-2025.9.18-cp313-cp313-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:36:41Z, size = 849119, hashes = { sha256 = "1ef86a9ebc53f379d921fb9a7e42b92059ad3ee800fcd9e0fe6181090e9f6c23" } }, + { url = "https://files.pythonhosted.org/packages/60/52/383d3044fc5154d9ffe4321696ee5b2ee4833a28c29b137c22c33f41885b/regex-2025.9.18-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:36:43Z, size = 788219, hashes = { sha256 = "d3bc882119764ba3a119fbf2bd4f1b47bc56c1da5d42df4ed54ae1e8e66fdf8f" } }, + { url = "https://files.pythonhosted.org/packages/20/bd/2614fc302671b7359972ea212f0e3a92df4414aaeacab054a8ce80a86073/regex-2025.9.18-cp313-cp313-win32.whl", upload-time = 2025-09-19T00:36:45Z, size = 264517, hashes = { sha256 = "3810a65675845c3bdfa58c3c7d88624356dd6ee2fc186628295e0969005f928d" } }, + { url = "https://files.pythonhosted.org/packages/07/0f/ab5c1581e6563a7bffdc1974fb2d25f05689b88e2d416525271f232b1946/regex-2025.9.18-cp313-cp313-win_amd64.whl", upload-time = 2025-09-19T00:36:46Z, size = 275481, hashes = { sha256 = "16eaf74b3c4180ede88f620f299e474913ab6924d5c4b89b3833bc2345d83b3d" } }, + { url = "https://files.pythonhosted.org/packages/49/22/ee47672bc7958f8c5667a587c2600a4fba8b6bab6e86bd6d3e2b5f7cac42/regex-2025.9.18-cp313-cp313-win_arm64.whl", upload-time = 2025-09-19T00:36:48Z, size = 268598, hashes = { sha256 = "4dc98ba7dd66bd1261927a9f49bd5ee2bcb3660f7962f1ec02617280fc00f5eb" } }, + { url = "https://files.pythonhosted.org/packages/e8/83/6887e16a187c6226cb85d8301e47d3b73ecc4505a3a13d8da2096b44fd76/regex-2025.9.18-cp313-cp313t-macosx_10_13_universal2.whl", upload-time = 2025-09-19T00:36:49Z, size = 489765, hashes = { sha256 = "fe5d50572bc885a0a799410a717c42b1a6b50e2f45872e2b40f4f288f9bce8a2" } }, + { url = "https://files.pythonhosted.org/packages/51/c5/e2f7325301ea2916ff301c8d963ba66b1b2c1b06694191df80a9c4fea5d0/regex-2025.9.18-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-19T00:36:51Z, size = 291228, hashes = { sha256 = "1b9d9a2d6cda6621551ca8cf7a06f103adf72831153f3c0d982386110870c4d3" } }, + { url = "https://files.pythonhosted.org/packages/91/60/7d229d2bc6961289e864a3a3cfebf7d0d250e2e65323a8952cbb7e22d824/regex-2025.9.18-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:36:53Z, size = 289270, hashes = { sha256 = "13202e4c4ac0ef9a317fff817674b293c8f7e8c68d3190377d8d8b749f566e12" } }, + { url = "https://files.pythonhosted.org/packages/3c/d7/b4f06868ee2958ff6430df89857fbf3d43014bbf35538b6ec96c2704e15d/regex-2025.9.18-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:36:54Z, size = 806326, hashes = { sha256 = "874ff523b0fecffb090f80ae53dc93538f8db954c8bb5505f05b7787ab3402a0" } }, + { url = "https://files.pythonhosted.org/packages/d6/e4/bca99034a8f1b9b62ccf337402a8e5b959dd5ba0e5e5b2ead70273df3277/regex-2025.9.18-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:36:56Z, size = 871556, hashes = { sha256 = "d13ab0490128f2bb45d596f754148cd750411afc97e813e4b3a61cf278a23bb6" } }, + { url = "https://files.pythonhosted.org/packages/6d/df/e06ffaf078a162f6dd6b101a5ea9b44696dca860a48136b3ae4a9caf25e2/regex-2025.9.18-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:36:57Z, size = 913817, hashes = { sha256 = "05440bc172bc4b4b37fb9667e796597419404dbba62e171e1f826d7d2a9ebcef" } }, + { url = "https://files.pythonhosted.org/packages/9e/05/25b05480b63292fd8e84800b1648e160ca778127b8d2367a0a258fa2e225/regex-2025.9.18-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:36:59Z, size = 811055, hashes = { sha256 = "5514b8e4031fdfaa3d27e92c75719cbe7f379e28cacd939807289bce76d0e35a" } }, + { url = "https://files.pythonhosted.org/packages/70/97/7bc7574655eb651ba3a916ed4b1be6798ae97af30104f655d8efd0cab24b/regex-2025.9.18-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:37:01Z, size = 794534, hashes = { sha256 = "65d3c38c39efce73e0d9dc019697b39903ba25b1ad45ebbd730d2cf32741f40d" } }, + { url = "https://files.pythonhosted.org/packages/b4/c2/d5da49166a52dda879855ecdba0117f073583db2b39bb47ce9a3378a8e9e/regex-2025.9.18-cp313-cp313t-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:37:03Z, size = 866684, hashes = { sha256 = "ae77e447ebc144d5a26d50055c6ddba1d6ad4a865a560ec7200b8b06bc529368" } }, + { url = "https://files.pythonhosted.org/packages/bd/2d/0a5c4e6ec417de56b89ff4418ecc72f7e3feca806824c75ad0bbdae0516b/regex-2025.9.18-cp313-cp313t-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:37:04Z, size = 853282, hashes = { sha256 = "e3ef8cf53dc8df49d7e28a356cf824e3623764e9833348b655cfed4524ab8a90" } }, + { url = "https://files.pythonhosted.org/packages/f4/8e/d656af63e31a86572ec829665d6fa06eae7e144771e0330650a8bb865635/regex-2025.9.18-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:37:06Z, size = 797830, hashes = { sha256 = "9feb29817df349c976da9a0debf775c5c33fc1c8ad7b9f025825da99374770b7" } }, + { url = "https://files.pythonhosted.org/packages/db/ce/06edc89df8f7b83ffd321b6071be4c54dc7332c0f77860edc40ce57d757b/regex-2025.9.18-cp313-cp313t-win32.whl", upload-time = 2025-09-19T00:37:08Z, size = 267281, hashes = { sha256 = "168be0d2f9b9d13076940b1ed774f98595b4e3c7fc54584bba81b3cc4181742e" } }, + { url = "https://files.pythonhosted.org/packages/83/9a/2b5d9c8b307a451fd17068719d971d3634ca29864b89ed5c18e499446d4a/regex-2025.9.18-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-19T00:37:10Z, size = 278724, hashes = { sha256 = "d59ecf3bb549e491c8104fea7313f3563c7b048e01287db0a90485734a70a730" } }, + { url = "https://files.pythonhosted.org/packages/3d/70/177d31e8089a278a764f8ec9a3faac8d14a312d622a47385d4b43905806f/regex-2025.9.18-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-19T00:37:13Z, size = 269771, hashes = { sha256 = "dbef80defe9fb21310948a2595420b36c6d641d9bea4c991175829b2cc4bc06a" } }, + { url = "https://files.pythonhosted.org/packages/44/b7/3b4663aa3b4af16819f2ab6a78c4111c7e9b066725d8107753c2257448a5/regex-2025.9.18-cp314-cp314-macosx_10_13_universal2.whl", upload-time = 2025-09-19T00:37:14Z, size = 486130, hashes = { sha256 = "c6db75b51acf277997f3adcd0ad89045d856190d13359f15ab5dda21581d9129" } }, + { url = "https://files.pythonhosted.org/packages/80/5b/4533f5d7ac9c6a02a4725fe8883de2aebc713e67e842c04cf02626afb747/regex-2025.9.18-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-19T00:37:16Z, size = 289539, hashes = { sha256 = "8f9698b6f6895d6db810e0bda5364f9ceb9e5b11328700a90cae573574f61eea" } }, + { url = "https://files.pythonhosted.org/packages/b8/8d/5ab6797c2750985f79e9995fad3254caa4520846580f266ae3b56d1cae58/regex-2025.9.18-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:37:18Z, size = 287233, hashes = { sha256 = "29cd86aa7cb13a37d0f0d7c21d8d949fe402ffa0ea697e635afedd97ab4b69f1" } }, + { url = "https://files.pythonhosted.org/packages/cb/1e/95afcb02ba8d3a64e6ffeb801718ce73471ad6440c55d993f65a4a5e7a92/regex-2025.9.18-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:37:19Z, size = 797876, hashes = { sha256 = "7c9f285a071ee55cd9583ba24dde006e53e17780bb309baa8e4289cd472bcc47" } }, + { url = "https://files.pythonhosted.org/packages/c8/fb/720b1f49cec1f3b5a9fea5b34cd22b88b5ebccc8c1b5de9cc6f65eed165a/regex-2025.9.18-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:37:21Z, size = 863385, hashes = { sha256 = "5adf266f730431e3be9021d3e5b8d5ee65e563fec2883ea8093944d21863b379" } }, + { url = "https://files.pythonhosted.org/packages/a9/ca/e0d07ecf701e1616f015a720dc13b84c582024cbfbb3fc5394ae204adbd7/regex-2025.9.18-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:37:23Z, size = 910220, hashes = { sha256 = "1137cabc0f38807de79e28d3f6e3e3f2cc8cfb26bead754d02e6d1de5f679203" } }, + { url = "https://files.pythonhosted.org/packages/b6/45/bba86413b910b708eca705a5af62163d5d396d5f647ed9485580c7025209/regex-2025.9.18-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:37:25Z, size = 801827, hashes = { sha256 = "7cc9e5525cada99699ca9223cce2d52e88c52a3d2a0e842bd53de5497c604164" } }, + { url = "https://files.pythonhosted.org/packages/b8/a6/740fbd9fcac31a1305a8eed30b44bf0f7f1e042342be0a4722c0365ecfca/regex-2025.9.18-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:37:27Z, size = 786843, hashes = { sha256 = "bbb9246568f72dce29bcd433517c2be22c7791784b223a810225af3b50d1aafb" } }, + { url = "https://files.pythonhosted.org/packages/80/a7/0579e8560682645906da640c9055506465d809cb0f5415d9976f417209a6/regex-2025.9.18-cp314-cp314-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:37:29Z, size = 857430, hashes = { sha256 = "6a52219a93dd3d92c675383efff6ae18c982e2d7651c792b1e6d121055808743" } }, + { url = "https://files.pythonhosted.org/packages/8d/9b/4dc96b6c17b38900cc9fee254fc9271d0dde044e82c78c0811b58754fde5/regex-2025.9.18-cp314-cp314-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:37:31Z, size = 848612, hashes = { sha256 = "ae9b3840c5bd456780e3ddf2f737ab55a79b790f6409182012718a35c6d43282" } }, + { url = "https://files.pythonhosted.org/packages/b3/6a/6f659f99bebb1775e5ac81a3fb837b85897c1a4ef5acffd0ff8ffe7e67fb/regex-2025.9.18-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:37:34Z, size = 787967, hashes = { sha256 = "d488c236ac497c46a5ac2005a952c1a0e22a07be9f10c3e735bc7d1209a34773" } }, + { url = "https://files.pythonhosted.org/packages/61/35/9e35665f097c07cf384a6b90a1ac11b0b1693084a0b7a675b06f760496c6/regex-2025.9.18-cp314-cp314-win32.whl", upload-time = 2025-09-19T00:37:35Z, size = 269847, hashes = { sha256 = "0c3506682ea19beefe627a38872d8da65cc01ffa25ed3f2e422dffa1474f0788" } }, + { url = "https://files.pythonhosted.org/packages/af/64/27594dbe0f1590b82de2821ebfe9a359b44dcb9b65524876cd12fabc447b/regex-2025.9.18-cp314-cp314-win_amd64.whl", upload-time = 2025-09-19T00:37:37Z, size = 278755, hashes = { sha256 = "57929d0f92bebb2d1a83af372cd0ffba2263f13f376e19b1e4fa32aec4efddc3" } }, + { url = "https://files.pythonhosted.org/packages/30/a3/0cd8d0d342886bd7d7f252d701b20ae1a3c72dc7f34ef4b2d17790280a09/regex-2025.9.18-cp314-cp314-win_arm64.whl", upload-time = 2025-09-19T00:37:39Z, size = 271873, hashes = { sha256 = "6a4b44df31d34fa51aa5c995d3aa3c999cec4d69b9bd414a8be51984d859f06d" } }, + { url = "https://files.pythonhosted.org/packages/99/cb/8a1ab05ecf404e18b54348e293d9b7a60ec2bd7aa59e637020c5eea852e8/regex-2025.9.18-cp314-cp314t-macosx_10_13_universal2.whl", upload-time = 2025-09-19T00:37:40Z, size = 489773, hashes = { sha256 = "b176326bcd544b5e9b17d6943f807697c0cb7351f6cfb45bf5637c95ff7e6306" } }, + { url = "https://files.pythonhosted.org/packages/93/3b/6543c9b7f7e734d2404fa2863d0d710c907bef99d4598760ed4563d634c3/regex-2025.9.18-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-09-19T00:37:42Z, size = 291221, hashes = { sha256 = "0ffd9e230b826b15b369391bec167baed57c7ce39efc35835448618860995946" } }, + { url = "https://files.pythonhosted.org/packages/cd/91/e9fdee6ad6bf708d98c5d17fded423dcb0661795a49cba1b4ffb8358377a/regex-2025.9.18-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:37:44Z, size = 289268, hashes = { sha256 = "ec46332c41add73f2b57e2f5b642f991f6b15e50e9f86285e08ffe3a512ac39f" } }, + { url = "https://files.pythonhosted.org/packages/94/a6/bc3e8a918abe4741dadeaeb6c508e3a4ea847ff36030d820d89858f96a6c/regex-2025.9.18-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:37:46Z, size = 806659, hashes = { sha256 = "b80fa342ed1ea095168a3f116637bd1030d39c9ff38dc04e54ef7c521e01fc95" } }, + { url = "https://files.pythonhosted.org/packages/2b/71/ea62dbeb55d9e6905c7b5a49f75615ea1373afcad95830047e4e310db979/regex-2025.9.18-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:37:48Z, size = 871701, hashes = { sha256 = "f4d97071c0ba40f0cf2a93ed76e660654c399a0a04ab7d85472239460f3da84b" } }, + { url = "https://files.pythonhosted.org/packages/6a/90/fbe9dedb7dad24a3a4399c0bae64bfa932ec8922a0a9acf7bc88db30b161/regex-2025.9.18-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:37:51Z, size = 913742, hashes = { sha256 = "0ac936537ad87cef9e0e66c5144484206c1354224ee811ab1519a32373e411f3" } }, + { url = "https://files.pythonhosted.org/packages/f0/1c/47e4a8c0e73d41eb9eb9fdeba3b1b810110a5139a2526e82fd29c2d9f867/regex-2025.9.18-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:37:52Z, size = 811117, hashes = { sha256 = "dec57f96d4def58c422d212d414efe28218d58537b5445cf0c33afb1b4768571" } }, + { url = "https://files.pythonhosted.org/packages/2a/da/435f29fddfd015111523671e36d30af3342e8136a889159b05c1d9110480/regex-2025.9.18-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:37:54Z, size = 794647, hashes = { sha256 = "48317233294648bf7cd068857f248e3a57222259a5304d32c7552e2284a1b2ad" } }, + { url = "https://files.pythonhosted.org/packages/23/66/df5e6dcca25c8bc57ce404eebc7342310a0d218db739d7882c9a2b5974a3/regex-2025.9.18-cp314-cp314t-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:37:56Z, size = 866747, hashes = { sha256 = "274687e62ea3cf54846a9b25fc48a04459de50af30a7bd0b61a9e38015983494" } }, + { url = "https://files.pythonhosted.org/packages/82/42/94392b39b531f2e469b2daa40acf454863733b674481fda17462a5ffadac/regex-2025.9.18-cp314-cp314t-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:37:58Z, size = 853434, hashes = { sha256 = "a78722c86a3e7e6aadf9579e3b0ad78d955f2d1f1a8ca4f67d7ca258e8719d4b" } }, + { url = "https://files.pythonhosted.org/packages/a8/f8/dcc64c7f7bbe58842a8f89622b50c58c3598fbbf4aad0a488d6df2c699f1/regex-2025.9.18-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:38:00Z, size = 798024, hashes = { sha256 = "06104cd203cdef3ade989a1c45b6215bf42f8b9dd705ecc220c173233f7cba41" } }, + { url = "https://files.pythonhosted.org/packages/20/8d/edf1c5d5aa98f99a692313db813ec487732946784f8f93145e0153d910e5/regex-2025.9.18-cp314-cp314t-win32.whl", upload-time = 2025-09-19T00:38:02Z, size = 273029, hashes = { sha256 = "2e1eddc06eeaffd249c0adb6fafc19e2118e6308c60df9db27919e96b5656096" } }, + { url = "https://files.pythonhosted.org/packages/a7/24/02d4e4f88466f17b145f7ea2b2c11af3a942db6222429c2c146accf16054/regex-2025.9.18-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-19T00:38:04Z, size = 282680, hashes = { sha256 = "8620d247fb8c0683ade51217b459cb4a1081c0405a3072235ba43a40d355c09a" } }, + { url = "https://files.pythonhosted.org/packages/1f/a3/c64894858aaaa454caa7cc47e2f225b04d3ed08ad649eacf58d45817fad2/regex-2025.9.18-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-19T00:38:05Z, size = 273034, hashes = { sha256 = "b7531a8ef61de2c647cdf68b3229b071e46ec326b3138b2180acb4275f470b01" } }, + { url = "https://files.pythonhosted.org/packages/ed/d2/5b0ded10467d6e96f78de5e6f195b7f9b57251f411b1090004597cffe5d9/regex-2025.9.18-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2025-09-19T00:38:07Z, size = 484847, hashes = { sha256 = "3dbcfcaa18e9480669030d07371713c10b4f1a41f791ffa5cb1a99f24e777f40" } }, + { url = "https://files.pythonhosted.org/packages/55/35/051da2c0ae6124e3f1aa1442ecc2bb4e2de930e95433bce1301a2e7ae255/regex-2025.9.18-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2025-09-19T00:38:09Z, size = 288995, hashes = { sha256 = "1e85f73ef7095f0380208269055ae20524bfde3f27c5384126ddccf20382a638" } }, + { url = "https://files.pythonhosted.org/packages/22/4b/4bfc51cad95263d25b6ed8c5253831b2536e8e279e6736d0a08c9f7ffe98/regex-2025.9.18-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-19T00:38:11Z, size = 286642, hashes = { sha256 = "9098e29b3ea4ffffeade423f6779665e2a4f8db64e699c0ed737ef0db6ba7b12" } }, + { url = "https://files.pythonhosted.org/packages/0e/67/d2f3e2483e09d1e9f7d93b4fe106b04933fba5e619bc901530d1c90d62da/regex-2025.9.18-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-19T00:38:12Z, size = 779896, hashes = { sha256 = "90b6b7a2d0f45b7ecaaee1aec6b362184d6596ba2092dd583ffba1b78dd0231c" } }, + { url = "https://files.pythonhosted.org/packages/14/5e/49a4f07ce6f5563de02b0e321220b9534f3fd3bae275311b785dd618aea5/regex-2025.9.18-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", upload-time = 2025-09-19T00:38:14Z, size = 848954, hashes = { sha256 = "c81b892af4a38286101502eae7aec69f7cd749a893d9987a92776954f3943408" } }, + { url = "https://files.pythonhosted.org/packages/00/8d/f5995ae51225c77ca9215d78ceb1dc30c52fa2b22c41dac977214e8b4bbd/regex-2025.9.18-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", upload-time = 2025-09-19T00:38:16Z, size = 896770, hashes = { sha256 = "3b524d010973f2e1929aeb635418d468d869a5f77b52084d9f74c272189c251d" } }, + { url = "https://files.pythonhosted.org/packages/6b/15/2a3a744d73a557337c7561db2114bab10b4e9941c626c03169ea62f42c8f/regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-09-19T00:38:18Z, size = 789484, hashes = { sha256 = "6b498437c026a3d5d0be0020023ff76d70ae4d77118e92f6f26c9d0423452446" } }, + { url = "https://files.pythonhosted.org/packages/d8/27/e425f3d17d32062a657b836d0c8a68f5e71a9e6295fa637159f265eaa609/regex-2025.9.18-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-19T00:38:19Z, size = 780150, hashes = { sha256 = "0716e4d6e58853d83f6563f3cf25c281ff46cf7107e5f11879e32cb0b59797d9" } }, + { url = "https://files.pythonhosted.org/packages/62/28/79dfae89b6fd7901b82611ac1a96ec25deceb7e918e9c5eb3f96cf5ad654/regex-2025.9.18-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T00:38:21Z, size = 773160, hashes = { sha256 = "065b6956749379d41db2625f880b637d4acc14c0a4de0d25d609a62850e96d36" } }, + { url = "https://files.pythonhosted.org/packages/0b/67/df83d6ae608f487448e9be7ac26211af2afa2b6e34465fde3e07d1f11290/regex-2025.9.18-cp39-cp39-musllinux_1_2_ppc64le.whl", upload-time = 2025-09-19T00:38:23Z, size = 843555, hashes = { sha256 = "d4a691494439287c08ddb9b5793da605ee80299dd31e95fa3f323fac3c33d9d4" } }, + { url = "https://files.pythonhosted.org/packages/32/67/c65f56f3edd3f213d3aa41e9b9b07cc2247721a23d34bcfb2947dc0f4685/regex-2025.9.18-cp39-cp39-musllinux_1_2_s390x.whl", upload-time = 2025-09-19T00:38:25Z, size = 834169, hashes = { sha256 = "ef8d10cc0989565bcbe45fb4439f044594d5c2b8919d3d229ea2c4238f1d55b0" } }, + { url = "https://files.pythonhosted.org/packages/95/90/7fca37435e3aa1a032c38fa1e171fdaf809c8dbf2717508e3f6a92c75446/regex-2025.9.18-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T00:38:28Z, size = 778024, hashes = { sha256 = "4baeb1b16735ac969a7eeecc216f1f8b7caf60431f38a2671ae601f716a32d25" } }, + { url = "https://files.pythonhosted.org/packages/8b/05/c2ee512cdf34d6be5ac5cf938a58c1b79a9d96cbad404bc4d70404212edb/regex-2025.9.18-cp39-cp39-win32.whl", upload-time = 2025-09-19T00:38:30Z, size = 264151, hashes = { sha256 = "8e5f41ad24a1e0b5dfcf4c4e5d9f5bd54c895feb5708dd0c1d0d35693b24d478" } }, + { url = "https://files.pythonhosted.org/packages/f8/2f/8414fb46181b6108484f04d670ece196db6734cc4c683f41125043fd3280/regex-2025.9.18-cp39-cp39-win_amd64.whl", upload-time = 2025-09-19T00:38:31Z, size = 276232, hashes = { sha256 = "50e8290707f2fb8e314ab3831e594da71e062f1d623b05266f8cfe4db4949afd" } }, + { url = "https://files.pythonhosted.org/packages/61/63/f40931d477e1ed4b53105d506758a58cfec1b052c12972054930ec743ee5/regex-2025.9.18-cp39-cp39-win_arm64.whl", upload-time = 2025-09-19T00:38:34Z, size = 268505, hashes = { sha256 = "039a9d7195fd88c943d7c777d4941e8ef736731947becce773c31a1009cb3c35" } }, +] + +[[packages]] name = "requests" -version = "2.32.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, -] - -[[package]] +version = "2.32.5" +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", upload-time = 2025-08-18T20:46:02Z, size = 134517, hashes = { sha256 = "dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", upload-time = 2025-08-18T20:46:00Z, size = 64738, hashes = { sha256 = "2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6" } }] + +[[packages]] +name = "ruff" +version = "0.14.0" +sdist = { url = "https://files.pythonhosted.org/packages/41/b9/9bd84453ed6dd04688de9b3f3a4146a1698e8faae2ceeccce4e14c67ae17/ruff-0.14.0.tar.gz", upload-time = 2025-10-07T18:21:55Z, size = 5452071, hashes = { sha256 = "62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/4e/79d463a5f80654e93fa653ebfb98e0becc3f0e7cf6219c9ddedf1e197072/ruff-0.14.0-py3-none-linux_armv6l.whl", upload-time = 2025-10-07T18:21:00Z, size = 12494532, hashes = { sha256 = "58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3" } }, + { url = "https://files.pythonhosted.org/packages/ee/40/e2392f445ed8e02aa6105d49db4bfff01957379064c30f4811c3bf38aece/ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", upload-time = 2025-10-07T18:21:04Z, size = 13160768, hashes = { sha256 = "838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8" } }, + { url = "https://files.pythonhosted.org/packages/75/da/2a656ea7c6b9bd14c7209918268dd40e1e6cea65f4bb9880eaaa43b055cd/ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", upload-time = 2025-10-07T18:21:07Z, size = 12363376, hashes = { sha256 = "703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8" } }, + { url = "https://files.pythonhosted.org/packages/42/e2/1ffef5a1875add82416ff388fcb7ea8b22a53be67a638487937aea81af27/ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-07T18:21:10Z, size = 12608055, hashes = { sha256 = "3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7" } }, + { url = "https://files.pythonhosted.org/packages/4a/32/986725199d7cee510d9f1dfdf95bf1efc5fa9dd714d0d85c1fb1f6be3bc3/ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-07T18:21:13Z, size = 12318544, hashes = { sha256 = "e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7" } }, + { url = "https://files.pythonhosted.org/packages/9a/ed/4969cefd53315164c94eaf4da7cfba1f267dc275b0abdd593d11c90829a3/ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-07T18:21:16Z, size = 14001280, hashes = { sha256 = "30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2" } }, + { url = "https://files.pythonhosted.org/packages/ab/ad/96c1fc9f8854c37681c9613d825925c7f24ca1acfc62a4eb3896b50bacd2/ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", upload-time = 2025-10-07T18:21:19Z, size = 15027286, hashes = { sha256 = "f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c" } }, + { url = "https://files.pythonhosted.org/packages/b3/00/1426978f97df4fe331074baf69615f579dc4e7c37bb4c6f57c2aad80c87f/ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-07T18:21:22Z, size = 14451506, hashes = { sha256 = "eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e" } }, + { url = "https://files.pythonhosted.org/packages/58/d5/9c1cea6e493c0cf0647674cca26b579ea9d2a213b74b5c195fbeb9678e15/ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-07T18:21:25Z, size = 13437384, hashes = { sha256 = "16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206" } }, + { url = "https://files.pythonhosted.org/packages/29/b4/4cd6a4331e999fc05d9d77729c95503f99eae3ba1160469f2b64866964e3/ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-07T18:21:28Z, size = 13447976, hashes = { sha256 = "eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e" } }, + { url = "https://files.pythonhosted.org/packages/3b/c0/ac42f546d07e4f49f62332576cb845d45c67cf5610d1851254e341d563b6/ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", upload-time = 2025-10-07T18:21:31Z, size = 13682850, hashes = { sha256 = "c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd" } }, + { url = "https://files.pythonhosted.org/packages/5f/c4/4b0c9bcadd45b4c29fe1af9c5d1dc0ca87b4021665dfbe1c4688d407aa20/ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", upload-time = 2025-10-07T18:21:35Z, size = 12449825, hashes = { sha256 = "7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d" } }, + { url = "https://files.pythonhosted.org/packages/4b/a8/e2e76288e6c16540fa820d148d83e55f15e994d852485f221b9524514730/ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", upload-time = 2025-10-07T18:21:38Z, size = 12272599, hashes = { sha256 = "4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f" } }, + { url = "https://files.pythonhosted.org/packages/18/14/e2815d8eff847391af632b22422b8207704222ff575dec8d044f9ab779b2/ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", upload-time = 2025-10-07T18:21:41Z, size = 13193828, hashes = { sha256 = "668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02" } }, + { url = "https://files.pythonhosted.org/packages/44/c6/61ccc2987cf0aecc588ff8f3212dea64840770e60d78f5606cd7dc34de32/ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", upload-time = 2025-10-07T18:21:44Z, size = 13628617, hashes = { sha256 = "a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296" } }, + { url = "https://files.pythonhosted.org/packages/73/e6/03b882225a1b0627e75339b420883dc3c90707a8917d2284abef7a58d317/ruff-0.14.0-py3-none-win32.whl", upload-time = 2025-10-07T18:21:46Z, size = 12367872, hashes = { sha256 = "7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543" } }, + { url = "https://files.pythonhosted.org/packages/41/77/56cf9cf01ea0bfcc662de72540812e5ba8e9563f33ef3d37ab2174892c47/ruff-0.14.0-py3-none-win_amd64.whl", upload-time = 2025-10-07T18:21:50Z, size = 13464628, hashes = { sha256 = "ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2" } }, + { url = "https://files.pythonhosted.org/packages/c6/2a/65880dfd0e13f7f13a775998f34703674a4554906167dce02daf7865b954/ruff-0.14.0-py3-none-win_arm64.whl", upload-time = 2025-10-07T18:21:53Z, size = 12565142, hashes = { sha256 = "f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730" } }, +] + +[[packages]] 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 } -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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, - { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, -] - -[[package]] +version = "0.6.2" +sdist = { url = "https://files.pythonhosted.org/packages/ac/cc/738f3011628920e027a11754d9cae9abec1aed00f7ae860abbf843755233/safetensors-0.6.2.tar.gz", upload-time = 2025-08-08T13:13:58Z, size = 197968, hashes = { sha256 = "43ff2aa0e6fa2dc3ea5524ac7ad93a9839256b8703761e76e2d0b2a3fa4f15d9" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/b1/3f5fd73c039fc87dba3ff8b5d528bfc5a32b597fea8e7a6a4800343a17c7/safetensors-0.6.2-cp38-abi3-macosx_10_12_x86_64.whl", upload-time = 2025-08-08T13:13:52Z, size = 454797, hashes = { sha256 = "9c85ede8ec58f120bad982ec47746981e210492a6db876882aa021446af8ffba" } }, + { url = "https://files.pythonhosted.org/packages/8c/c9/bb114c158540ee17907ec470d01980957fdaf87b4aa07914c24eba87b9c6/safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", upload-time = 2025-08-08T13:13:50Z, size = 432206, hashes = { sha256 = "d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b" } }, + { url = "https://files.pythonhosted.org/packages/d3/8e/f70c34e47df3110e8e0bb268d90db8d4be8958a54ab0336c9be4fe86dac8/safetensors-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-08-08T13:13:41Z, size = 473261, hashes = { sha256 = "1d2d2b3ce1e2509c68932ca03ab8f20570920cd9754b05063d4368ee52833ecd" } }, + { url = "https://files.pythonhosted.org/packages/2a/f5/be9c6a7c7ef773e1996dc214e73485286df1836dbd063e8085ee1976f9cb/safetensors-0.6.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-08-08T13:13:43Z, size = 485117, hashes = { sha256 = "93de35a18f46b0f5a6a1f9e26d91b442094f2df02e9fd7acf224cfec4238821a" } }, + { url = "https://files.pythonhosted.org/packages/c9/55/23f2d0a2c96ed8665bf17a30ab4ce5270413f4d74b6d87dd663258b9af31/safetensors-0.6.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-08-08T13:13:45Z, size = 616154, hashes = { sha256 = "89a89b505f335640f9120fac65ddeb83e40f1fd081cb8ed88b505bdccec8d0a1" } }, + { url = "https://files.pythonhosted.org/packages/98/c6/affb0bd9ce02aa46e7acddbe087912a04d953d7a4d74b708c91b5806ef3f/safetensors-0.6.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-08-08T13:13:46Z, size = 520713, hashes = { sha256 = "fc4d0d0b937e04bdf2ae6f70cd3ad51328635fe0e6214aa1fc811f3b576b3bda" } }, + { url = "https://files.pythonhosted.org/packages/fe/5d/5a514d7b88e310c8b146e2404e0dc161282e78634d9358975fd56dfd14be/safetensors-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-08-08T13:13:49Z, size = 485835, hashes = { sha256 = "8045db2c872db8f4cbe3faa0495932d89c38c899c603f21e9b6486951a5ecb8f" } }, + { url = "https://files.pythonhosted.org/packages/7a/7b/4fc3b2ba62c352b2071bea9cfbad330fadda70579f617506ae1a2f129cab/safetensors-0.6.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", upload-time = 2025-08-08T13:13:47Z, size = 521503, hashes = { sha256 = "81e67e8bab9878bb568cffbc5f5e655adb38d2418351dc0859ccac158f753e19" } }, + { url = "https://files.pythonhosted.org/packages/5a/50/0057e11fe1f3cead9254315a6c106a16dd4b1a19cd247f7cc6414f6b7866/safetensors-0.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", upload-time = 2025-08-08T13:13:53Z, size = 652256, hashes = { sha256 = "b0e4d029ab0a0e0e4fdf142b194514695b1d7d3735503ba700cf36d0fc7136ce" } }, + { url = "https://files.pythonhosted.org/packages/e9/29/473f789e4ac242593ac1656fbece6e1ecd860bb289e635e963667807afe3/safetensors-0.6.2-cp38-abi3-musllinux_1_2_armv7l.whl", upload-time = 2025-08-08T13:13:54Z, size = 747281, hashes = { sha256 = "fa48268185c52bfe8771e46325a1e21d317207bcabcb72e65c6e28e9ffeb29c7" } }, + { url = "https://files.pythonhosted.org/packages/68/52/f7324aad7f2df99e05525c84d352dc217e0fa637a4f603e9f2eedfbe2c67/safetensors-0.6.2-cp38-abi3-musllinux_1_2_i686.whl", upload-time = 2025-08-08T13:13:55Z, size = 692286, hashes = { sha256 = "d83c20c12c2d2f465997c51b7ecb00e407e5f94d7dec3ea0cc11d86f60d3fde5" } }, + { url = "https://files.pythonhosted.org/packages/ad/fe/cad1d9762868c7c5dc70c8620074df28ebb1a8e4c17d4c0cb031889c457e/safetensors-0.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", upload-time = 2025-08-08T13:13:57Z, size = 655957, hashes = { sha256 = "d944cea65fad0ead848b6ec2c37cc0b197194bec228f8020054742190e9312ac" } }, + { url = "https://files.pythonhosted.org/packages/59/a7/e2158e17bbe57d104f0abbd95dff60dda916cf277c9f9663b4bf9bad8b6e/safetensors-0.6.2-cp38-abi3-win32.whl", upload-time = 2025-08-08T13:14:01Z, size = 308926, hashes = { sha256 = "cab75ca7c064d3911411461151cb69380c9225798a20e712b102edda2542ddb1" } }, + { url = "https://files.pythonhosted.org/packages/2c/c3/c0be1135726618dc1e28d181b8c442403d8dbb9e273fd791de2d4384bcdd/safetensors-0.6.2-cp38-abi3-win_amd64.whl", upload-time = 2025-08-08T13:13:59Z, size = 320192, hashes = { sha256 = "c7b214870df923cbc1593c3faee16bec59ea462758699bd3fee399d00aac072c" } }, +] + +[[packages]] name = "scikit-learn" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/a5/4ae3b3a0755f7b35a280ac90b28817d1f380318973cff14075ab41ef50d9/scikit_learn-1.6.1.tar.gz", hash = "sha256:b4fc2525eca2c69a59260f583c56a7557c6ccdf8deafdba6e060f94c1c59738e", size = 7068312 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/59/8eb1872ca87009bdcdb7f3cdc679ad557b992c12f4b61f9250659e592c63/scikit_learn-1.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ffa1e9e25b3d93990e74a4be2c2fc61ee5af85811562f1288d5d055880c4322", size = 12010001 }, - { url = "https://files.pythonhosted.org/packages/9d/05/f2fc4effc5b32e525408524c982c468c29d22f828834f0625c5ef3d601be/scikit_learn-1.6.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:dc5cf3d68c5a20ad6d571584c0750ec641cc46aeef1c1507be51300e6003a7e1", size = 11096360 }, - { url = "https://files.pythonhosted.org/packages/c8/e4/4195d52cf4f113573fb8ebc44ed5a81bd511a92c0228889125fac2f4c3d1/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c06beb2e839ecc641366000ca84f3cf6fa9faa1777e29cf0c04be6e4d096a348", size = 12209004 }, - { url = "https://files.pythonhosted.org/packages/94/be/47e16cdd1e7fcf97d95b3cb08bde1abb13e627861af427a3651fcb80b517/scikit_learn-1.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8ca8cb270fee8f1f76fa9bfd5c3507d60c6438bbee5687f81042e2bb98e5a97", size = 13171776 }, - { url = "https://files.pythonhosted.org/packages/34/b0/ca92b90859070a1487827dbc672f998da95ce83edce1270fc23f96f1f61a/scikit_learn-1.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:7a1c43c8ec9fde528d664d947dc4c0789be4077a3647f232869f41d9bf50e0fb", size = 11071865 }, - { url = "https://files.pythonhosted.org/packages/12/ae/993b0fb24a356e71e9a894e42b8a9eec528d4c70217353a1cd7a48bc25d4/scikit_learn-1.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a17c1dea1d56dcda2fac315712f3651a1fea86565b64b48fa1bc090249cbf236", size = 11955804 }, - { url = "https://files.pythonhosted.org/packages/d6/54/32fa2ee591af44507eac86406fa6bba968d1eb22831494470d0a2e4a1eb1/scikit_learn-1.6.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a7aa5f9908f0f28f4edaa6963c0a6183f1911e63a69aa03782f0d924c830a35", size = 11100530 }, - { url = "https://files.pythonhosted.org/packages/3f/58/55856da1adec655bdce77b502e94a267bf40a8c0b89f8622837f89503b5a/scikit_learn-1.6.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0650e730afb87402baa88afbf31c07b84c98272622aaba002559b614600ca691", size = 12433852 }, - { url = "https://files.pythonhosted.org/packages/ff/4f/c83853af13901a574f8f13b645467285a48940f185b690936bb700a50863/scikit_learn-1.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:3f59fe08dc03ea158605170eb52b22a105f238a5d512c4470ddeca71feae8e5f", size = 11337256 }, -] - -[[package]] +version = "1.7.2" +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", upload-time = 2025-09-09T08:21:29Z, size = 7193136, hashes = { sha256 = "20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-09-09T08:20:19Z, size = 9336221, hashes = { sha256 = "6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f" } }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:20:22Z, size = 8653834, hashes = { sha256 = "36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c" } }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:20:24Z, size = 9660938, hashes = { sha256 = "7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8" } }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:20:26Z, size = 9477818, hashes = { sha256 = "4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18" } }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", upload-time = 2025-09-09T08:20:29Z, size = 8886969, hashes = { sha256 = "ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5" } }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-09-09T08:20:32Z, size = 9331967, hashes = { sha256 = "c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e" } }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:20:34Z, size = 8648645, hashes = { sha256 = "0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1" } }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:20:36Z, size = 9715424, hashes = { sha256 = "89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d" } }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:20:38Z, size = 9509234, hashes = { sha256 = "8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1" } }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", upload-time = 2025-09-09T08:20:41Z, size = 8894244, hashes = { sha256 = "9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1" } }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T08:20:43Z, size = 9259818, hashes = { sha256 = "8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96" } }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:20:45Z, size = 8636997, hashes = { sha256 = "acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476" } }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:20:47Z, size = 9478381, hashes = { sha256 = "e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b" } }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:20:50Z, size = 9300296, hashes = { sha256 = "b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44" } }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", upload-time = 2025-09-09T08:20:52Z, size = 8731256, hashes = { sha256 = "6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290" } }, + { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T08:20:54Z, size = 9212382, hashes = { sha256 = "0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7" } }, + { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:20:57Z, size = 8592042, hashes = { sha256 = "abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe" } }, + { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:20:59Z, size = 9434180, hashes = { sha256 = "502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f" } }, + { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:21:01Z, size = 9283660, hashes = { sha256 = "7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0" } }, + { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", upload-time = 2025-09-09T08:21:04Z, size = 8702057, hashes = { sha256 = "63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c" } }, + { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T08:21:06Z, size = 9558731, hashes = { sha256 = "9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8" } }, + { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:21:08Z, size = 9038852, hashes = { sha256 = "2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a" } }, + { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:21:11Z, size = 9527094, hashes = { sha256 = "98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c" } }, + { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:21:13Z, size = 9367436, hashes = { sha256 = "191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c" } }, + { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-09T08:21:15Z, size = 9275749, hashes = { sha256 = "57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973" } }, + { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-09-09T08:21:18Z, size = 9208906, hashes = { sha256 = "fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33" } }, + { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", upload-time = 2025-09-09T08:21:20Z, size = 8627836, hashes = { sha256 = "f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615" } }, + { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-09T08:21:22Z, size = 9426236, hashes = { sha256 = "9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106" } }, + { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", upload-time = 2025-09-09T08:21:24Z, size = 9312593, hashes = { sha256 = "96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61" } }, + { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", upload-time = 2025-09-09T08:21:26Z, size = 8820007, hashes = { sha256 = "bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8" } }, +] + +[[packages]] name = "scipy" -version = "1.15.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256 }, - { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540 }, - { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115 }, - { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884 }, - { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018 }, - { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716 }, - { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342 }, - { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869 }, - { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851 }, - { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011 }, - { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407 }, - { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030 }, - { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709 }, - { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045 }, - { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062 }, - { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132 }, - { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503 }, - { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097 }, -] - -[[package]] +version = "1.16.2" +sdist = { url = "https://files.pythonhosted.org/packages/4c/3b/546a6f0bfe791bbb7f8d591613454d15097e53f906308ec6f7c1ce588e8e/scipy-1.16.2.tar.gz", upload-time = 2025-09-11T17:48:08Z, size = 30580599, hashes = { sha256 = "af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/ef/37ed4b213d64b48422df92560af7300e10fe30b5d665dd79932baebee0c6/scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:39:20Z, size = 36619956, hashes = { sha256 = "6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92" } }, + { url = "https://files.pythonhosted.org/packages/85/ab/5c2eba89b9416961a982346a4d6a647d78c91ec96ab94ed522b3b6baf444/scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:39:29Z, size = 28931117, hashes = { sha256 = "c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e" } }, + { url = "https://files.pythonhosted.org/packages/80/d1/eed51ab64d227fe60229a2d57fb60ca5898cfa50ba27d4f573e9e5f0b430/scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:39:34Z, size = 20921997, hashes = { sha256 = "87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173" } }, + { url = "https://files.pythonhosted.org/packages/be/7c/33ea3e23bbadde96726edba6bf9111fb1969d14d9d477ffa202c67bec9da/scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:39:40Z, size = 23523374, hashes = { sha256 = "4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d" } }, + { url = "https://files.pythonhosted.org/packages/96/0b/7399dc96e1e3f9a05e258c98d716196a34f528eef2ec55aad651ed136d03/scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:39:49Z, size = 33583702, hashes = { sha256 = "e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2" } }, + { url = "https://files.pythonhosted.org/packages/1a/bc/a5c75095089b96ea72c1bd37a4497c24b581ec73db4ef58ebee142ad2d14/scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:39:57Z, size = 35883427, hashes = { sha256 = "f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9" } }, + { url = "https://files.pythonhosted.org/packages/ab/66/e25705ca3d2b87b97fe0a278a24b7f477b4023a926847935a1a71488a6a6/scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:40:06Z, size = 36212940, hashes = { sha256 = "ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3" } }, + { url = "https://files.pythonhosted.org/packages/d6/fd/0bb911585e12f3abdd603d721d83fc1c7492835e1401a0e6d498d7822b4b/scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:40:15Z, size = 38865092, hashes = { sha256 = "912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88" } }, + { url = "https://files.pythonhosted.org/packages/d6/73/c449a7d56ba6e6f874183759f8483cde21f900a8be117d67ffbb670c2958/scipy-1.16.2-cp311-cp311-win_amd64.whl", upload-time = 2025-09-11T17:40:24Z, size = 38687626, hashes = { sha256 = "91e9e8a37befa5a69e9cacbe0bcb79ae5afb4a0b130fd6db6ee6cc0d491695fa" } }, + { url = "https://files.pythonhosted.org/packages/68/72/02f37316adf95307f5d9e579023c6899f89ff3a051fa079dbd6faafc48e5/scipy-1.16.2-cp311-cp311-win_arm64.whl", upload-time = 2025-09-11T17:40:30Z, size = 25503506, hashes = { sha256 = "f3bf75a6dcecab62afde4d1f973f1692be013110cad5338007927db8da73249c" } }, + { url = "https://files.pythonhosted.org/packages/b7/8d/6396e00db1282279a4ddd507c5f5e11f606812b608ee58517ce8abbf883f/scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:40:39Z, size = 36646259, hashes = { sha256 = "89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d" } }, + { url = "https://files.pythonhosted.org/packages/3b/93/ea9edd7e193fceb8eef149804491890bde73fb169c896b61aa3e2d1e4e77/scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:40:46Z, size = 28888976, hashes = { sha256 = "ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371" } }, + { url = "https://files.pythonhosted.org/packages/91/4d/281fddc3d80fd738ba86fd3aed9202331180b01e2c78eaae0642f22f7e83/scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:40:52Z, size = 20879905, hashes = { sha256 = "fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0" } }, + { url = "https://files.pythonhosted.org/packages/69/40/b33b74c84606fd301b2915f0062e45733c6ff5708d121dd0deaa8871e2d0/scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:40:59Z, size = 23553066, hashes = { sha256 = "033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232" } }, + { url = "https://files.pythonhosted.org/packages/55/a7/22c739e2f21a42cc8f16bc76b47cff4ed54fbe0962832c589591c2abec34/scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:41:06Z, size = 33336407, hashes = { sha256 = "ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1" } }, + { url = "https://files.pythonhosted.org/packages/53/11/a0160990b82999b45874dc60c0c183d3a3a969a563fffc476d5a9995c407/scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:41:15Z, size = 35673281, hashes = { sha256 = "f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f" } }, + { url = "https://files.pythonhosted.org/packages/96/53/7ef48a4cfcf243c3d0f1643f5887c81f29fdf76911c4e49331828e19fc0a/scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:41:23Z, size = 36004222, hashes = { sha256 = "5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef" } }, + { url = "https://files.pythonhosted.org/packages/49/7f/71a69e0afd460049d41c65c630c919c537815277dfea214031005f474d78/scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:41:31Z, size = 38664586, hashes = { sha256 = "03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1" } }, + { url = "https://files.pythonhosted.org/packages/34/95/20e02ca66fb495a95fba0642fd48e0c390d0ece9b9b14c6e931a60a12dea/scipy-1.16.2-cp312-cp312-win_amd64.whl", upload-time = 2025-09-11T17:41:36Z, size = 38550641, hashes = { sha256 = "0ce54e07bbb394b417457409a64fd015be623f36e330ac49306433ffe04bc97e" } }, + { url = "https://files.pythonhosted.org/packages/92/ad/13646b9beb0a95528ca46d52b7babafbe115017814a611f2065ee4e61d20/scipy-1.16.2-cp312-cp312-win_arm64.whl", upload-time = 2025-09-11T17:41:41Z, size = 25456070, hashes = { sha256 = "2a8ffaa4ac0df81a0b94577b18ee079f13fecdb924df3328fc44a7dc5ac46851" } }, + { url = "https://files.pythonhosted.org/packages/c1/27/c5b52f1ee81727a9fc457f5ac1e9bf3d6eab311805ea615c83c27ba06400/scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:41:47Z, size = 36604856, hashes = { sha256 = "84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70" } }, + { url = "https://files.pythonhosted.org/packages/32/a9/15c20d08e950b540184caa8ced675ba1128accb0e09c653780ba023a4110/scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:41:52Z, size = 28864626, hashes = { sha256 = "5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9" } }, + { url = "https://files.pythonhosted.org/packages/4c/fc/ea36098df653cca26062a627c1a94b0de659e97127c8491e18713ca0e3b9/scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:41:57Z, size = 20855689, hashes = { sha256 = "e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5" } }, + { url = "https://files.pythonhosted.org/packages/dc/6f/d0b53be55727f3e6d7c72687ec18ea6d0047cf95f1f77488b99a2bafaee1/scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:42:02Z, size = 23512151, hashes = { sha256 = "024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925" } }, + { url = "https://files.pythonhosted.org/packages/11/85/bf7dab56e5c4b1d3d8eef92ca8ede788418ad38a7dc3ff50262f00808760/scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:42:07Z, size = 33329824, hashes = { sha256 = "7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9" } }, + { url = "https://files.pythonhosted.org/packages/da/6a/1a927b14ddc7714111ea51f4e568203b2bb6ed59bdd036d62127c1a360c8/scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:42:13Z, size = 35681881, hashes = { sha256 = "c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7" } }, + { url = "https://files.pythonhosted.org/packages/c1/5f/331148ea5780b4fcc7007a4a6a6ee0a0c1507a796365cc642d4d226e1c3a/scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:42:18Z, size = 36006219, hashes = { sha256 = "af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb" } }, + { url = "https://files.pythonhosted.org/packages/46/3a/e991aa9d2aec723b4a8dcfbfc8365edec5d5e5f9f133888067f1cbb7dfc1/scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:42:25Z, size = 38682147, hashes = { sha256 = "9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e" } }, + { url = "https://files.pythonhosted.org/packages/a1/57/0f38e396ad19e41b4c5db66130167eef8ee620a49bc7d0512e3bb67e0cab/scipy-1.16.2-cp313-cp313-win_amd64.whl", upload-time = 2025-09-11T17:43:25Z, size = 38520766, hashes = { sha256 = "fda714cf45ba43c9d3bae8f2585c777f64e3f89a2e073b668b32ede412d8f52c" } }, + { url = "https://files.pythonhosted.org/packages/1b/a5/85d3e867b6822d331e26c862a91375bb7746a0b458db5effa093d34cdb89/scipy-1.16.2-cp313-cp313-win_arm64.whl", upload-time = 2025-09-11T17:43:30Z, size = 25451169, hashes = { sha256 = "2f5350da923ccfd0b00e07c3e5cfb316c1c0d6c1d864c07a72d092e9f20db104" } }, + { url = "https://files.pythonhosted.org/packages/09/d9/60679189bcebda55992d1a45498de6d080dcaf21ce0c8f24f888117e0c2d/scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:42:30Z, size = 37012682, hashes = { sha256 = "53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1" } }, + { url = "https://files.pythonhosted.org/packages/83/be/a99d13ee4d3b7887a96f8c71361b9659ba4ef34da0338f14891e102a127f/scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:42:35Z, size = 29389926, hashes = { sha256 = "9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a" } }, + { url = "https://files.pythonhosted.org/packages/bf/0a/130164a4881cec6ca8c00faf3b57926f28ed429cd6001a673f83c7c2a579/scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:42:40Z, size = 21381152, hashes = { sha256 = "7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f" } }, + { url = "https://files.pythonhosted.org/packages/47/a6/503ffb0310ae77fba874e10cddfc4a1280bdcca1d13c3751b8c3c2996cf8/scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:42:44Z, size = 23914410, hashes = { sha256 = "6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4" } }, + { url = "https://files.pythonhosted.org/packages/fa/c7/1147774bcea50d00c02600aadaa919facbd8537997a62496270133536ed6/scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:42:49Z, size = 33481880, hashes = { sha256 = "ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21" } }, + { url = "https://files.pythonhosted.org/packages/6a/74/99d5415e4c3e46b2586f30cdbecb95e101c7192628a484a40dd0d163811a/scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:42:54Z, size = 35791425, hashes = { sha256 = "654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7" } }, + { url = "https://files.pythonhosted.org/packages/1b/ee/a6559de7c1cc710e938c0355d9d4fbcd732dac4d0d131959d1f3b63eb29c/scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:43:00Z, size = 36178622, hashes = { sha256 = "63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8" } }, + { url = "https://files.pythonhosted.org/packages/4e/7b/f127a5795d5ba8ece4e0dce7d4a9fb7cb9e4f4757137757d7a69ab7d4f1a/scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:43:06Z, size = 38783985, hashes = { sha256 = "fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472" } }, + { url = "https://files.pythonhosted.org/packages/3e/9f/bc81c1d1e033951eb5912cd3750cc005943afa3e65a725d2443a3b3c4347/scipy-1.16.2-cp313-cp313t-win_amd64.whl", upload-time = 2025-09-11T17:43:14Z, size = 38631367, hashes = { sha256 = "116296e89fba96f76353a8579820c2512f6e55835d3fad7780fece04367de351" } }, + { url = "https://files.pythonhosted.org/packages/d6/5e/2cc7555fd81d01814271412a1d59a289d25f8b63208a0a16c21069d55d3e/scipy-1.16.2-cp313-cp313t-win_arm64.whl", upload-time = 2025-09-11T17:43:19Z, size = 25787992, hashes = { sha256 = "98e22834650be81d42982360382b43b17f7ba95e0e6993e2a4f5b9ad9283a94d" } }, + { url = "https://files.pythonhosted.org/packages/8b/ac/ad8951250516db71619f0bd3b2eb2448db04b720a003dd98619b78b692c0/scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:43:35Z, size = 36595109, hashes = { sha256 = "567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77" } }, + { url = "https://files.pythonhosted.org/packages/ff/f6/5779049ed119c5b503b0f3dc6d6f3f68eefc3a9190d4ad4c276f854f051b/scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:43:40Z, size = 28859110, hashes = { sha256 = "17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70" } }, + { url = "https://files.pythonhosted.org/packages/82/09/9986e410ae38bf0a0c737ff8189ac81a93b8e42349aac009891c054403d7/scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:43:44Z, size = 20850110, hashes = { sha256 = "0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88" } }, + { url = "https://files.pythonhosted.org/packages/0d/ad/485cdef2d9215e2a7df6d61b81d2ac073dfacf6ae24b9ae87274c4e936ae/scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:43:49Z, size = 23497014, hashes = { sha256 = "d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f" } }, + { url = "https://files.pythonhosted.org/packages/a7/74/f6a852e5d581122b8f0f831f1d1e32fb8987776ed3658e95c377d308ed86/scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:43:54Z, size = 33401155, hashes = { sha256 = "9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb" } }, + { url = "https://files.pythonhosted.org/packages/d9/f5/61d243bbc7c6e5e4e13dde9887e84a5cbe9e0f75fd09843044af1590844e/scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:44:00Z, size = 35691174, hashes = { sha256 = "d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7" } }, + { url = "https://files.pythonhosted.org/packages/03/99/59933956331f8cc57e406cdb7a483906c74706b156998f322913e789c7e1/scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:44:05Z, size = 36070752, hashes = { sha256 = "70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548" } }, + { url = "https://files.pythonhosted.org/packages/c6/7d/00f825cfb47ee19ef74ecf01244b43e95eae74e7e0ff796026ea7cd98456/scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:44:11Z, size = 38701010, hashes = { sha256 = "5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936" } }, + { url = "https://files.pythonhosted.org/packages/e4/9f/b62587029980378304ba5a8563d376c96f40b1e133daacee76efdcae32de/scipy-1.16.2-cp314-cp314-win_amd64.whl", upload-time = 2025-09-11T17:45:09Z, size = 39360061, hashes = { sha256 = "f5a85d7b2b708025af08f060a496dd261055b617d776fc05a1a1cc69e09fe9ff" } }, + { url = "https://files.pythonhosted.org/packages/82/04/7a2f1609921352c7fbee0815811b5050582f67f19983096c4769867ca45f/scipy-1.16.2-cp314-cp314-win_arm64.whl", upload-time = 2025-09-11T17:45:14Z, size = 26126914, hashes = { sha256 = "2cc73a33305b4b24556957d5857d6253ce1e2dcd67fa0ff46d87d1670b3e1e1d" } }, + { url = "https://files.pythonhosted.org/packages/51/b9/60929ce350c16b221928725d2d1d7f86cf96b8bc07415547057d1196dc92/scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", upload-time = 2025-09-11T17:44:16Z, size = 37013193, hashes = { sha256 = "9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8" } }, + { url = "https://files.pythonhosted.org/packages/2a/41/ed80e67782d4bc5fc85a966bc356c601afddd175856ba7c7bb6d9490607e/scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", upload-time = 2025-09-11T17:44:21Z, size = 29390172, hashes = { sha256 = "7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4" } }, + { url = "https://files.pythonhosted.org/packages/c4/a3/2f673ace4090452696ccded5f5f8efffb353b8f3628f823a110e0170b605/scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", upload-time = 2025-09-11T17:44:25Z, size = 21381326, hashes = { sha256 = "8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831" } }, + { url = "https://files.pythonhosted.org/packages/42/bf/59df61c5d51395066c35836b78136accf506197617c8662e60ea209881e1/scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", upload-time = 2025-09-11T17:44:30Z, size = 23915036, hashes = { sha256 = "5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3" } }, + { url = "https://files.pythonhosted.org/packages/91/c3/edc7b300dc16847ad3672f1a6f3f7c5d13522b21b84b81c265f4f2760d4a/scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", upload-time = 2025-09-11T17:44:35Z, size = 33484341, hashes = { sha256 = "efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac" } }, + { url = "https://files.pythonhosted.org/packages/26/c7/24d1524e72f06ff141e8d04b833c20db3021020563272ccb1b83860082a9/scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", upload-time = 2025-09-11T17:44:41Z, size = 35790840, hashes = { sha256 = "7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374" } }, + { url = "https://files.pythonhosted.org/packages/aa/b7/5aaad984eeedd56858dc33d75efa59e8ce798d918e1033ef62d2708f2c3d/scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-09-11T17:44:47Z, size = 36174716, hashes = { sha256 = "bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6" } }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e276a237acb09824822b0ada11b028ed4067fdc367a946730979feacb870/scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-09-11T17:44:53Z, size = 38790088, hashes = { sha256 = "b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c" } }, + { url = "https://files.pythonhosted.org/packages/c6/b4/5c18a766e8353015439f3780f5fc473f36f9762edc1a2e45da3ff5a31b21/scipy-1.16.2-cp314-cp314t-win_amd64.whl", upload-time = 2025-09-11T17:44:58Z, size = 39457455, hashes = { sha256 = "26284797e38b8a75e14ea6631d29bda11e76ceaa6ddb6fdebbfe4c4d90faf2f9" } }, + { url = "https://files.pythonhosted.org/packages/97/30/2f9a5243008f76dfc5dee9a53dfb939d9b31e16ce4bd4f2e628bfc5d89d2/scipy-1.16.2-cp314-cp314t-win_arm64.whl", upload-time = 2025-09-11T17:45:03Z, size = 26448374, hashes = { sha256 = "d2a4472c231328d4de38d5f1f68fdd6d28a615138f842580a8a321b5845cf779" } }, +] + +[[packages]] name = "sentence-transformers" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "huggingface-hub" }, - { name = "pillow" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "torch" }, - { name = "tqdm" }, - { name = "transformers" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/73/84/b30d1b29ff58cfdff423e36a50efd622c8e31d7039b1a0d5e72066620da1/sentence_transformers-4.1.0.tar.gz", hash = "sha256:f125ffd1c727533e0eca5d4567de72f84728de8f7482834de442fd90c2c3d50b", size = 272420 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/2d/1151b371f28caae565ad384fdc38198f1165571870217aedda230b9d7497/sentence_transformers-4.1.0-py3-none-any.whl", hash = "sha256:382a7f6be1244a100ce40495fb7523dbe8d71b3c10b299f81e6b735092b3b8ca", size = 345695 }, -] +version = "5.1.1" +sdist = { url = "https://files.pythonhosted.org/packages/21/47/7d61a19ba7e6b5f36f0ffff5bbf032a1c1913612caac611e12383069eda0/sentence_transformers-5.1.1.tar.gz", upload-time = 2025-09-22T11:28:27Z, size = 374434, hashes = { sha256 = "8af3f844b2ecf9a6c2dfeafc2c02938a87f61202b54329d70dfd7dfd7d17a84e" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/48/21/4670d03ab8587b0ab6f7d5fa02a95c3dd6b1f39d0e40e508870201f3d76c/sentence_transformers-5.1.1-py3-none-any.whl", upload-time = 2025-09-22T11:28:26Z, size = 486574, hashes = { sha256 = "5ed544629eafe89ca668a8910ebff96cf0a9c5254ec14b05c66c086226c892fd" } }] -[[package]] +[[packages]] name = "setuptools" -version = "80.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/0cc40fe41fd2adb80a2f388987f4f8db3c866c69e33e0b4c8b093fdf700e/setuptools-80.4.0.tar.gz", hash = "sha256:5a78f61820bc088c8e4add52932ae6b8cf423da2aff268c23f813cfbb13b4006", size = 1315008 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/93/dba5ed08c2e31ec7cdc2ce75705a484ef0be1a2fecac8a58272489349de8/setuptools-80.4.0-py3-none-any.whl", hash = "sha256:6cdc8cb9a7d590b237dbe4493614a9b75d0559b888047c1f67d49ba50fc3edb2", size = 1200812 }, -] +version = "80.9.0" +marker = "(python_full_version >= '3.12' and platform_machine != 'x86_64') or (python_full_version >= '3.12' and sys_platform != 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", upload-time = 2025-05-27T00:56:51Z, size = 1319958, hashes = { sha256 = "f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", upload-time = 2025-05-27T00:56:49Z, size = 1201486, hashes = { sha256 = "062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922" } }] -[[package]] +[[packages]] name = "six" version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", upload-time = 2024-12-04T17:35:28Z, size = 34031, hashes = { sha256 = "ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", upload-time = 2024-12-04T17:35:26Z, size = 11050, hashes = { sha256 = "4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", upload-time = 2024-02-25T23:20:04Z, size = 20372, hashes = { sha256 = "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", upload-time = 2024-02-25T23:20:01Z, size = 10235, hashes = { sha256 = "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" } }] -[[package]] +[[packages]] +name = "starlette" +version = "0.48.0" +sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", upload-time = 2025-09-13T08:41:05Z, size = 2652949, hashes = { sha256 = "7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", upload-time = 2025-09-13T08:41:03Z, size = 73736, hashes = { sha256 = "0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659" } }] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", upload-time = 2025-04-27T18:05:01Z, size = 7793921, hashes = { sha256 = "d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", upload-time = 2025-04-27T18:04:59Z, size = 6299353, hashes = { sha256 = "e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5" } }] -[[package]] +[[packages]] name = "threadpoolctl" version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", upload-time = 2025-03-13T13:49:23Z, size = 21274, hashes = { sha256 = "8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", upload-time = 2025-03-13T13:49:21Z, size = 18638, hashes = { sha256 = "43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb" } }] -[[package]] +[[packages]] name = "tiktoken" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919 }, - { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877 }, - { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649 }, - { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465 }, - { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669 }, -] - -[[package]] +version = "0.12.0" +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", upload-time = 2025-10-06T20:22:45Z, size = 37806, hashes = { sha256 = "b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/b3/2cb7c17b6c4cf8ca983204255d3f1d95eda7213e247e6947a0ee2c747a2c/tiktoken-0.12.0-cp310-cp310-macosx_10_12_x86_64.whl", upload-time = 2025-10-06T20:21:34Z, size = 1051991, hashes = { sha256 = "3de02f5a491cfd179aec916eddb70331814bd6bf764075d39e21d5862e533970" } }, + { url = "https://files.pythonhosted.org/packages/27/0f/df139f1df5f6167194ee5ab24634582ba9a1b62c6b996472b0277ec80f66/tiktoken-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:21:35Z, size = 995798, hashes = { sha256 = "b6cfb6d9b7b54d20af21a912bfe63a2727d9cfa8fbda642fd8322c70340aad16" } }, + { url = "https://files.pythonhosted.org/packages/ef/5d/26a691f28ab220d5edc09b9b787399b130f24327ef824de15e5d85ef21aa/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:21:36Z, size = 1129865, hashes = { sha256 = "cde24cdb1b8a08368f709124f15b36ab5524aac5fa830cc3fdce9c03d4fb8030" } }, + { url = "https://files.pythonhosted.org/packages/b2/94/443fab3d4e5ebecac895712abd3849b8da93b7b7dec61c7db5c9c7ebe40c/tiktoken-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:21:37Z, size = 1152856, hashes = { sha256 = "6de0da39f605992649b9cfa6f84071e3f9ef2cec458d08c5feb1b6f0ff62e134" } }, + { url = "https://files.pythonhosted.org/packages/54/35/388f941251b2521c70dd4c5958e598ea6d2c88e28445d2fb8189eecc1dfc/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:21:39Z, size = 1195308, hashes = { sha256 = "6faa0534e0eefbcafaccb75927a4a380463a2eaa7e26000f0173b920e98b720a" } }, + { url = "https://files.pythonhosted.org/packages/f8/00/c6681c7f833dd410576183715a530437a9873fa910265817081f65f9105f/tiktoken-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:21:41Z, size = 1255697, hashes = { sha256 = "82991e04fc860afb933efb63957affc7ad54f83e2216fe7d319007dab1ba5892" } }, + { url = "https://files.pythonhosted.org/packages/5f/d2/82e795a6a9bafa034bf26a58e68fe9a89eeaaa610d51dbeb22106ba04f0a/tiktoken-0.12.0-cp310-cp310-win_amd64.whl", upload-time = 2025-10-06T20:21:43Z, size = 879375, hashes = { sha256 = "6fb2995b487c2e31acf0a9e17647e3b242235a20832642bb7a9d1a181c0c1bb1" } }, + { url = "https://files.pythonhosted.org/packages/de/46/21ea696b21f1d6d1efec8639c204bdf20fde8bafb351e1355c72c5d7de52/tiktoken-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", upload-time = 2025-10-06T20:21:44Z, size = 1051565, hashes = { sha256 = "6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb" } }, + { url = "https://files.pythonhosted.org/packages/c9/d9/35c5d2d9e22bb2a5f74ba48266fb56c63d76ae6f66e02feb628671c0283e/tiktoken-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:21:45Z, size = 995284, hashes = { sha256 = "c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa" } }, + { url = "https://files.pythonhosted.org/packages/01/84/961106c37b8e49b9fdcf33fe007bb3a8fdcc380c528b20cc7fbba80578b8/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:21:47Z, size = 1129201, hashes = { sha256 = "f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc" } }, + { url = "https://files.pythonhosted.org/packages/6a/d0/3d9275198e067f8b65076a68894bb52fd253875f3644f0a321a720277b8a/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:21:48Z, size = 1152444, hashes = { sha256 = "47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded" } }, + { url = "https://files.pythonhosted.org/packages/78/db/a58e09687c1698a7c592e1038e01c206569b86a0377828d51635561f8ebf/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:21:49Z, size = 1195080, hashes = { sha256 = "508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd" } }, + { url = "https://files.pythonhosted.org/packages/9e/1b/a9e4d2bf91d515c0f74afc526fd773a812232dd6cda33ebea7f531202325/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:21:50Z, size = 1255240, hashes = { sha256 = "a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967" } }, + { url = "https://files.pythonhosted.org/packages/9d/15/963819345f1b1fb0809070a79e9dd96938d4ca41297367d471733e79c76c/tiktoken-0.12.0-cp311-cp311-win_amd64.whl", upload-time = 2025-10-06T20:21:51Z, size = 879422, hashes = { sha256 = "3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def" } }, + { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-10-06T20:21:52Z, size = 1050728, hashes = { sha256 = "b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8" } }, + { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:21:53Z, size = 994049, hashes = { sha256 = "2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b" } }, + { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:21:54Z, size = 1129008, hashes = { sha256 = "65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37" } }, + { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:21:56Z, size = 1152665, hashes = { sha256 = "edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad" } }, + { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:21:57Z, size = 1194230, hashes = { sha256 = "35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5" } }, + { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:21:58Z, size = 1254688, hashes = { sha256 = "83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3" } }, + { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", upload-time = 2025-10-06T20:21:59Z, size = 878694, hashes = { sha256 = "ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd" } }, + { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-10-06T20:22:00Z, size = 1050802, hashes = { sha256 = "775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3" } }, + { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:22:02Z, size = 993995, hashes = { sha256 = "a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160" } }, + { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:22:03Z, size = 1128948, hashes = { sha256 = "01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa" } }, + { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:22:05Z, size = 1151986, hashes = { sha256 = "4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be" } }, + { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:22:06Z, size = 1194222, hashes = { sha256 = "981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a" } }, + { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:22:07Z, size = 1255097, hashes = { sha256 = "9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3" } }, + { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", upload-time = 2025-10-06T20:22:08Z, size = 879117, hashes = { sha256 = "b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697" } }, + { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", upload-time = 2025-10-06T20:22:10Z, size = 1050309, hashes = { sha256 = "d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16" } }, + { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:22:12Z, size = 993712, hashes = { sha256 = "b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a" } }, + { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:22:13Z, size = 1128725, hashes = { sha256 = "fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27" } }, + { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:22:14Z, size = 1151875, hashes = { sha256 = "06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb" } }, + { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:22:15Z, size = 1194451, hashes = { sha256 = "04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e" } }, + { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:22:16Z, size = 1253794, hashes = { sha256 = "0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25" } }, + { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", upload-time = 2025-10-06T20:22:18Z, size = 878777, hashes = { sha256 = "dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f" } }, + { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", upload-time = 2025-10-06T20:22:19Z, size = 1050188, hashes = { sha256 = "a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646" } }, + { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:22:20Z, size = 993978, hashes = { sha256 = "da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88" } }, + { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:22:22Z, size = 1129271, hashes = { sha256 = "285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff" } }, + { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:22:23Z, size = 1151216, hashes = { sha256 = "d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830" } }, + { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:22:24Z, size = 1194860, hashes = { sha256 = "604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b" } }, + { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:22:25Z, size = 1254567, hashes = { sha256 = "8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b" } }, + { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", upload-time = 2025-10-06T20:22:26Z, size = 921067, hashes = { sha256 = "399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3" } }, + { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", upload-time = 2025-10-06T20:22:27Z, size = 1050473, hashes = { sha256 = "c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365" } }, + { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:22:28Z, size = 993855, hashes = { sha256 = "cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e" } }, + { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:22:29Z, size = 1129022, hashes = { sha256 = "dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63" } }, + { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:22:30Z, size = 1150736, hashes = { sha256 = "584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0" } }, + { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:22:32Z, size = 1194908, hashes = { sha256 = "54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a" } }, + { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:22:33Z, size = 1253706, hashes = { sha256 = "5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0" } }, + { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", upload-time = 2025-10-06T20:22:34Z, size = 920667, hashes = { sha256 = "f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71" } }, + { url = "https://files.pythonhosted.org/packages/c7/d1/7507bfb9c2ceef52ae3ae813013215c185648e21127538aae66dedd3af9c/tiktoken-0.12.0-cp39-cp39-macosx_10_12_x86_64.whl", upload-time = 2025-10-06T20:22:35Z, size = 1053407, hashes = { sha256 = "d51d75a5bffbf26f86554d28e78bfb921eae998edc2675650fd04c7e1f0cdc1e" } }, + { url = "https://files.pythonhosted.org/packages/ee/4a/8ea1da602ac39dee4356b4cd6040a2325507482c36043044b6f581597b4f/tiktoken-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-10-06T20:22:37Z, size = 997150, hashes = { sha256 = "09eb4eae62ae7e4c62364d9ec3a57c62eea707ac9a2b2c5d6bd05de6724ea179" } }, + { url = "https://files.pythonhosted.org/packages/2c/1a/62d1d36b167eccd441aff2f0091551ca834295541b949d161021aa658167/tiktoken-0.12.0-cp39-cp39-manylinux_2_28_aarch64.whl", upload-time = 2025-10-06T20:22:39Z, size = 1131575, hashes = { sha256 = "df37684ace87d10895acb44b7f447d4700349b12197a526da0d4a4149fde074c" } }, + { url = "https://files.pythonhosted.org/packages/f7/16/544207d63c8c50edd2321228f21d236e4e49d235128bb7e3e0f69eed0807/tiktoken-0.12.0-cp39-cp39-manylinux_2_28_x86_64.whl", upload-time = 2025-10-06T20:22:40Z, size = 1154920, hashes = { sha256 = "4c9614597ac94bb294544345ad8cf30dac2129c05e2db8dc53e082f355857af7" } }, + { url = "https://files.pythonhosted.org/packages/99/4c/0a3504157c81364fc0c64cada54efef0567961357e786706ea63bc8946e1/tiktoken-0.12.0-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-10-06T20:22:41Z, size = 1196766, hashes = { sha256 = "20cf97135c9a50de0b157879c3c4accbb29116bcf001283d26e073ff3b345946" } }, + { url = "https://files.pythonhosted.org/packages/d4/46/8e6a258ae65447c75770fe5ea8968acab369e8c9f537f727c91f83772325/tiktoken-0.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-10-06T20:22:42Z, size = 1258278, hashes = { sha256 = "15d875454bbaa3728be39880ddd11a5a2a9e548c29418b41e8fd8a767172b5ec" } }, + { url = "https://files.pythonhosted.org/packages/35/43/3b95de4f5e76f3cafc70dac9b1b9cfe759ff3bfd494ac91a280e93772e90/tiktoken-0.12.0-cp39-cp39-win_amd64.whl", upload-time = 2025-10-06T20:22:44Z, size = 881888, hashes = { sha256 = "2cff3688ba3c639ebe816f8d58ffbbb0aa7433e23e08ab1cade5d175fc973fb3" } }, +] + +[[packages]] 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 } -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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { 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 }, - { url = "https://files.pythonhosted.org/packages/e8/5b/a5d98c89f747455e8b7a9504910c865d5e51da55e825a7ae641fb5ff0a58/tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3", size = 2239506 }, - { url = "https://files.pythonhosted.org/packages/e6/b6/072a8e053ae600dcc2ac0da81a23548e3b523301a442a6ca900e92ac35be/tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382", size = 2435481 }, -] - -[[package]] +version = "0.22.1" +sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", upload-time = 2025-09-19T09:49:23Z, size = 363123, hashes = { sha256 = "61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", upload-time = 2025-09-19T09:49:11Z, size = 3069318, hashes = { sha256 = "59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73" } }, + { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", upload-time = 2025-09-19T09:49:09Z, size = 2926478, hashes = { sha256 = "8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc" } }, + { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-09-19T09:48:56Z, size = 3256994, hashes = { sha256 = "19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a" } }, + { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-09-19T09:48:59Z, size = 3153141, hashes = { sha256 = "38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7" } }, + { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-09-19T09:49:05Z, size = 3508049, hashes = { sha256 = "d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21" } }, + { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-09-19T09:49:01Z, size = 3710730, hashes = { sha256 = "e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214" } }, + { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-09-19T09:49:03Z, size = 3412560, hashes = { sha256 = "afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f" } }, + { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-09-19T09:49:07Z, size = 3250221, hashes = { sha256 = "e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4" } }, + { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", upload-time = 2025-09-19T09:49:14Z, size = 9345569, hashes = { sha256 = "ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879" } }, + { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", upload-time = 2025-09-19T09:49:16Z, size = 9271599, hashes = { sha256 = "331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446" } }, + { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", upload-time = 2025-09-19T09:49:19Z, size = 9533862, hashes = { sha256 = "607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a" } }, + { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", upload-time = 2025-09-19T09:49:21Z, size = 9681250, hashes = { sha256 = "a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390" } }, + { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", upload-time = 2025-09-19T09:49:27Z, size = 2472003, hashes = { sha256 = "b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82" } }, + { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", upload-time = 2025-09-19T09:49:24Z, size = 2674684, hashes = { sha256 = "65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138" } }, +] + +[[packages]] +name = "toml-sort" +version = "0.24.3" +sdist = { url = "https://files.pythonhosted.org/packages/15/65/d62fb175769355a65c2c5e53a155cf28f2162af4e4067f11d8af89c0a4a3/toml_sort-0.24.3.tar.gz", upload-time = 2025-09-10T02:43:54Z, size = 17773, hashes = { sha256 = "74c3cab5c1ecaf76c8239addc15342de548fb39598694f1298550d0fe2673a81" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/0e/1c/f5a9a4f918ff0e1578fdc3f607a7ec704c5da1b13ddc290fd0aefa97ff2e/toml_sort-0.24.3-py3-none-any.whl", upload-time = 2025-09-10T02:43:53Z, size = 16538, hashes = { sha256 = "d70642f54827b4c17a6cde788dcb45d598c1eb9833544a2fcec5eb7b0b744ed4" } }] + +[[packages]] +name = "tomlkit" +version = "0.13.3" +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", upload-time = 2025-06-05T07:13:44Z, size = 185207, hashes = { sha256 = "430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", upload-time = 2025-06-05T07:13:43Z, size = 38901, hashes = { sha256 = "c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0" } }] + +[[packages]] name = "torch" -version = "2.7.0" -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/14/24/720ea9a66c29151b315ea6ba6f404650834af57a26b2a04af23ec246b2d5/torch-2.7.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:868ccdc11798535b5727509480cd1d86d74220cfdc42842c4617338c1109a205", size = 99015553 }, - { url = "https://files.pythonhosted.org/packages/4b/27/285a8cf12bd7cd71f9f211a968516b07dcffed3ef0be585c6e823675ab91/torch-2.7.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b52347118116cf3dff2ab5a3c3dd97c719eb924ac658ca2a7335652076df708", size = 865046389 }, - { url = "https://files.pythonhosted.org/packages/74/c8/2ab2b6eadc45554af8768ae99668c5a8a8552e2012c7238ded7e9e4395e1/torch-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:434cf3b378340efc87c758f250e884f34460624c0523fe5c9b518d205c91dd1b", size = 212490304 }, - { url = "https://files.pythonhosted.org/packages/28/fd/74ba6fde80e2b9eef4237fe668ffae302c76f0e4221759949a632ca13afa/torch-2.7.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:edad98dddd82220465b106506bb91ee5ce32bd075cddbcf2b443dfaa2cbd83bf", size = 68856166 }, - { url = "https://files.pythonhosted.org/packages/cb/b4/8df3f9fe6bdf59e56a0e538592c308d18638eb5f5dc4b08d02abb173c9f0/torch-2.7.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2a885fc25afefb6e6eb18a7d1e8bfa01cc153e92271d980a49243b250d5ab6d9", size = 99091348 }, - { url = "https://files.pythonhosted.org/packages/9d/f5/0bd30e9da04c3036614aa1b935a9f7e505a9e4f1f731b15e165faf8a4c74/torch-2.7.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:176300ff5bc11a5f5b0784e40bde9e10a35c4ae9609beed96b4aeb46a27f5fae", size = 865104023 }, - { url = "https://files.pythonhosted.org/packages/d1/b7/2235d0c3012c596df1c8d39a3f4afc1ee1b6e318d469eda4c8bb68566448/torch-2.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d0ca446a93f474985d81dc866fcc8dccefb9460a29a456f79d99c29a78a66993", size = 212750916 }, - { url = "https://files.pythonhosted.org/packages/90/48/7e6477cf40d48cc0a61fa0d41ee9582b9a316b12772fcac17bc1a40178e7/torch-2.7.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:27f5007bdf45f7bb7af7f11d1828d5c2487e030690afb3d89a651fd7036a390e", size = 68575074 }, -] - -[[package]] +version = "2.8.0" +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/28/110f7274254f1b8476c561dada127173f994afa2b1ffc044efb773c15650/torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:53:15Z, size = 102052793, hashes = { sha256 = "0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905" } }, + { url = "https://files.pythonhosted.org/packages/70/1c/58da560016f81c339ae14ab16c98153d51c941544ae568da3cb5b1ceb572/torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:54:18Z, size = 888025420, hashes = { sha256 = "89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011" } }, + { url = "https://files.pythonhosted.org/packages/70/87/f69752d0dd4ba8218c390f0438130c166fa264a33b7025adb5014b92192c/torch-2.8.0-cp310-cp310-win_amd64.whl", upload-time = 2025-08-06T14:53:31Z, size = 241363614, hashes = { sha256 = "e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46" } }, + { url = "https://files.pythonhosted.org/packages/ef/d6/e6d4c57e61c2b2175d3aafbfb779926a2cfd7c32eeda7c543925dceec923/torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", upload-time = 2025-08-06T14:53:10Z, size = 73611154, hashes = { sha256 = "a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760" } }, + { url = "https://files.pythonhosted.org/packages/8f/c4/3e7a3887eba14e815e614db70b3b529112d1513d9dae6f4d43e373360b7f/torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:53:20Z, size = 102073391, hashes = { sha256 = "220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710" } }, + { url = "https://files.pythonhosted.org/packages/5a/63/4fdc45a0304536e75a5e1b1bbfb1b56dd0e2743c48ee83ca729f7ce44162/torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:55:05Z, size = 888063640, hashes = { sha256 = "c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b" } }, + { url = "https://files.pythonhosted.org/packages/84/57/2f64161769610cf6b1c5ed782bd8a780e18a3c9d48931319f2887fa9d0b1/torch-2.8.0-cp311-cp311-win_amd64.whl", upload-time = 2025-08-06T14:53:38Z, size = 241366752, hashes = { sha256 = "8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa" } }, + { url = "https://files.pythonhosted.org/packages/a4/5e/05a5c46085d9b97e928f3f037081d3d2b87fb4b4195030fc099aaec5effc/torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", upload-time = 2025-08-06T14:53:25Z, size = 73621174, hashes = { sha256 = "5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916" } }, + { url = "https://files.pythonhosted.org/packages/49/0c/2fd4df0d83a495bb5e54dca4474c4ec5f9c62db185421563deeb5dabf609/torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:53:52Z, size = 101906089, hashes = { sha256 = "e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705" } }, + { url = "https://files.pythonhosted.org/packages/99/a8/6acf48d48838fb8fe480597d98a0668c2beb02ee4755cc136de92a0a956f/torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:56:44Z, size = 887913624, hashes = { sha256 = "b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c" } }, + { url = "https://files.pythonhosted.org/packages/af/8a/5c87f08e3abd825c7dfecef5a0f1d9aa5df5dd0e3fd1fa2f490a8e512402/torch-2.8.0-cp312-cp312-win_amd64.whl", upload-time = 2025-08-06T14:53:46Z, size = 241326087, hashes = { sha256 = "2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e" } }, + { url = "https://files.pythonhosted.org/packages/be/66/5c9a321b325aaecb92d4d1855421e3a055abd77903b7dab6575ca07796db/torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", upload-time = 2025-08-06T14:53:57Z, size = 73630478, hashes = { sha256 = "619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0" } }, + { url = "https://files.pythonhosted.org/packages/10/4e/469ced5a0603245d6a19a556e9053300033f9c5baccf43a3d25ba73e189e/torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:54:01Z, size = 101936856, hashes = { sha256 = "2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128" } }, + { url = "https://files.pythonhosted.org/packages/16/82/3948e54c01b2109238357c6f86242e6ecbf0c63a1af46906772902f82057/torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:55:50Z, size = 887922844, hashes = { sha256 = "65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b" } }, + { url = "https://files.pythonhosted.org/packages/e3/54/941ea0a860f2717d86a811adf0c2cd01b3983bdd460d0803053c4e0b8649/torch-2.8.0-cp313-cp313-win_amd64.whl", upload-time = 2025-08-06T14:54:45Z, size = 241330968, hashes = { sha256 = "659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16" } }, + { url = "https://files.pythonhosted.org/packages/de/69/8b7b13bba430f5e21d77708b616f767683629fc4f8037564a177d20f90ed/torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", upload-time = 2025-08-06T14:54:34Z, size = 73915128, hashes = { sha256 = "1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767" } }, + { url = "https://files.pythonhosted.org/packages/15/0e/8a800e093b7f7430dbaefa80075aee9158ec22e4c4fc3c1a66e4fb96cb4f/torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:54:39Z, size = 102020139, hashes = { sha256 = "83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def" } }, + { url = "https://files.pythonhosted.org/packages/4a/15/5e488ca0bc6162c86a33b58642bc577c84ded17c7b72d97e49b5833e2d73/torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:56:18Z, size = 887990692, hashes = { sha256 = "8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a" } }, + { url = "https://files.pythonhosted.org/packages/b4/a8/6a04e4b54472fc5dba7ca2341ab219e529f3c07b6941059fbf18dccac31f/torch-2.8.0-cp313-cp313t-win_amd64.whl", upload-time = 2025-08-06T14:55:22Z, size = 241603453, hashes = { sha256 = "a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca" } }, + { url = "https://files.pythonhosted.org/packages/04/6e/650bb7f28f771af0cb791b02348db8b7f5f64f40f6829ee82aa6ce99aabe/torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", upload-time = 2025-08-06T14:55:28Z, size = 73632395, hashes = { sha256 = "7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211" } }, + { url = "https://files.pythonhosted.org/packages/5b/b0/a321f27270049baa12f5c3fb0d6ceea005634787e3af9a8d75dce8306b0a/torch-2.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", upload-time = 2025-08-06T14:55:33Z, size = 102059214, hashes = { sha256 = "da6afa31c13b669d4ba49d8a2169f0db2c3ec6bec4af898aa714f401d4c38904" } }, + { url = "https://files.pythonhosted.org/packages/fd/dd/1630cb51b10d3d2e97db95e5a84c32def81fc26b005bce6fc880b0e6db81/torch-2.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", upload-time = 2025-08-06T14:57:28Z, size = 888024302, hashes = { sha256 = "06fcee8000e5c62a9f3e52a688b9c5abb7c6228d0e56e3452983416025c41381" } }, + { url = "https://files.pythonhosted.org/packages/b9/dc/1f1f621afe15e3c496e1e8f94f8903f75f87e7d642d5a985e92210cc208d/torch-2.8.0-cp39-cp39-win_amd64.whl", upload-time = 2025-08-06T14:57:05Z, size = 241249338, hashes = { sha256 = "5128fe752a355d9308e56af1ad28b15266fe2da5948660fad44de9e3a9e36e8c" } }, + { url = "https://files.pythonhosted.org/packages/ae/95/ae26263aceb3d57b821179f827d0e321373ed49423e603dd5906ab14a730/torch-2.8.0-cp39-none-macosx_11_0_arm64.whl", upload-time = 2025-08-06T14:57:11Z, size = 73610795, hashes = { sha256 = "e9f071f5b52a9f6970dc8a919694b27a91ae9dc08898b2b988abbef5eddfd1ae" } }, +] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", upload-time = 2024-11-24T20:12:22Z, size = 169737, hashes = { sha256 = "f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", upload-time = 2024-11-24T20:12:19Z, size = 78540, hashes = { sha256 = "26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2" } }] -[[package]] +[[packages]] name = "transformers" -version = "4.51.3" -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/f1/11/7414d5bc07690002ce4d7553602107bf969af85144bbd02830f9fb471236/transformers-4.51.3.tar.gz", hash = "sha256:e292fcab3990c6defe6328f0f7d2004283ca81a7a07b2de9a46d67fd81ea1409", size = 8941266 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b6/5257d04ae327b44db31f15cce39e6020cc986333c715660b1315a9724d82/transformers-4.51.3-py3-none-any.whl", hash = "sha256:fd3279633ceb2b777013234bbf0b4f5c2d23c4626b05497691f00cfda55e8a83", size = 10383940 }, -] +version = "4.57.1" +sdist = { url = "https://files.pythonhosted.org/packages/d6/68/a39307bcc4116a30b2106f2e689130a48de8bd8a1e635b5e1030e46fcd9e/transformers-4.57.1.tar.gz", upload-time = 2025-10-14T15:39:26Z, size = 10142511, hashes = { sha256 = "f06c837959196c75039809636cd964b959f6604b75b8eeec6fdfc0440b89cc55" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/71/d3/c16c3b3cf7655a67db1144da94b021c200ac1303f82428f2beef6c2e72bb/transformers-4.57.1-py3-none-any.whl", upload-time = 2025-10-14T15:39:23Z, size = 11990925, hashes = { sha256 = "b10d05da8fa67dc41644dbbf9bc45a44cb86ae33da6f9295f5fbf5b7890bd267" } }] -[[package]] +[[packages]] name = "triton" -version = "3.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] +version = "3.4.0" +marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/74/4bf2702b65e93accaa20397b74da46fb7a0356452c1bb94dbabaf0582930/triton-3.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47bc87ad66fa4ef17968299acacecaab71ce40a238890acc6ad197c3abe2b8f1", size = 156516468 }, - { url = "https://files.pythonhosted.org/packages/0a/93/f28a696fa750b9b608baa236f8225dd3290e5aff27433b06143adc025961/triton-3.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce4700fc14032af1e049005ae94ba908e71cd6c2df682239aed08e49bc71b742", size = 156580729 }, + { url = "https://files.pythonhosted.org/packages/62/ee/0ee5f64a87eeda19bbad9bc54ae5ca5b98186ed00055281fd40fb4beb10e/triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:21Z, size = 155430069, hashes = { sha256 = "7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128" } }, + { url = "https://files.pythonhosted.org/packages/7d/39/43325b3b651d50187e591eefa22e236b2981afcebaefd4f2fc0ea99df191/triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:29Z, size = 155531138, hashes = { sha256 = "7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467" } }, + { url = "https://files.pythonhosted.org/packages/d0/66/b1eb52839f563623d185f0927eb3530ee4d5ffe9d377cdaf5346b306689e/triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:37Z, size = 155560068, hashes = { sha256 = "31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04" } }, + { url = "https://files.pythonhosted.org/packages/30/7b/0a685684ed5322d2af0bddefed7906674f67974aa88b0fae6e82e3b766f6/triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:44Z, size = 155569223, hashes = { sha256 = "00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb" } }, + { url = "https://files.pythonhosted.org/packages/20/63/8cb444ad5cdb25d999b7d647abac25af0ee37d292afc009940c05b82dda0/triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:51Z, size = 155659780, hashes = { sha256 = "7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d" } }, + { url = "https://files.pythonhosted.org/packages/12/34/1251beb5a3cb93f3950ebe68732752014646003ef6eb11eb5f1a37ca78cd/triton-3.4.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", upload-time = 2025-07-30T19:58:57Z, size = 155430799, hashes = { sha256 = "98e5c1442eaeabae2e2452ae765801bd53cd4ce873cab0d1bdd59a32ab2d9397" } }, ] -[[package]] +[[packages]] +name = "typeguard" +version = "4.4.4" +sdist = { url = "https://files.pythonhosted.org/packages/c7/68/71c1a15b5f65f40e91b65da23b8224dad41349894535a97f63a52e462196/typeguard-4.4.4.tar.gz", upload-time = 2025-06-18T09:56:07Z, size = 75203, hashes = { sha256 = "3a7fd2dffb705d4d0efaed4306a704c89b9dee850b688f060a8b1615a79e5f74" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl", upload-time = 2025-06-18T09:56:05Z, size = 34874, hashes = { sha256 = "b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e" } }] + +[[packages]] name = "typing-extensions" -version = "4.13.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, -] +version = "4.15.0" +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", upload-time = 2025-08-25T13:49:26Z, size = 109391, hashes = { sha256 = "0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", upload-time = 2025-08-25T13:49:24Z, size = 44614, hashes = { sha256 = "f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" } }] -[[package]] +[[packages]] name = "typing-inspection" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, -] +version = "0.4.2" +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", upload-time = 2025-10-01T02:14:41Z, size = 75949, hashes = { sha256 = "ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", upload-time = 2025-10-01T02:14:40Z, size = 14611, hashes = { sha256 = "4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7" } }] -[[package]] +[[packages]] name = "tzdata" version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, -] +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", upload-time = 2025-03-23T13:54:43Z, size = 196380, hashes = { sha256 = "b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", upload-time = 2025-03-23T13:54:41Z, size = 347839, hashes = { sha256 = "1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8" } }] -[[package]] +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, -] - -[[package]] +version = "2.5.0" +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", upload-time = 2025-06-18T14:07:41Z, size = 393185, hashes = { sha256 = "3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", upload-time = 2025-06-18T14:07:40Z, size = 129795, hashes = { sha256 = "e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" } }] + +[[packages]] +name = "uvicorn" +version = "0.37.0" +sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", upload-time = 2025-09-23T13:33:47Z, size = 80367, hashes = { sha256 = "4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", upload-time = 2025-09-23T13:33:45Z, size = 67976, hashes = { sha256 = "913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c" } }] + +[[packages]] +name = "uvloop" +version = "0.21.0" +marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" +sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", upload-time = 2024-10-14T23:38:35Z, size = 2492741, hashes = { sha256 = "3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/76/44a55515e8c9505aa1420aebacf4dd82552e5e15691654894e90d0bd051a/uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2024-10-14T23:37:20Z, size = 1442019, hashes = { sha256 = "ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f" } }, + { url = "https://files.pythonhosted.org/packages/35/5a/62d5800358a78cc25c8a6c72ef8b10851bdb8cca22e14d9c74167b7f86da/uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2024-10-14T23:37:22Z, size = 801898, hashes = { sha256 = "196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d" } }, + { url = "https://files.pythonhosted.org/packages/f3/96/63695e0ebd7da6c741ccd4489b5947394435e198a1382349c17b1146bb97/uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:37:25Z, size = 3827735, hashes = { sha256 = "f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26" } }, + { url = "https://files.pythonhosted.org/packages/61/e0/f0f8ec84979068ffae132c58c79af1de9cceeb664076beea86d941af1a30/uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:37:27Z, size = 3825126, hashes = { sha256 = "87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb" } }, + { url = "https://files.pythonhosted.org/packages/bf/fe/5e94a977d058a54a19df95f12f7161ab6e323ad49f4dabc28822eb2df7ea/uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:37:29Z, size = 3705789, hashes = { sha256 = "10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f" } }, + { url = "https://files.pythonhosted.org/packages/26/dd/c7179618e46092a77e036650c1f056041a028a35c4d76945089fcfc38af8/uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:37:32Z, size = 3800523, hashes = { sha256 = "67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c" } }, + { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2024-10-14T23:37:33Z, size = 1447410, hashes = { sha256 = "c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8" } }, + { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2024-10-14T23:37:36Z, size = 805476, hashes = { sha256 = "0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0" } }, + { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:37:37Z, size = 3960855, hashes = { sha256 = "b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e" } }, + { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:37:40Z, size = 3973185, hashes = { sha256 = "8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb" } }, + { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:37:42Z, size = 3820256, hashes = { sha256 = "baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6" } }, + { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:37:45Z, size = 3937323, hashes = { sha256 = "4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d" } }, + { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2024-10-14T23:37:47Z, size = 1471284, hashes = { sha256 = "359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c" } }, + { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2024-10-14T23:37:50Z, size = 821349, hashes = { sha256 = "f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2" } }, + { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:37:51Z, size = 4580089, hashes = { sha256 = "baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d" } }, + { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:37:54Z, size = 4693770, hashes = { sha256 = "86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc" } }, + { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:37:55Z, size = 4451321, hashes = { sha256 = "461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb" } }, + { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:37:58Z, size = 4659022, hashes = { sha256 = "183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f" } }, + { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2024-10-14T23:38:00Z, size = 1468123, hashes = { sha256 = "bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281" } }, + { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2024-10-14T23:38:02Z, size = 819325, hashes = { sha256 = "787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af" } }, + { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:38:04Z, size = 4582806, hashes = { sha256 = "5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6" } }, + { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:38:06Z, size = 4701068, hashes = { sha256 = "f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816" } }, + { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:38:08Z, size = 4454428, hashes = { sha256 = "bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc" } }, + { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:38:10Z, size = 4660018, hashes = { sha256 = "a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553" } }, + { url = "https://files.pythonhosted.org/packages/b5/7b/85a2c8231eac451ef9caecba8715295820c9f94fb51c4f5b2e39c79a5c11/uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", upload-time = 2024-10-14T23:38:12Z, size = 1433814, hashes = { sha256 = "17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414" } }, + { url = "https://files.pythonhosted.org/packages/78/c9/10272e791562be6cfc4ee127883087de6443fede8f010b019ca0fdf841c1/uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", upload-time = 2024-10-14T23:38:15Z, size = 797954, hashes = { sha256 = "bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206" } }, + { url = "https://files.pythonhosted.org/packages/62/23/29da7a6d3fba8dfe375ea48a8c3a3e5562b770d24008d79a7a6e0150d7c1/uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:38:16Z, size = 4302867, hashes = { sha256 = "f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe" } }, + { url = "https://files.pythonhosted.org/packages/9a/46/72fb3fbb457cd68632542ecc7fa191a17dac501f70b7f3786a18912bbe0e/uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:38:19Z, size = 4303228, hashes = { sha256 = "e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79" } }, + { url = "https://files.pythonhosted.org/packages/1a/80/3f57f2458460501b709aec7c7e7f303b81b38ca35f786b41bf402b3349e8/uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:38:21Z, size = 4163776, hashes = { sha256 = "460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a" } }, + { url = "https://files.pythonhosted.org/packages/4f/9f/07c88dd3e76171e7808ff63719af12ee8bb6ea56fe40ea274da606ae5ade/uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:38:23Z, size = 4292873, hashes = { sha256 = "10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc" } }, + { url = "https://files.pythonhosted.org/packages/3c/a4/646a9d0edff7cde25fc1734695d3dfcee0501140dd0e723e4df3f0a50acb/uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2024-10-14T23:38:24Z, size = 1439646, hashes = { sha256 = "c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b" } }, + { url = "https://files.pythonhosted.org/packages/01/2e/e128c66106af9728f86ebfeeb52af27ecd3cb09336f3e2f3e06053707a15/uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2024-10-14T23:38:26Z, size = 800931, hashes = { sha256 = "46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2" } }, + { url = "https://files.pythonhosted.org/packages/2d/1a/9fbc2b1543d0df11f7aed1632f64bdf5ecc4053cf98cdc9edb91a65494f9/uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2024-10-14T23:38:27Z, size = 3829660, hashes = { sha256 = "53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0" } }, + { url = "https://files.pythonhosted.org/packages/b8/c0/392e235e4100ae3b95b5c6dac77f82b529d2760942b1e7e0981e5d8e895d/uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2024-10-14T23:38:29Z, size = 3827185, hashes = { sha256 = "88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75" } }, + { url = "https://files.pythonhosted.org/packages/e1/24/a5da6aba58f99aed5255eca87d58d1760853e8302d390820cc29058408e3/uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2024-10-14T23:38:31Z, size = 3705833, hashes = { sha256 = "221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd" } }, + { url = "https://files.pythonhosted.org/packages/1a/5c/6ba221bb60f1e6474474102e17e38612ec7a06dc320e22b687ab563d877f/uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2024-10-14T23:38:33Z, size = 3804696, hashes = { sha256 = "2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff" } }, +] + +[[packages]] name = "vine" version = "5.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636 }, -] - -[[package]] +sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", upload-time = 2023-11-05T08:46:53Z, size = 48980, hashes = { sha256 = "8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", upload-time = 2023-11-05T08:46:51Z, size = 9636, hashes = { sha256 = "40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc" } }] + +[[packages]] +name = "watchdog" +version = "6.0.0" +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", upload-time = 2024-11-01T14:07:13Z, size = 131220, hashes = { sha256 = "9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2024-11-01T14:06:24Z, size = 96390, hashes = { sha256 = "d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26" } }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2024-11-01T14:06:27Z, size = 88389, hashes = { sha256 = "bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112" } }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:29Z, size = 89020, hashes = { sha256 = "c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3" } }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2024-11-01T14:06:31Z, size = 96393, hashes = { sha256 = "6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c" } }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2024-11-01T14:06:32Z, size = 88392, hashes = { sha256 = "ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2" } }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:34Z, size = 89019, hashes = { sha256 = "afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c" } }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2024-11-01T14:06:37Z, size = 96471, hashes = { sha256 = "bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948" } }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2024-11-01T14:06:39Z, size = 88449, hashes = { sha256 = "c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860" } }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:41Z, size = 89054, hashes = { sha256 = "6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0" } }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2024-11-01T14:06:42Z, size = 96480, hashes = { sha256 = "490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c" } }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2024-11-01T14:06:45Z, size = 88451, hashes = { sha256 = "76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134" } }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:47Z, size = 89057, hashes = { sha256 = "a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b" } }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2024-11-01T14:06:49Z, size = 96390, hashes = { sha256 = "e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8" } }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2024-11-01T14:06:50Z, size = 88386, hashes = { sha256 = "90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a" } }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:51Z, size = 89017, hashes = { sha256 = "e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c" } }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", upload-time = 2024-11-01T14:06:53Z, size = 87902, hashes = { sha256 = "c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881" } }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:55Z, size = 88380, hashes = { sha256 = "9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11" } }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", upload-time = 2024-11-01T14:06:57Z, size = 87903, hashes = { sha256 = "7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa" } }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", upload-time = 2024-11-01T14:06:58Z, size = 88381, hashes = { sha256 = "e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e" } }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", upload-time = 2024-11-01T14:06:59Z, size = 79079, hashes = { sha256 = "7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13" } }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", upload-time = 2024-11-01T14:07:01Z, size = 79078, hashes = { sha256 = "9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379" } }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", upload-time = 2024-11-01T14:07:02Z, size = 79076, hashes = { sha256 = "82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e" } }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", upload-time = 2024-11-01T14:07:03Z, size = 79077, hashes = { sha256 = "212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f" } }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", upload-time = 2024-11-01T14:07:05Z, size = 79078, hashes = { sha256 = "e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26" } }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", upload-time = 2024-11-01T14:07:06Z, size = 79077, hashes = { sha256 = "2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c" } }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", upload-time = 2024-11-01T14:07:07Z, size = 79078, hashes = { sha256 = "20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2" } }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", upload-time = 2024-11-01T14:07:09Z, size = 79065, hashes = { sha256 = "07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a" } }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", upload-time = 2024-11-01T14:07:10Z, size = 79070, hashes = { sha256 = "cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680" } }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", upload-time = 2024-11-01T14:07:11Z, size = 79067, hashes = { sha256 = "a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f" } }, +] + +[[packages]] +name = "watchfiles" +version = "1.1.1" +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", upload-time = 2025-10-14T15:06:21Z, size = 94440, hashes = { sha256 = "a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:04:18Z, size = 407318, hashes = { sha256 = "eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c" } }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:04:20Z, size = 394478, hashes = { sha256 = "03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43" } }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:04:21Z, size = 449894, hashes = { sha256 = "8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31" } }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:04:22Z, size = 459065, hashes = { sha256 = "f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac" } }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:04:24Z, size = 488377, hashes = { sha256 = "3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d" } }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:04:25Z, size = 595837, hashes = { sha256 = "e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d" } }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:04:26Z, size = 473456, hashes = { sha256 = "620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863" } }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:04:27Z, size = 455614, hashes = { sha256 = "544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab" } }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:04:28Z, size = 630690, hashes = { sha256 = "bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82" } }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:04:29Z, size = 622459, hashes = { sha256 = "1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4" } }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", upload-time = 2025-10-14T15:04:30Z, size = 272663, hashes = { sha256 = "3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844" } }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", upload-time = 2025-10-14T15:04:31Z, size = 287453, hashes = { sha256 = "a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e" } }, + { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:04:32Z, size = 406529, hashes = { sha256 = "f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5" } }, + { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:04:33Z, size = 394384, hashes = { sha256 = "421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741" } }, + { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:04:34Z, size = 448789, hashes = { sha256 = "6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6" } }, + { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:04:35Z, size = 460521, hashes = { sha256 = "f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b" } }, + { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:04:37Z, size = 488722, hashes = { sha256 = "b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14" } }, + { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:04:38Z, size = 596088, hashes = { sha256 = "5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d" } }, + { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:04:39Z, size = 472923, hashes = { sha256 = "9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff" } }, + { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:04:40Z, size = 456080, hashes = { sha256 = "aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606" } }, + { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:04:41Z, size = 629432, hashes = { sha256 = "5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701" } }, + { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:04:42Z, size = 623046, hashes = { sha256 = "399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10" } }, + { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", upload-time = 2025-10-14T15:04:43Z, size = 271473, hashes = { sha256 = "de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849" } }, + { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", upload-time = 2025-10-14T15:04:44Z, size = 287598, hashes = { sha256 = "35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4" } }, + { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", upload-time = 2025-10-14T15:04:45Z, size = 277210, hashes = { sha256 = "57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e" } }, + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:04:46Z, size = 404745, hashes = { sha256 = "8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d" } }, + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:04:48Z, size = 391769, hashes = { sha256 = "bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610" } }, + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:04:49Z, size = 449374, hashes = { sha256 = "8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af" } }, + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:04:50Z, size = 459485, hashes = { sha256 = "2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6" } }, + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:04:51Z, size = 488813, hashes = { sha256 = "30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce" } }, + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:04:52Z, size = 594816, hashes = { sha256 = "f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa" } }, + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:04:53Z, size = 475186, hashes = { sha256 = "dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb" } }, + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:04:55Z, size = 456812, hashes = { sha256 = "1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803" } }, + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:04:56Z, size = 630196, hashes = { sha256 = "28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94" } }, + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:04:57Z, size = 622657, hashes = { sha256 = "36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43" } }, + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", upload-time = 2025-10-14T15:04:59Z, size = 272042, hashes = { sha256 = "859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9" } }, + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", upload-time = 2025-10-14T15:05:00Z, size = 288410, hashes = { sha256 = "91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9" } }, + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", upload-time = 2025-10-14T15:05:01Z, size = 278209, hashes = { sha256 = "a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404" } }, + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:05:02Z, size = 404321, hashes = { sha256 = "130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18" } }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:05:03Z, size = 391783, hashes = { sha256 = "5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a" } }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:05:04Z, size = 449279, hashes = { sha256 = "14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219" } }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:05:04Z, size = 459405, hashes = { sha256 = "f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428" } }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:05:05Z, size = 488976, hashes = { sha256 = "059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0" } }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:05:06Z, size = 595506, hashes = { sha256 = "bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150" } }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:05:07Z, size = 474936, hashes = { sha256 = "319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae" } }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:05:09Z, size = 456147, hashes = { sha256 = "c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d" } }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:05:10Z, size = 630007, hashes = { sha256 = "c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b" } }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:05:11Z, size = 622280, hashes = { sha256 = "3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374" } }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", upload-time = 2025-10-14T15:05:12Z, size = 272056, hashes = { sha256 = "bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0" } }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", upload-time = 2025-10-14T15:05:13Z, size = 288162, hashes = { sha256 = "52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42" } }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", upload-time = 2025-10-14T15:05:14Z, size = 277909, hashes = { sha256 = "ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18" } }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:05:15Z, size = 403389, hashes = { sha256 = "563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da" } }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:05:16Z, size = 389964, hashes = { sha256 = "3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051" } }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:05:17Z, size = 448114, hashes = { sha256 = "ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e" } }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:05:18Z, size = 460264, hashes = { sha256 = "cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70" } }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:05:20Z, size = 487877, hashes = { sha256 = "836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261" } }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:05:21Z, size = 595176, hashes = { sha256 = "743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620" } }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:05:22Z, size = 473577, hashes = { sha256 = "afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04" } }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:05:23Z, size = 455425, hashes = { sha256 = "3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77" } }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:05:24Z, size = 628826, hashes = { sha256 = "831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef" } }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:05:25Z, size = 622208, hashes = { sha256 = "f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf" } }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:05:26Z, size = 404315, hashes = { sha256 = "d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5" } }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:05:27Z, size = 390869, hashes = { sha256 = "39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd" } }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:05:28Z, size = 449919, hashes = { sha256 = "7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb" } }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:05:30Z, size = 460845, hashes = { sha256 = "bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5" } }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:05:31Z, size = 489027, hashes = { sha256 = "b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3" } }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:05:32Z, size = 595615, hashes = { sha256 = "526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33" } }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:05:33Z, size = 474836, hashes = { sha256 = "04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510" } }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:05:34Z, size = 455099, hashes = { sha256 = "5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05" } }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:05:35Z, size = 630626, hashes = { sha256 = "74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6" } }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:05:36Z, size = 622519, hashes = { sha256 = "8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81" } }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", upload-time = 2025-10-14T15:05:37Z, size = 272078, hashes = { sha256 = "3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b" } }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", upload-time = 2025-10-14T15:05:38Z, size = 287664, hashes = { sha256 = "c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a" } }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", upload-time = 2025-10-14T15:05:39Z, size = 277154, hashes = { sha256 = "842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02" } }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:05:40Z, size = 403820, hashes = { sha256 = "88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21" } }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:05:41Z, size = 390510, hashes = { sha256 = "55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5" } }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:05:43Z, size = 448408, hashes = { sha256 = "3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7" } }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:05:44Z, size = 458968, hashes = { sha256 = "6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101" } }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:05:45Z, size = 488096, hashes = { sha256 = "f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44" } }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:05:46Z, size = 596040, hashes = { sha256 = "00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c" } }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:05:47Z, size = 473847, hashes = { sha256 = "a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc" } }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:05:48Z, size = 455072, hashes = { sha256 = "8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c" } }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:05:49Z, size = 629104, hashes = { sha256 = "311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099" } }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:05:50Z, size = 622112, hashes = { sha256 = "a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01" } }, + { url = "https://files.pythonhosted.org/packages/a4/68/a7303a15cc797ab04d58f1fea7f67c50bd7f80090dfd7e750e7576e07582/watchfiles-1.1.1-cp39-cp39-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:05:51Z, size = 409220, hashes = { sha256 = "c882d69f6903ef6092bedfb7be973d9319940d56b8427ab9187d1ecd73438a70" } }, + { url = "https://files.pythonhosted.org/packages/99/b8/d1857ce9ac76034c053fa7ef0e0ef92d8bd031e842ea6f5171725d31e88f/watchfiles-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:05:53Z, size = 396712, hashes = { sha256 = "d6ff426a7cb54f310d51bfe83fe9f2bbe40d540c741dc974ebc30e6aa238f52e" } }, + { url = "https://files.pythonhosted.org/packages/41/7a/da7ada566f48beaa6a30b13335b49d1f6febaf3a5ddbd1d92163a1002cf4/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:05:54Z, size = 451462, hashes = { sha256 = "79ff6c6eadf2e3fc0d7786331362e6ef1e51125892c75f1004bd6b52155fb956" } }, + { url = "https://files.pythonhosted.org/packages/e2/b2/7cb9e0d5445a8d45c4cccd68a590d9e3a453289366b96ff37d1075aaebef/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", upload-time = 2025-10-14T15:05:55Z, size = 460811, hashes = { sha256 = "c1f5210f1b8fc91ead1283c6fd89f70e76fb07283ec738056cf34d51e9c1d62c" } }, + { url = "https://files.pythonhosted.org/packages/04/9d/b07d4491dde6db6ea6c680fdec452f4be363d65c82004faf2d853f59b76f/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-10-14T15:05:56Z, size = 490576, hashes = { sha256 = "b9c4702f29ca48e023ffd9b7ff6b822acdf47cb1ff44cb490a3f1d5ec8987e9c" } }, + { url = "https://files.pythonhosted.org/packages/56/03/e64dcab0a1806157db272a61b7891b062f441a30580a581ae72114259472/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", upload-time = 2025-10-14T15:05:57Z, size = 597726, hashes = { sha256 = "acb08650863767cbc58bca4813b92df4d6c648459dcaa3d4155681962b2aa2d3" } }, + { url = "https://files.pythonhosted.org/packages/5c/8e/a827cf4a8d5f2903a19a934dcf512082eb07675253e154d4cd9367978a58/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", upload-time = 2025-10-14T15:05:59Z, size = 474900, hashes = { sha256 = "08af70fd77eee58549cd69c25055dc344f918d992ff626068242259f98d598a2" } }, + { url = "https://files.pythonhosted.org/packages/dc/a6/94fed0b346b85b22303a12eee5f431006fae6af70d841cac2f4403245533/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:06:00Z, size = 457521, hashes = { sha256 = "6c3631058c37e4a0ec440bf583bc53cdbd13e5661bb6f465bc1d88ee9a0a4d02" } }, + { url = "https://files.pythonhosted.org/packages/c4/64/bc3331150e8f3c778d48a4615d4b72b3d2d87868635e6c54bbd924946189/watchfiles-1.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", upload-time = 2025-10-14T15:06:01Z, size = 632191, hashes = { sha256 = "cf57a27fb986c6243d2ee78392c503826056ffe0287e8794503b10fb51b881be" } }, + { url = "https://files.pythonhosted.org/packages/e4/84/f39e19549c2f3ec97225dcb2ceb9a7bb3c5004ed227aad1f321bf0ff2051/watchfiles-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", upload-time = 2025-10-14T15:06:02Z, size = 623923, hashes = { sha256 = "d7e7067c98040d646982daa1f37a33d3544138ea155536c2e0e63e07ff8a7e0f" } }, + { url = "https://files.pythonhosted.org/packages/0e/24/0759ae15d9a0c9c5fe946bd4cf45ab9e7bad7cfede2c06dc10f59171b29f/watchfiles-1.1.1-cp39-cp39-win32.whl", upload-time = 2025-10-14T15:06:03Z, size = 274010, hashes = { sha256 = "6c9c9262f454d1c4d8aaa7050121eb4f3aea197360553699520767daebf2180b" } }, + { url = "https://files.pythonhosted.org/packages/7e/3b/eb26cddd4dfa081e2bf6918be3b2fc05ee3b55c1d21331d5562ee0c6aaad/watchfiles-1.1.1-cp39-cp39-win_amd64.whl", upload-time = 2025-10-14T15:06:04Z, size = 289090, hashes = { sha256 = "74472234c8370669850e1c312490f6026d132ca2d396abfad8830b4f1c096957" } }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:06:05Z, size = 409611, hashes = { sha256 = "17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3" } }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:06:07Z, size = 396889, hashes = { sha256 = "672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2" } }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:06:08Z, size = 451616, hashes = { sha256 = "77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d" } }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:06:09Z, size = 458413, hashes = { sha256 = "0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b" } }, + { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:06:10Z, size = 408250, hashes = { sha256 = "db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88" } }, + { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:06:11Z, size = 396117, hashes = { sha256 = "89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336" } }, + { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:06:12Z, size = 450493, hashes = { sha256 = "ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24" } }, + { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:06:13Z, size = 457546, hashes = { sha256 = "3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49" } }, + { url = "https://files.pythonhosted.org/packages/00/db/38a2c52fdbbfe2fc7ffaaaaaebc927d52b9f4d5139bba3186c19a7463001/watchfiles-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", upload-time = 2025-10-14T15:06:14Z, size = 409210, hashes = { sha256 = "cdab464fee731e0884c35ae3588514a9bcf718d0e2c82169c1c4a85cc19c3c7f" } }, + { url = "https://files.pythonhosted.org/packages/d1/43/d7e8b71f6c21ff813ee8da1006f89b6c7fff047fb4c8b16ceb5e840599c5/watchfiles-1.1.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", upload-time = 2025-10-14T15:06:16Z, size = 397286, hashes = { sha256 = "3dbd8cbadd46984f802f6d479b7e3afa86c42d13e8f0f322d669d79722c8ec34" } }, + { url = "https://files.pythonhosted.org/packages/1f/5d/884074a5269317e75bd0b915644b702b89de73e61a8a7446e2b225f45b1f/watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-10-14T15:06:18Z, size = 451768, hashes = { sha256 = "5524298e3827105b61951a29c3512deb9578586abf3a7c5da4a8069df247cccc" } }, + { url = "https://files.pythonhosted.org/packages/17/71/7ffcaa9b5e8961a25026058058c62ec8f604d2a6e8e1e94bee8a09e1593f/watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-10-14T15:06:19Z, size = 458561, hashes = { sha256 = "4b943d3668d61cfa528eb949577479d3b077fd25fb83c641235437bc0b5bc60e" } }, +] + +[[packages]] 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 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, -] +version = "0.2.14" +sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", upload-time = 2025-09-22T16:29:53Z, size = 102293, hashes = { sha256 = "4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", upload-time = 2025-09-22T16:29:51Z, size = 37286, hashes = { sha256 = "a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1" } }] + +[[packages]] +name = "websockets" +version = "15.0.1" +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", upload-time = 2025-03-05T20:03:41Z, size = 177016, hashes = { sha256 = "82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", upload-time = 2025-03-05T20:01:35Z, size = 175423, hashes = { sha256 = "d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b" } }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", upload-time = 2025-03-05T20:01:37Z, size = 173080, hashes = { sha256 = "ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205" } }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:01:39Z, size = 173329, hashes = { sha256 = "5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a" } }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:01:41Z, size = 182312, hashes = { sha256 = "0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e" } }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-03-05T20:01:43Z, size = 181319, hashes = { sha256 = "4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf" } }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-03-05T20:01:46Z, size = 181631, hashes = { sha256 = "ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb" } }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", upload-time = 2025-03-05T20:01:47Z, size = 182016, hashes = { sha256 = "5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d" } }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", upload-time = 2025-03-05T20:01:48Z, size = 181426, hashes = { sha256 = "0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9" } }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", upload-time = 2025-03-05T20:01:50Z, size = 181360, hashes = { sha256 = "3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c" } }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", upload-time = 2025-03-05T20:01:52Z, size = 176388, hashes = { sha256 = "1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256" } }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", upload-time = 2025-03-05T20:01:53Z, size = 176830, hashes = { sha256 = "39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41" } }, + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", upload-time = 2025-03-05T20:01:56Z, size = 175423, hashes = { sha256 = "823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431" } }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", upload-time = 2025-03-05T20:01:57Z, size = 173082, hashes = { sha256 = "678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57" } }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:01:59Z, size = 173330, hashes = { sha256 = "d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905" } }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:02:00Z, size = 182878, hashes = { sha256 = "d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562" } }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-03-05T20:02:03Z, size = 181883, hashes = { sha256 = "66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792" } }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-03-05T20:02:05Z, size = 182252, hashes = { sha256 = "8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413" } }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", upload-time = 2025-03-05T20:02:07Z, size = 182521, hashes = { sha256 = "8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8" } }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", upload-time = 2025-03-05T20:02:09Z, size = 181958, hashes = { sha256 = "693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3" } }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", upload-time = 2025-03-05T20:02:11Z, size = 181918, hashes = { sha256 = "54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf" } }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", upload-time = 2025-03-05T20:02:13Z, size = 176388, hashes = { sha256 = "16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85" } }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", upload-time = 2025-03-05T20:02:14Z, size = 176828, hashes = { sha256 = "27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065" } }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", upload-time = 2025-03-05T20:02:16Z, size = 175437, hashes = { sha256 = "3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3" } }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", upload-time = 2025-03-05T20:02:18Z, size = 173096, hashes = { sha256 = "592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665" } }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:02:20Z, size = 173332, hashes = { sha256 = "0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2" } }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:02:22Z, size = 183152, hashes = { sha256 = "e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215" } }, + { 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", upload-time = 2025-03-05T20:02:24Z, size = 182096, hashes = { sha256 = "0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5" } }, + { 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", upload-time = 2025-03-05T20:02:25Z, size = 182523, hashes = { sha256 = "64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65" } }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", upload-time = 2025-03-05T20:02:26Z, size = 182790, hashes = { sha256 = "d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe" } }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", upload-time = 2025-03-05T20:02:30Z, size = 182165, hashes = { sha256 = "5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4" } }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", upload-time = 2025-03-05T20:02:31Z, size = 182160, hashes = { sha256 = "3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597" } }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", upload-time = 2025-03-05T20:02:33Z, size = 176395, hashes = { sha256 = "c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9" } }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", upload-time = 2025-03-05T20:02:34Z, size = 176841, hashes = { sha256 = "fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7" } }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", upload-time = 2025-03-05T20:02:36Z, size = 175440, hashes = { sha256 = "ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931" } }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", upload-time = 2025-03-05T20:02:37Z, size = 173098, hashes = { sha256 = "5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675" } }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:02:39Z, size = 173329, hashes = { sha256 = "746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151" } }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:02:40Z, size = 183111, hashes = { sha256 = "595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22" } }, + { 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", upload-time = 2025-03-05T20:02:41Z, size = 182054, hashes = { sha256 = "3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f" } }, + { 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", upload-time = 2025-03-05T20:02:43Z, size = 182496, hashes = { sha256 = "0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8" } }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", upload-time = 2025-03-05T20:02:48Z, size = 182829, hashes = { sha256 = "229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375" } }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", upload-time = 2025-03-05T20:02:50Z, size = 182217, hashes = { sha256 = "756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d" } }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", upload-time = 2025-03-05T20:02:51Z, size = 182195, hashes = { sha256 = "558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4" } }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", upload-time = 2025-03-05T20:02:53Z, size = 176393, hashes = { sha256 = "ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa" } }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", upload-time = 2025-03-05T20:02:55Z, size = 176837, hashes = { sha256 = "e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561" } }, + { url = "https://files.pythonhosted.org/packages/36/db/3fff0bcbe339a6fa6a3b9e3fbc2bfb321ec2f4cd233692272c5a8d6cf801/websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2025-03-05T20:02:56Z, size = 175424, hashes = { sha256 = "5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5" } }, + { url = "https://files.pythonhosted.org/packages/46/e6/519054c2f477def4165b0ec060ad664ed174e140b0d1cbb9fafa4a54f6db/websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", upload-time = 2025-03-05T20:02:58Z, size = 173077, hashes = { sha256 = "abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a" } }, + { url = "https://files.pythonhosted.org/packages/1a/21/c0712e382df64c93a0d16449ecbf87b647163485ca1cc3f6cbadb36d2b03/websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:02:59Z, size = 173324, hashes = { sha256 = "a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b" } }, + { url = "https://files.pythonhosted.org/packages/1c/cb/51ba82e59b3a664df54beed8ad95517c1b4dc1a913730e7a7db778f21291/websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:03:01Z, size = 182094, hashes = { sha256 = "d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770" } }, + { url = "https://files.pythonhosted.org/packages/fb/0f/bf3788c03fec679bcdaef787518dbe60d12fe5615a544a6d4cf82f045193/websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-03-05T20:03:03Z, size = 181094, hashes = { sha256 = "47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb" } }, + { url = "https://files.pythonhosted.org/packages/5e/da/9fb8c21edbc719b66763a571afbaf206cb6d3736d28255a46fc2fe20f902/websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-03-05T20:03:04Z, size = 181397, hashes = { sha256 = "ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054" } }, + { url = "https://files.pythonhosted.org/packages/2e/65/65f379525a2719e91d9d90c38fe8b8bc62bd3c702ac651b7278609b696c4/websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", upload-time = 2025-03-05T20:03:06Z, size = 181794, hashes = { sha256 = "4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee" } }, + { url = "https://files.pythonhosted.org/packages/d9/26/31ac2d08f8e9304d81a1a7ed2851c0300f636019a57cbaa91342015c72cc/websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", upload-time = 2025-03-05T20:03:08Z, size = 181194, hashes = { sha256 = "363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed" } }, + { url = "https://files.pythonhosted.org/packages/98/72/1090de20d6c91994cd4b357c3f75a4f25ee231b63e03adea89671cc12a3f/websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", upload-time = 2025-03-05T20:03:10Z, size = 181164, hashes = { sha256 = "2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880" } }, + { url = "https://files.pythonhosted.org/packages/2d/37/098f2e1c103ae8ed79b0e77f08d83b0ec0b241cf4b7f2f10edd0126472e1/websockets-15.0.1-cp39-cp39-win32.whl", upload-time = 2025-03-05T20:03:12Z, size = 176381, hashes = { sha256 = "3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411" } }, + { url = "https://files.pythonhosted.org/packages/75/8b/a32978a3ab42cebb2ebdd5b05df0696a09f4d436ce69def11893afa301f0/websockets-15.0.1-cp39-cp39-win_amd64.whl", upload-time = 2025-03-05T20:03:14Z, size = 176841, hashes = { sha256 = "b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4" } }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-03-05T20:03:17Z, size = 173109, hashes = { sha256 = "0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3" } }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:03:19Z, size = 173343, hashes = { sha256 = "1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1" } }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:03:21Z, size = 174599, hashes = { sha256 = "76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475" } }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-03-05T20:03:23Z, size = 174207, hashes = { sha256 = "f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9" } }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-03-05T20:03:25Z, size = 174155, hashes = { sha256 = "b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04" } }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", upload-time = 2025-03-05T20:03:27Z, size = 176884, hashes = { sha256 = "cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122" } }, + { url = "https://files.pythonhosted.org/packages/b7/48/4b67623bac4d79beb3a6bb27b803ba75c1bdedc06bd827e465803690a4b2/websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", upload-time = 2025-03-05T20:03:29Z, size = 173106, hashes = { sha256 = "7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940" } }, + { url = "https://files.pythonhosted.org/packages/ed/f0/adb07514a49fe5728192764e04295be78859e4a537ab8fcc518a3dbb3281/websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", upload-time = 2025-03-05T20:03:30Z, size = 173339, hashes = { sha256 = "47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e" } }, + { url = "https://files.pythonhosted.org/packages/87/28/bd23c6344b18fb43df40d0700f6d3fffcd7cef14a6995b4f976978b52e62/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", upload-time = 2025-03-05T20:03:32Z, size = 174597, hashes = { sha256 = "67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9" } }, + { url = "https://files.pythonhosted.org/packages/6d/79/ca288495863d0f23a60f546f0905ae8f3ed467ad87f8b6aceb65f4c013e4/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", upload-time = 2025-03-05T20:03:33Z, size = 174205, hashes = { sha256 = "d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b" } }, + { url = "https://files.pythonhosted.org/packages/04/e4/120ff3180b0872b1fe6637f6f995bcb009fb5c87d597c1fc21456f50c848/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", upload-time = 2025-03-05T20:03:35Z, size = 174150, hashes = { sha256 = "4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f" } }, + { url = "https://files.pythonhosted.org/packages/cb/c3/30e2f9c539b8da8b1d76f64012f3b19253271a63413b2d3adb94b143407f/websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", upload-time = 2025-03-05T20:03:37Z, size = 176877, hashes = { sha256 = "21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123" } }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", upload-time = 2025-03-05T20:03:39Z, size = 169743, hashes = { sha256 = "f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f" } }, +] + +[[packages]] +name = "yamllint" +version = "1.37.1" +sdist = { url = "https://files.pythonhosted.org/packages/46/f2/cd8b7584a48ee83f0bc94f8a32fea38734cefcdc6f7324c4d3bfc699457b/yamllint-1.37.1.tar.gz", upload-time = 2025-05-04T08:25:54Z, size = 141613, hashes = { sha256 = "81f7c0c5559becc8049470d86046b36e96113637bcbe4753ecef06977c00245d" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/dd/b9/be7a4cfdf47e03785f657f94daea8123e838d817be76c684298305bd789f/yamllint-1.37.1-py3-none-any.whl", upload-time = 2025-05-04T08:25:52Z, size = 68813, hashes = { sha256 = "364f0d79e81409f591e323725e6a9f4504c8699ddf2d7263d8d2b539cd66a583" } }]