From c59a01778505a06d528c4e6b43dc789aa3008b86 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 07:55:33 -0300 Subject: [PATCH 01/25] Add Volume Manager integration --- .../src/compute_horde_core/volume/__init__.py | 8 + .../src/compute_horde_core/volume/_manager.py | 172 ++++++++++++++++++ .../src/compute_horde_core/volume/_models.py | 13 ++ .../executor/job_runner.py | 132 +++++++++++--- .../src/compute_horde_executor/settings.py | 3 + executor/pyproject.toml | 1 + executor/uv.lock | 2 + 7 files changed, 310 insertions(+), 21 deletions(-) create mode 100644 compute_horde_sdk/src/compute_horde_core/volume/_manager.py diff --git a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py index e832d218c..8b8b4208d 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py @@ -8,3 +8,11 @@ ZipUrlVolumeDownloader, ) from ._models import * +from ._manager import ( + VolumeManagerClient, + VolumeManagerError, + VolumeManagerResponse, + VolumeManagerMount, + get_volume_manager_headers, + create_volume_manager_client, +) diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py new file mode 100644 index 000000000..0a0947ac4 --- /dev/null +++ b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import json +import logging +import os +from dataclasses import dataclass +from typing import Any + +import httpx +import tenacity +from ._models import Volume + +logger = logging.getLogger(__name__) + + +def get_volume_manager_headers(): + """Extract volume manager headers from environment variables.""" + headers = {} + prefix = "COMPUTE_HORDE_VOLUME_MANAGER_HEADER_" + for key, value in os.environ.items(): + if key.startswith(prefix): + header_name = key[len(prefix) :] + headers[header_name] = value + return headers + + +@dataclass +class VolumeManagerMount: + """Represents a Docker mount option returned by the volume manager.""" + + type: str + source: str + target: str + + +@dataclass +class VolumeManagerResponse: + """Response from volume manager prepare_volume endpoint.""" + + mounts: list[VolumeManagerMount] + + +class VolumeManagerError(Exception): + """Exception raised when volume manager operations fail.""" + + def __init__(self, description: str, error_detail: str | None = None) -> None: + self.description = description + self.error_detail = error_detail + + def __str__(self) -> str: + if self.error_detail is not None: + return f"{self.description}: {self.error_detail}" + return self.description + + +class VolumeManagerClient: + """Client for communicating with the Volume Manager service.""" + + def __init__(self, base_url: str, headers: dict[str, str] | None = None): + self.base_url = base_url.rstrip("/") + self.headers = headers or {} + + async def prepare_volume( + self, job_uuid: str, volume: Volume, job_metadata: dict[str, Any] + ) -> VolumeManagerResponse: + """ + Request the volume manager to prepare a volume for the job. + + Args: + job_uuid: Unique identifier for the job + volume: Volume specification to prepare + job_metadata: Additional metadata about the job + + Returns: + VolumeManagerResponse with mount options + + Raises: + VolumeManagerError: If the request fails + """ + url = f"{self.base_url}/prepare_volume" + volume_data = volume.model_dump() + payload = {"job_uuid": job_uuid, "volume": volume_data, "job_metadata": job_metadata} + + response_data = await self._make_request(url, payload, "prepare_volume") + mounts = [VolumeManagerMount(**mount) for mount in response_data["mounts"]] + return VolumeManagerResponse(mounts=mounts) + + async def job_finished(self, job_uuid: str) -> None: + """ + Notify the volume manager that a job has finished. + + Args: + job_uuid: Unique identifier for the finished job + + Raises: + VolumeManagerError: If the notification fails + """ + url = f"{self.base_url}/job_finished" + payload = {"job_uuid": job_uuid} + + await self._make_request(url, payload, "job_finished", timeout=30.0) + + @tenacity.retry( + stop=tenacity.stop_after_attempt(3), + wait=tenacity.wait_fixed(1), + retry=tenacity.retry_if_exception_type((httpx.RequestError, httpx.HTTPStatusError)), + ) + async def _make_request( + self, + url: str, + payload: dict[str, Any], + operation: str, + timeout: float = 300.0 + ) -> dict[str, Any]: + """ + Make a POST request to the Volume Manager with standardized error handling. + + Args: + url: The endpoint URL + payload: The JSON payload to send + operation: Operation name for error messages + timeout: Request timeout in seconds + + Returns: + Parsed JSON response + + Raises: + VolumeManagerError: If the request fails or response is invalid + """ + logger.debug(f"Making {operation} request to Volume Manager at {url}") + logger.debug(f"Request payload: {json.dumps(payload, indent=2)}") + + try: + async with httpx.AsyncClient(timeout=timeout) as client: + response = await client.post(url, json=payload, headers=self.headers) + response.raise_for_status() + + logger.debug(f"Volume Manager {operation} response status: {response.status_code}") + logger.debug(f"Volume Manager {operation} response: {response.text}") + + return response.json() + + except httpx.HTTPStatusError as e: + # Handle non-retryable status codes + error_msg = f"Volume Manager {operation} returned status {e.response.status_code}" + error_detail = None + + try: + error_data = e.response.json() + if "error" in error_data: + error_detail = error_data["error"] + except (json.JSONDecodeError, KeyError): + error_detail = e.response.text + + raise VolumeManagerError(error_msg, error_detail=error_detail) + except json.JSONDecodeError as e: + raise VolumeManagerError(f"Invalid JSON response from Volume Manager {operation}: {e}", error_detail=str(e)) + + +def create_volume_manager_client(base_url: str, headers: dict[str, str] | None = None) -> VolumeManagerClient: + """ + Create a Volume Manager client with the specified configuration. + + Args: + base_url: The base URL of the Volume Manager service + headers: Optional headers to include in requests + + Returns: + A configured VolumeManagerClient instance + """ + logger.debug(f"Volume manager created at {base_url}") + return VolumeManagerClient(base_url=base_url, headers=headers) \ No newline at end of file diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_models.py b/compute_horde_sdk/src/compute_horde_core/volume/_models.py index 1ac6f85a6..5697f751b 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_models.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_models.py @@ -21,6 +21,15 @@ def __str__(self) -> str: return str.__str__(self) +class VolumeUsageType(str, enum.Enum): + """Hint for volume managers about expected usage patterns.""" + single_use = "single_use" # Volume is expected to be used just once + reusable = "reusable" # Volume is expected to be used many times + + def __str__(self) -> str: + return str.__str__(self) + + class HuggingfaceVolume(pydantic.BaseModel): volume_type: Literal[VolumeType.huggingface_volume] = VolumeType.huggingface_volume repo_id: str @@ -32,6 +41,7 @@ class HuggingfaceVolume(pydantic.BaseModel): # If provided, only files matching at least one pattern are downloaded. allow_patterns: str | list[str] | None = None token: str | None = None + usage_type: VolumeUsageType | None = None def is_safe(self) -> bool: return True @@ -41,6 +51,7 @@ class InlineVolume(pydantic.BaseModel): volume_type: Literal[VolumeType.inline] = VolumeType.inline contents: str = pydantic.Field(repr=False) relative_path: str | None = None + usage_type: VolumeUsageType | None = None def is_safe(self) -> bool: return True @@ -50,6 +61,7 @@ class ZipUrlVolume(pydantic.BaseModel): volume_type: Literal[VolumeType.zip_url] = VolumeType.zip_url contents: str # backwards compatibility - this is the URL relative_path: str | None = Field(default=None) + usage_type: VolumeUsageType | None = None def is_safe(self) -> bool: domain = urlparse(self.contents).netloc @@ -62,6 +74,7 @@ class SingleFileVolume(pydantic.BaseModel): volume_type: Literal[VolumeType.single_file] = VolumeType.single_file url: str relative_path: str + usage_type: VolumeUsageType | None = None def is_safe(self) -> bool: domain = urlparse(self.url).netloc diff --git a/executor/app/src/compute_horde_executor/executor/job_runner.py b/executor/app/src/compute_horde_executor/executor/job_runner.py index 7db940672..4558a1f3b 100644 --- a/executor/app/src/compute_horde_executor/executor/job_runner.py +++ b/executor/app/src/compute_horde_executor/executor/job_runner.py @@ -31,6 +31,13 @@ from compute_horde_executor.executor.miner_client import ExecutionResult, JobError, JobResult from compute_horde_executor.executor.utils import temporary_process +from compute_horde_core.volume import ( + create_volume_manager_client, + VolumeManagerClient, + VolumeManagerError, + VolumeManagerMount, + get_volume_manager_headers, +) logger = logging.getLogger(__name__) @@ -136,6 +143,15 @@ def __init__(self): self.nginx_dir_path: pathlib.Path | None = None self.executor_certificate: str | None = None + # Volume manager client (if configured) + self.volume_manager_client: VolumeManagerClient | None = None + if settings.VOLUME_MANAGER_ADDRESS: + headers = get_volume_manager_headers() + self.volume_manager_client = create_volume_manager_client(settings.VOLUME_MANAGER_ADDRESS, headers) + + # Track volume manager mounts for cleanup + self.volume_manager_mounts: list[VolumeManagerMount] = [] + def generate_streaming_certificate(self, executor_ip: str, public_key: str): """ Generate and save the streaming certificate and public key for the executor. @@ -242,6 +258,17 @@ async def start_job(self) -> AsyncGenerator[None, Any]: f"{self.artifacts_mount_dir.as_posix()}/:{self.full_job_request.artifacts_dir}", ] + # Build volume mount flags + volume_flags = [] + if self.volume_manager_mounts: + # Use volume manager mounts + for mount in self.volume_manager_mounts: + volume_flags.extend(["-v", f"{mount.source}:{mount.target}"]) + logger.debug(f"Adding volume manager mount: {mount.source}:{mount.target}") + else: + # Use default volume mount + volume_flags = ["-v", f"{self.volume_mount_dir.as_posix()}/:/volume/"] + self.cmd = [ "docker", "run", @@ -251,8 +278,7 @@ async def start_job(self) -> AsyncGenerator[None, Any]: "--rm", "--network", job_network, - "-v", - f"{self.volume_mount_dir.as_posix()}/:/volume/", + *volume_flags, "-v", f"{self.output_volume_mount_dir.as_posix()}/:/output/", "-v", @@ -411,6 +437,10 @@ async def upload_results(self) -> JobResult: ) async def clean(self): + # Notify volume manager if configured + if self.volume_manager_client and self.initial_job_request: + await self._notify_volume_manager_job_finished() + # remove input/output directories with docker, to deal with funky file permissions root_for_remove = pathlib.Path("/temp_dir/") process = await asyncio.create_subprocess_exec( @@ -439,7 +469,25 @@ async def clean(self): except Exception as e: logger.error(f"Failed to remove temp dir {self.temp_dir}: {e}") + async def _notify_volume_manager_job_finished(self): + """Notify the volume manager that the job has finished.""" + assert self.volume_manager_client is not None + assert self.initial_job_request is not None + + job_uuid = self.initial_job_request.job_uuid + try: + logger.debug(f"Notifying Volume Manager that job {job_uuid} has finished") + await self.volume_manager_client.job_finished(job_uuid) + except Exception as e: + # Log the error but don't fail the job cleanup + logger.warning(f"Failed to notify Volume Manager of job completion: {e}") + async def _unpack_volume(self, volume: Volume | None): + """ + Handle volume preparation. If volume manager is configured, delegate to it. + Otherwise, use the traditional download approach. + """ + # Clear the volume mount directory assert str(self.volume_mount_dir) not in {"~", "/"} for path in self.volume_mount_dir.glob("*"): if path.is_file(): @@ -448,32 +496,74 @@ async def _unpack_volume(self, volume: Volume | None): shutil.rmtree(path) if volume is not None: - # TODO(mlech): Refactor this to not treat `HuggingfaceVolume` with a special care - volume_downloader = VolumeDownloader.for_volume(volume) - volume_downloader.max_size_bytes = settings.VOLUME_MAX_SIZE_BYTES - if volume_downloader.handles_volume_type() is HuggingfaceVolume: - if volume.token is None: - volume.token = settings.HF_ACCESS_TOKEN - try: - await volume_downloader.download(self.volume_mount_dir) - except VolumeDownloadFailed as exc: - logger.error(f"Failed to download model from Hugging Face: {exc}") - raise JobError( - str(exc), - V0JobFailedRequest.ErrorType.HUGGINGFACE_DOWNLOAD, - exc.error_detail, - ) from exc + # Check if volume manager is configured + if self.volume_manager_client: + await self._prepare_volume_with_manager(volume) else: - try: - await volume_downloader.download(self.volume_mount_dir) - except VolumeDownloadFailed as exc: - raise JobError(str(exc)) from exc + await self._download_volume_directly(volume) chmod_proc = await asyncio.create_subprocess_exec( "chmod", "-R", "777", self.temp_dir.as_posix() ) assert 0 == await chmod_proc.wait() + async def _prepare_volume_with_manager(self, volume: Volume): + """Use volume manager to prepare the volume.""" + assert self.initial_job_request is not None + assert self.full_job_request is not None + assert self.volume_manager_client is not None + + job_uuid = self.initial_job_request.job_uuid + + # Prepare job metadata + job_metadata = { + "image": self.full_job_request.docker_image, + "consumer_key": self.initial_job_request.executor_class, # Using executor_class as consumer_key + "namespace": "SN" + job_uuid[:8] # Create a namespace from job UUID + } + + try: + logger.debug(f"Requesting volume preparation from Volume Manager for job {job_uuid}") + response = await self.volume_manager_client.prepare_volume( + job_uuid=job_uuid, + volume=volume, + job_metadata=job_metadata + ) + + # Store the mounts for later use in Docker command + self.volume_manager_mounts = response.mounts + logger.debug(f"Volume Manager provided {len(response.mounts)} mounts") + + except VolumeManagerError as exc: + logger.warning(f"Volume Manager failed to prepare volume: {exc}") + logger.info("Falling back to direct volume download") + + # Fallback to direct download + await self._download_volume_directly(volume) + + async def _download_volume_directly(self, volume: Volume): + """Traditional volume download approach.""" + # TODO(mlech): Refactor this to not treat `HuggingfaceVolume` with a special care + volume_downloader = VolumeDownloader.for_volume(volume) + volume_downloader.max_size_bytes = settings.VOLUME_MAX_SIZE_BYTES + if volume_downloader.handles_volume_type() is HuggingfaceVolume: + if volume.token is None: + volume.token = settings.HF_ACCESS_TOKEN + try: + await volume_downloader.download(self.volume_mount_dir) + except VolumeDownloadFailed as exc: + logger.error(f"Failed to download model from Hugging Face: {exc}") + raise JobError( + str(exc), + V0JobFailedRequest.ErrorType.HUGGINGFACE_DOWNLOAD, + exc.error_detail, + ) from exc + else: + try: + await volume_downloader.download(self.volume_mount_dir) + except VolumeDownloadFailed as exc: + raise JobError(str(exc)) from exc + async def get_job_volume(self) -> Volume | None: assert self.full_job_request is not None, ( "Full job request must be set. Call prepare_full() first." diff --git a/executor/app/src/compute_horde_executor/settings.py b/executor/app/src/compute_horde_executor/settings.py index 1e81b937c..5f9e11b23 100644 --- a/executor/app/src/compute_horde_executor/settings.py +++ b/executor/app/src/compute_horde_executor/settings.py @@ -296,6 +296,9 @@ def wrapped(*args, **kwargs): "OUTPUT_ZIP_UPLOAD_MAX_SIZE_BYTES", default=2147483648 ) # 2GB +# Volume Manager Configuration +VOLUME_MANAGER_ADDRESS = env.str("COMPUTE_HORDE_VOLUME_MANAGER_ADDRESS", default=None) + # Sentry if SENTRY_DSN := env("SENTRY_DSN", default=""): import sentry_sdk diff --git a/executor/pyproject.toml b/executor/pyproject.toml index d77b2e261..b6207ba47 100644 --- a/executor/pyproject.toml +++ b/executor/pyproject.toml @@ -27,6 +27,7 @@ dependencies = [ "django-business-metrics @ git+https://github.com/reef-technologies/django-business-metrics.git@9d08ddb3a9d26e8a7e478110d7c8c34c3aa03a01", "packaging>=24.2", "aiohttp>=3.12.0", + "httpx>=0.26.0", ] [tool.uv.sources] diff --git a/executor/uv.lock b/executor/uv.lock index fec17c3c3..c1f456bab 100644 --- a/executor/uv.lock +++ b/executor/uv.lock @@ -873,6 +873,7 @@ dependencies = [ { name = "django-prometheus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "flower", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "gunicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "ipython", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "nox", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, @@ -930,6 +931,7 @@ requires-dist = [ { name = "django-prometheus", specifier = "==2.3.1" }, { name = "flower", specifier = "~=2.0.0" }, { name = "gunicorn", specifier = "==20.1.0" }, + { name = "httpx", specifier = ">=0.26.0" }, { name = "ipython", specifier = "==8.14.0" }, { name = "nox", specifier = "==2025.5.1" }, { name = "packaging", specifier = ">=24.2" }, From de97e933705d8ccd5eed8209f0246524c044bbf0 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 09:00:27 -0300 Subject: [PATCH 02/25] Add tests for Volume Manager integration --- compute_horde_sdk/tests/unit/api/test_sdk.py | 3 + .../tests/unit/core/test_volume.py | 231 ++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/compute_horde_sdk/tests/unit/api/test_sdk.py b/compute_horde_sdk/tests/unit/api/test_sdk.py index 5e80aaa15..6bbcb235b 100644 --- a/compute_horde_sdk/tests/unit/api/test_sdk.py +++ b/compute_horde_sdk/tests/unit/api/test_sdk.py @@ -379,16 +379,19 @@ async def test_create_job(apiver_module, compute_horde_client, httpx_mock): "revision": None, "allow_patterns": None, "token": None, + "usage_type": None, }, { "volume_type": "inline", "relative_path": "version.txt", "contents": "dmVyc2lvbj0y", + "usage_type": None, }, { "volume_type": "single_file", "relative_path": "dataset.json", "url": "https://s3.aws.something.com/mybucket/myfile.json", + "usage_type": None, }, ] assert req_json["uploads"] == [ diff --git a/compute_horde_sdk/tests/unit/core/test_volume.py b/compute_horde_sdk/tests/unit/core/test_volume.py index 7444039d9..fd6d151f9 100644 --- a/compute_horde_sdk/tests/unit/core/test_volume.py +++ b/compute_horde_sdk/tests/unit/core/test_volume.py @@ -1,6 +1,8 @@ import asyncio import base64 import io +import json +import os import pathlib import zipfile from unittest import mock @@ -21,6 +23,12 @@ VolumeDownloadFailed, ZipUrlVolume, ZipUrlVolumeDownloader, + VolumeManagerClient, + VolumeManagerError, + VolumeManagerMount, + VolumeManagerResponse, + create_volume_manager_client, + get_volume_manager_headers, ) @@ -489,3 +497,226 @@ async def test_empty_volumes(self, tmp_path): # No files should have been created assert len(list(tmp_path.iterdir())) == 0 + + +class TestGetVolumeManagerHeaders: + """Test the get_volume_manager_headers function.""" + + def test_no_headers(self): + """Test when no volume manager headers are set.""" + with mock.patch.dict(os.environ, {}, clear=True): + headers = get_volume_manager_headers() + assert headers == {} + + def test_with_headers(self): + """Test when volume manager headers are set.""" + env_vars = { + "COMPUTE_HORDE_VOLUME_MANAGER_HEADER_Authorization": "Bearer token123", + "COMPUTE_HORDE_VOLUME_MANAGER_HEADER_X-Custom-Header": "custom-value", + "COMPUTE_HORDE_OTHER_ENV_VAR": "should-be-ignored", + } + + with mock.patch.dict(os.environ, env_vars, clear=True): + headers = get_volume_manager_headers() + expected = { + "Authorization": "Bearer token123", + "X-Custom-Header": "custom-value", + } + assert headers == expected + + +class TestVolumeManagerClient: + """Test the VolumeManagerClient class.""" + + @pytest.fixture + def client(self): + """Create a VolumeManagerClient instance for testing.""" + return VolumeManagerClient("http://localhost:8080", {"Authorization": "Bearer token"}) + + @pytest.fixture + def huggingface_volume(self): + """Create a test HuggingfaceVolume.""" + return HuggingfaceVolume( + repo_id="test/repo", + revision="main", + repo_type="model", + allow_patterns=["*.json"], + relative_path="models", + token="test_token", + ) + + + @pytest.mark.asyncio + async def test_prepare_volume_success(self, client, huggingface_volume): + """Test successful prepare_volume call.""" + mock_response = { + "mounts": [ + { + "type": "bind", + "source": "/host/path", + "target": "/container/path" + } + ] + } + + with mock.patch.object(client, '_make_request') as mock_make_request: + mock_make_request.return_value = mock_response + + result = await client.prepare_volume( + job_uuid="test-job-123", + volume=huggingface_volume, + job_metadata={"image": "test-image"} + ) + + assert isinstance(result, VolumeManagerResponse) + assert len(result.mounts) == 1 + assert result.mounts[0].type == "bind" + + @pytest.mark.asyncio + async def test_job_finished_success(self, client): + """Test successful job_finished call.""" + with mock.patch.object(client, '_make_request') as mock_make_request: + await client.job_finished("test-job-123") + + mock_make_request.assert_called_once() + call_args = mock_make_request.call_args + assert call_args[0][0] == "http://localhost:8080/job_finished" + assert call_args[0][1] == {"job_uuid": "test-job-123"} + + + @pytest.mark.asyncio + async def test_error_handling(self, client, huggingface_volume): + """Test error handling for both prepare_volume and job_finished.""" + # Mock HTTP error + mock_response = mock.MagicMock() + mock_response.status_code = 500 + mock_response.json.return_value = {"error": "Server error"} + + http_error = httpx.HTTPStatusError("500 Internal Server Error", request=None, response=mock_response) + + with mock.patch('httpx.AsyncClient') as mock_client_class: + mock_client = mock.AsyncMock() + mock_client_class.return_value.__aenter__.return_value = mock_client + mock_client.post.side_effect = http_error + + # Test prepare_volume error + with pytest.raises(VolumeManagerError) as exc_info: + await client.prepare_volume( + job_uuid="test-job-123", + volume=huggingface_volume, + job_metadata={"image": "test-image"} + ) + + error = exc_info.value + assert "Volume Manager prepare_volume returned status 500" in error.description + assert error.error_detail == "Server error" + + # Test job_finished error + with pytest.raises(VolumeManagerError) as exc_info: + await client.job_finished("test-job-123") + + error = exc_info.value + assert "Volume Manager job_finished returned status 500" in error.description + + @pytest.mark.asyncio + async def test_prepare_volume_with_different_volume_types(self, client): + """Test prepare_volume with different volume types.""" + # Test with InlineVolume + inline_volume = InlineVolume( + contents="dGVzdCBjb250ZW50", # base64 encoded "test content" + relative_path="data" + ) + + mock_response = { + "mounts": [ + { + "type": "bind", + "source": "/tmp/inline-data", + "target": "/volume/data" + } + ] + } + + with mock.patch.object(client, '_make_request') as mock_make_request: + mock_make_request.return_value = mock_response + + result = await client.prepare_volume( + job_uuid="test-job-456", + volume=inline_volume, + job_metadata={"image": "test-image"} + ) + + assert isinstance(result, VolumeManagerResponse) + assert len(result.mounts) == 1 + assert result.mounts[0].type == "bind" + + @pytest.mark.asyncio + async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_volume): + """Test prepare_volume with multiple mounts in response.""" + mock_response = { + "mounts": [ + { + "type": "bind", + "source": "/host/models", + "target": "/volume/models" + }, + { + "type": "volume", + "source": "cache-volume", + "target": "/cache" + }, + { + "type": "tmpfs", + "source": "", + "target": "/tmp" + } + ] + } + + with mock.patch.object(client, '_make_request') as mock_make_request: + mock_make_request.return_value = mock_response + + result = await client.prepare_volume( + job_uuid="test-job-789", + volume=huggingface_volume, + job_metadata={"image": "test-image"} + ) + + assert isinstance(result, VolumeManagerResponse) + assert len(result.mounts) == 3 + + # Check first mount + assert result.mounts[0].type == "bind" + assert result.mounts[0].source == "/host/models" + assert result.mounts[0].target == "/volume/models" + + # Check second mount + assert result.mounts[1].type == "volume" + assert result.mounts[1].source == "cache-volume" + assert result.mounts[1].target == "/cache" + + # Check third mount + assert result.mounts[2].type == "tmpfs" + assert result.mounts[2].source == "" + assert result.mounts[2].target == "/tmp" + + +class TestCreateVolumeManagerClient: + """Test the create_volume_manager_client function.""" + + def test_create_volume_manager_client_with_headers(self): + """Test creating a client with headers.""" + headers = {"Authorization": "Bearer token"} + client = create_volume_manager_client("http://localhost:8080", headers) + + assert isinstance(client, VolumeManagerClient) + assert client.base_url == "http://localhost:8080" + assert client.headers == headers + + def test_create_volume_manager_client_no_headers(self): + """Test creating a client without headers.""" + client = create_volume_manager_client("http://localhost:8080") + + assert isinstance(client, VolumeManagerClient) + assert client.base_url == "http://localhost:8080" + assert client.headers == {} \ No newline at end of file From 66342169944a6c5a0c755af3b0b5b4f6108abdd2 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 09:18:30 -0300 Subject: [PATCH 03/25] Add documentation for Custom Volume Managers in README.md --- compute_horde_sdk/README.md | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/compute_horde_sdk/README.md b/compute_horde_sdk/README.md index 3caecde73..75b4f00fd 100644 --- a/compute_horde_sdk/README.md +++ b/compute_horde_sdk/README.md @@ -247,6 +247,90 @@ The SDK includes an optional `FallbackClient` that mirrors the standard `Compute See the [Fallback docs](https://sdk.computehorde.io/master/fallback.html) for usage examples and limitations. +## 🔧 Custom Volume Managers + +By default, executors download all input volumes before running a job. Miners can implement their own volume manager to handle volume preparation more efficiently (e.g., caching, custom storage strategies). + +### How It Works + +When an executor has a volume manager configured, it delegates volume preparation to the volume manager instead of downloading volumes directly: + +1. Executor sends volume specifications to volume manager +2. Volume manager returns Docker mount options +3. Executor uses those mounts when running the job container +4. Executor notifies volume manager when job completes + +### Configuration + +Set the volume manager address as an environment variable: + +```bash +export COMPUTE_HORDE_VOLUME_MANAGER_ADDRESS="http://localhost:8080" +``` + +Add custom headers for authentication: + +```bash +export COMPUTE_HORDE_VOLUME_MANAGER_HEADER_Authorization="Bearer your-token" +export COMPUTE_HORDE_VOLUME_MANAGER_HEADER_X-Custom-Header="custom-value" +``` + +### Volume Manager API + +Your volume manager must implement these endpoints: + +#### `POST /prepare_volume` + +**Request:** +```json +{ + "job_uuid": "7b522daa-e807-4094-8d96-99b9a863f960", + "volume": { + "volume_type": "huggingface_volume", + "repo_id": "example/model", + "revision": "main", + "relative_path": "models", + "usage_type": "reusable" + }, + "job_metadata": { + "image": "example/image:latest", + "consumer_key": "executor_class", + "namespace": "SN7b522da" + } +} +``` + +**Response:** +```json +{ + "mounts": [ + { + "type": "bind", + "source": "/host/path/to/cached/model", + "target": "/volume/models" + } + ] +} +``` + +#### `POST /job_finished` + +**Request:** +```json +{ + "job_uuid": "7b522daa-e807-4094-8d96-99b9a863f960" +} +``` + +**Response:** `200 OK` (no body required) + +### Volume Usage Hints + +The `usage_type` field helps volume managers optimize storage: + +- `single_use`: Volume is used once and can be cleaned up immediately +- `reusable`: Volume may be used multiple times (cache aggressively) + ## Versioning This package uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). From 491bdc33f3bdc5cc5412ea543a4cbc1ab8ca7961 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 11:33:53 -0300 Subject: [PATCH 04/25] Refactor job metadata preparation in JobRunner to use full_job_request info --- .../app/src/compute_horde_executor/executor/job_runner.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/executor/app/src/compute_horde_executor/executor/job_runner.py b/executor/app/src/compute_horde_executor/executor/job_runner.py index 4558a1f3b..ee763c076 100644 --- a/executor/app/src/compute_horde_executor/executor/job_runner.py +++ b/executor/app/src/compute_horde_executor/executor/job_runner.py @@ -516,11 +516,7 @@ async def _prepare_volume_with_manager(self, volume: Volume): job_uuid = self.initial_job_request.job_uuid # Prepare job metadata - job_metadata = { - "image": self.full_job_request.docker_image, - "consumer_key": self.initial_job_request.executor_class, # Using executor_class as consumer_key - "namespace": "SN" + job_uuid[:8] # Create a namespace from job UUID - } + job_metadata = self.full_job_request.model_dump() try: logger.debug(f"Requesting volume preparation from Volume Manager for job {job_uuid}") From 4b17de6178b6c854ab9313b3179a6d3bcd09f358 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 11:49:03 -0300 Subject: [PATCH 05/25] Remove httpx dependency from pyproject.toml and uv.lock files --- executor/pyproject.toml | 1 - executor/uv.lock | 2 -- 2 files changed, 3 deletions(-) diff --git a/executor/pyproject.toml b/executor/pyproject.toml index b6207ba47..d77b2e261 100644 --- a/executor/pyproject.toml +++ b/executor/pyproject.toml @@ -27,7 +27,6 @@ dependencies = [ "django-business-metrics @ git+https://github.com/reef-technologies/django-business-metrics.git@9d08ddb3a9d26e8a7e478110d7c8c34c3aa03a01", "packaging>=24.2", "aiohttp>=3.12.0", - "httpx>=0.26.0", ] [tool.uv.sources] diff --git a/executor/uv.lock b/executor/uv.lock index c1f456bab..fec17c3c3 100644 --- a/executor/uv.lock +++ b/executor/uv.lock @@ -873,7 +873,6 @@ dependencies = [ { name = "django-prometheus", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "flower", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "gunicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, - { name = "httpx", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "ipython", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "nox", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, @@ -931,7 +930,6 @@ requires-dist = [ { name = "django-prometheus", specifier = "==2.3.1" }, { name = "flower", specifier = "~=2.0.0" }, { name = "gunicorn", specifier = "==20.1.0" }, - { name = "httpx", specifier = ">=0.26.0" }, { name = "ipython", specifier = "==8.14.0" }, { name = "nox", specifier = "==2025.5.1" }, { name = "packaging", specifier = ">=24.2" }, From 5014b8fa9f85845eeb371feae0c1264f54eb97eb Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 14 Jul 2025 11:57:03 -0300 Subject: [PATCH 06/25] Update job metadata structure in README.md to include new fields for job requests and output handling --- compute_horde_sdk/README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compute_horde_sdk/README.md b/compute_horde_sdk/README.md index 75b4f00fd..100f56130 100644 --- a/compute_horde_sdk/README.md +++ b/compute_horde_sdk/README.md @@ -293,9 +293,20 @@ Your volume manager must implement these endpoints: "usage_type": "reusable" }, "job_metadata": { - "image": "example/image:latest", - "consumer_key": "executor_class", - "namespace": "SN7b522da" + "message_type": "V0JobRequest", + "job_uuid": "7b522daa-e807-4094-8d96-99b9a863f960", + "executor_class": "always_on__llm__a6000", + "docker_image": "example/image:latest", + "raw_script": null, + "docker_run_options_preset": "nvidia_all", + "docker_run_cmd": ["python", "main.py"], + "volume": null, + "output_upload": { + "output_upload_type": "single_file_put", + "relative_path": "results.json", + "url": "https://s3.example.com/results.json" + }, + "artifacts_dir": "/artifacts" } } ``` From 88890a991000d6fd47f60de736b40146a0cec7e8 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 16 Jul 2025 17:58:05 -0300 Subject: [PATCH 07/25] Update environment variable names for Volume Manager in README.md and settings.py; enhance logging in job_runner.py for volume preparation errors --- compute_horde_sdk/README.md | 6 +++--- .../app/src/compute_horde_executor/executor/job_runner.py | 2 +- executor/app/src/compute_horde_executor/settings.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compute_horde_sdk/README.md b/compute_horde_sdk/README.md index 100f56130..264d40073 100644 --- a/compute_horde_sdk/README.md +++ b/compute_horde_sdk/README.md @@ -265,14 +265,14 @@ When an executor has a volume manager configured, it delegates volume preparatio Set the volume manager address as an environment variable: ```bash -export COMPUTE_HORDE_VOLUME_MANAGER_ADDRESS="http://localhost:8080" +export VOLUME_MANAGER_ADDRESS="http://localhost:8080" ``` Add custom headers for authentication: ```bash -export COMPUTE_HORDE_VOLUME_MANAGER_HEADER_Authorization="Bearer your-token" -export COMPUTE_HORDE_VOLUME_MANAGER_HEADER_X-Custom-Header="custom-value" +export VOLUME_MANAGER_HEADER_Authorization="Bearer your-token" +export VOLUME_MANAGER_HEADER_X-Custom-Header="custom-value" ``` ### Volume Manager API diff --git a/executor/app/src/compute_horde_executor/executor/job_runner.py b/executor/app/src/compute_horde_executor/executor/job_runner.py index ee763c076..75af86eb1 100644 --- a/executor/app/src/compute_horde_executor/executor/job_runner.py +++ b/executor/app/src/compute_horde_executor/executor/job_runner.py @@ -531,7 +531,7 @@ async def _prepare_volume_with_manager(self, volume: Volume): logger.debug(f"Volume Manager provided {len(response.mounts)} mounts") except VolumeManagerError as exc: - logger.warning(f"Volume Manager failed to prepare volume: {exc}") + logger.warning(f"Volume Manager failed to prepare volume for job {job_uuid}: {exc}") logger.info("Falling back to direct volume download") # Fallback to direct download diff --git a/executor/app/src/compute_horde_executor/settings.py b/executor/app/src/compute_horde_executor/settings.py index 5f9e11b23..eb230f826 100644 --- a/executor/app/src/compute_horde_executor/settings.py +++ b/executor/app/src/compute_horde_executor/settings.py @@ -297,7 +297,7 @@ def wrapped(*args, **kwargs): ) # 2GB # Volume Manager Configuration -VOLUME_MANAGER_ADDRESS = env.str("COMPUTE_HORDE_VOLUME_MANAGER_ADDRESS", default=None) +VOLUME_MANAGER_ADDRESS = env.str("VOLUME_MANAGER_ADDRESS", default=None) # Sentry if SENTRY_DSN := env("SENTRY_DSN", default=""): From 2beb94db34cf9083009bd48e162080e8e7edc37c Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 21 Jul 2025 11:08:32 -0300 Subject: [PATCH 08/25] Add new test for Huggingface model and volume manager integration --- local_stack/prepare.sh | 2 + local_stack/send_huggingface_job.py | 74 +++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 local_stack/send_huggingface_job.py diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index 69e265074..ae734c79c 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -61,6 +61,8 @@ PROJECT_ROOT=$(git rev-parse --show-toplevel) cd $PROJECT_ROOT/executor ./setup-dev.sh update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" +# Use for volume manager testing, not a requirement for the local stack setup +update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner diff --git a/local_stack/send_huggingface_job.py b/local_stack/send_huggingface_job.py new file mode 100644 index 000000000..4b905e28f --- /dev/null +++ b/local_stack/send_huggingface_job.py @@ -0,0 +1,74 @@ +import asyncio +import pathlib +import os +import logging + +import bittensor +from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec +from compute_horde_sdk.v1 import ( + ComputeHordeClient, + ExecutorClass, + HuggingfaceInputVolume, +) + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Initialize wallet and Compute Horde client. +wallet = bittensor.wallet( + name="validator", + hotkey="default", + path=(pathlib.Path(__file__).parent / "wallets").as_posix(), +) +compute_horde_client = ComputeHordeClient( + hotkey=wallet.hotkey, + compute_horde_validator_hotkey=wallet.hotkey.ss58_address, + facilitator_url="http://localhost:9000", +) + + +async def main() -> None: + os.system("docker pull python:3.11-slim") + + # Create a job spec that downloads a small Huggingface model + compute_horde_job_spec = ComputeHordeJobSpec( + executor_class=ExecutorClass.always_on__llm__a6000, + job_namespace="SN123.0", + docker_image="python:3.11-slim", + args=[ + "bash", + "-c", + "echo 'Hello, world!' && echo 'Job completed successfully' > /output/result.txt", + ], + artifacts_dir="/artifacts", + input_volumes={ + "/volume/model": HuggingfaceInputVolume( + repo_id="prajjwal1/bert-tiny", + repo_type="model", + # usage_type="reusable", + ), + }, + download_time_limit_sec=60, + execution_time_limit_sec=30, + upload_time_limit_sec=10, + ) + + # Create and submit the job + logger.info("Creating job...") + job = await compute_horde_client.create_job(compute_horde_job_spec) + logger.info("Waiting for job completion...") + await job.wait(timeout=10 * 60) + + # Validate job completion and output + if job.status != "Completed": + logger.error(f"Job failed with status: {job.status}") + if hasattr(job.result, "stderr"): + logger.error(f"Job stderr: {job.result.stderr}") + raise RuntimeError(f"Job failed: status={job.status}") + + logger.info(f"Job completed successfully! Artifacts: {job.result.artifacts}") + + +if __name__ == "__main__": + asyncio.run(main()) From 244455c627ae3f74fbb0b7849a36cb7a594fcfaf Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Mon, 21 Jul 2025 11:10:11 -0300 Subject: [PATCH 09/25] ruff check & format changes --- .../src/compute_horde_core/volume/__init__.py | 6 +- .../src/compute_horde_core/volume/_manager.py | 28 ++-- .../src/compute_horde_core/volume/_models.py | 5 +- .../tests/unit/core/test_volume.py | 122 ++++++------------ .../executor/job_runner.py | 30 ++--- local_stack/fallback_job/send_fallback_job.py | 38 ++++-- .../send_fallback_job_streaming.py | 18 ++- local_stack/send_hello_world_job.py | 100 ++++++++------ local_stack/uv.lock | 2 +- .../test_miner_on_dev_executor_manager.py | 17 ++- 10 files changed, 189 insertions(+), 177 deletions(-) diff --git a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py index 8b8b4208d..7252bae1f 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py @@ -7,12 +7,12 @@ VolumeDownloadFailed, ZipUrlVolumeDownloader, ) -from ._models import * from ._manager import ( VolumeManagerClient, VolumeManagerError, - VolumeManagerResponse, VolumeManagerMount, - get_volume_manager_headers, + VolumeManagerResponse, create_volume_manager_client, + get_volume_manager_headers, ) +from ._models import * diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py index 0a0947ac4..63ea9c040 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py @@ -8,6 +8,7 @@ import httpx import tenacity + from ._models import Volume logger = logging.getLogger(__name__) @@ -76,6 +77,7 @@ async def prepare_volume( Raises: VolumeManagerError: If the request fails + """ url = f"{self.base_url}/prepare_volume" volume_data = volume.model_dump() @@ -94,6 +96,7 @@ async def job_finished(self, job_uuid: str) -> None: Raises: VolumeManagerError: If the notification fails + """ url = f"{self.base_url}/job_finished" payload = {"job_uuid": job_uuid} @@ -106,35 +109,31 @@ async def job_finished(self, job_uuid: str) -> None: retry=tenacity.retry_if_exception_type((httpx.RequestError, httpx.HTTPStatusError)), ) async def _make_request( - self, - url: str, - payload: dict[str, Any], - operation: str, - timeout: float = 300.0 + self, url: str, payload: dict[str, Any], operation: str, timeout: float = 300.0 ) -> dict[str, Any]: """ Make a POST request to the Volume Manager with standardized error handling. - + Args: url: The endpoint URL payload: The JSON payload to send operation: Operation name for error messages timeout: Request timeout in seconds - + Returns: Parsed JSON response - + Raises: VolumeManagerError: If the request fails or response is invalid + """ logger.debug(f"Making {operation} request to Volume Manager at {url}") - logger.debug(f"Request payload: {json.dumps(payload, indent=2)}") try: async with httpx.AsyncClient(timeout=timeout) as client: response = await client.post(url, json=payload, headers=self.headers) response.raise_for_status() - + logger.debug(f"Volume Manager {operation} response status: {response.status_code}") logger.debug(f"Volume Manager {operation} response: {response.text}") @@ -144,7 +143,7 @@ async def _make_request( # Handle non-retryable status codes error_msg = f"Volume Manager {operation} returned status {e.response.status_code}" error_detail = None - + try: error_data = e.response.json() if "error" in error_data: @@ -160,13 +159,14 @@ async def _make_request( def create_volume_manager_client(base_url: str, headers: dict[str, str] | None = None) -> VolumeManagerClient: """ Create a Volume Manager client with the specified configuration. - + Args: base_url: The base URL of the Volume Manager service headers: Optional headers to include in requests - + Returns: A configured VolumeManagerClient instance + """ logger.debug(f"Volume manager created at {base_url}") - return VolumeManagerClient(base_url=base_url, headers=headers) \ No newline at end of file + return VolumeManagerClient(base_url=base_url, headers=headers) diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_models.py b/compute_horde_sdk/src/compute_horde_core/volume/_models.py index 5697f751b..dd1504b3f 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_models.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_models.py @@ -23,9 +23,10 @@ def __str__(self) -> str: class VolumeUsageType(str, enum.Enum): """Hint for volume managers about expected usage patterns.""" + single_use = "single_use" # Volume is expected to be used just once - reusable = "reusable" # Volume is expected to be used many times - + reusable = "reusable" # Volume is expected to be used many times + def __str__(self) -> str: return str.__str__(self) diff --git a/compute_horde_sdk/tests/unit/core/test_volume.py b/compute_horde_sdk/tests/unit/core/test_volume.py index fd6d151f9..5720d89e7 100644 --- a/compute_horde_sdk/tests/unit/core/test_volume.py +++ b/compute_horde_sdk/tests/unit/core/test_volume.py @@ -1,7 +1,6 @@ import asyncio import base64 import io -import json import os import pathlib import zipfile @@ -21,12 +20,11 @@ SingleFileVolumeDownloader, VolumeDownloader, VolumeDownloadFailed, - ZipUrlVolume, - ZipUrlVolumeDownloader, VolumeManagerClient, VolumeManagerError, - VolumeManagerMount, VolumeManagerResponse, + ZipUrlVolume, + ZipUrlVolumeDownloader, create_volume_manager_client, get_volume_manager_headers, ) @@ -515,7 +513,7 @@ def test_with_headers(self): "COMPUTE_HORDE_VOLUME_MANAGER_HEADER_X-Custom-Header": "custom-value", "COMPUTE_HORDE_OTHER_ENV_VAR": "should-be-ignored", } - + with mock.patch.dict(os.environ, env_vars, clear=True): headers = get_volume_manager_headers() expected = { @@ -545,29 +543,18 @@ def huggingface_volume(self): token="test_token", ) - @pytest.mark.asyncio async def test_prepare_volume_success(self, client, huggingface_volume): """Test successful prepare_volume call.""" - mock_response = { - "mounts": [ - { - "type": "bind", - "source": "/host/path", - "target": "/container/path" - } - ] - } - - with mock.patch.object(client, '_make_request') as mock_make_request: + mock_response = {"mounts": [{"type": "bind", "source": "/host/path", "target": "/container/path"}]} + + with mock.patch.object(client, "_make_request") as mock_make_request: mock_make_request.return_value = mock_response - + result = await client.prepare_volume( - job_uuid="test-job-123", - volume=huggingface_volume, - job_metadata={"image": "test-image"} + job_uuid="test-job-123", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - + assert isinstance(result, VolumeManagerResponse) assert len(result.mounts) == 1 assert result.mounts[0].type == "bind" @@ -575,15 +562,14 @@ async def test_prepare_volume_success(self, client, huggingface_volume): @pytest.mark.asyncio async def test_job_finished_success(self, client): """Test successful job_finished call.""" - with mock.patch.object(client, '_make_request') as mock_make_request: + with mock.patch.object(client, "_make_request") as mock_make_request: await client.job_finished("test-job-123") - + mock_make_request.assert_called_once() call_args = mock_make_request.call_args assert call_args[0][0] == "http://localhost:8080/job_finished" assert call_args[0][1] == {"job_uuid": "test-job-123"} - @pytest.mark.asyncio async def test_error_handling(self, client, huggingface_volume): """Test error handling for both prepare_volume and job_finished.""" @@ -591,30 +577,28 @@ async def test_error_handling(self, client, huggingface_volume): mock_response = mock.MagicMock() mock_response.status_code = 500 mock_response.json.return_value = {"error": "Server error"} - + http_error = httpx.HTTPStatusError("500 Internal Server Error", request=None, response=mock_response) - - with mock.patch('httpx.AsyncClient') as mock_client_class: + + with mock.patch("httpx.AsyncClient") as mock_client_class: mock_client = mock.AsyncMock() mock_client_class.return_value.__aenter__.return_value = mock_client mock_client.post.side_effect = http_error - + # Test prepare_volume error with pytest.raises(VolumeManagerError) as exc_info: await client.prepare_volume( - job_uuid="test-job-123", - volume=huggingface_volume, - job_metadata={"image": "test-image"} + job_uuid="test-job-123", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - + error = exc_info.value assert "Volume Manager prepare_volume returned status 500" in error.description assert error.error_detail == "Server error" - + # Test job_finished error with pytest.raises(VolumeManagerError) as exc_info: await client.job_finished("test-job-123") - + error = exc_info.value assert "Volume Manager job_finished returned status 500" in error.description @@ -624,28 +608,18 @@ async def test_prepare_volume_with_different_volume_types(self, client): # Test with InlineVolume inline_volume = InlineVolume( contents="dGVzdCBjb250ZW50", # base64 encoded "test content" - relative_path="data" + relative_path="data", ) - - mock_response = { - "mounts": [ - { - "type": "bind", - "source": "/tmp/inline-data", - "target": "/volume/data" - } - ] - } - - with mock.patch.object(client, '_make_request') as mock_make_request: + + mock_response = {"mounts": [{"type": "bind", "source": "/tmp/inline-data", "target": "/volume/data"}]} + + with mock.patch.object(client, "_make_request") as mock_make_request: mock_make_request.return_value = mock_response - + result = await client.prepare_volume( - job_uuid="test-job-456", - volume=inline_volume, - job_metadata={"image": "test-image"} + job_uuid="test-job-456", volume=inline_volume, job_metadata={"image": "test-image"} ) - + assert isinstance(result, VolumeManagerResponse) assert len(result.mounts) == 1 assert result.mounts[0].type == "bind" @@ -655,46 +629,32 @@ async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_vol """Test prepare_volume with multiple mounts in response.""" mock_response = { "mounts": [ - { - "type": "bind", - "source": "/host/models", - "target": "/volume/models" - }, - { - "type": "volume", - "source": "cache-volume", - "target": "/cache" - }, - { - "type": "tmpfs", - "source": "", - "target": "/tmp" - } + {"type": "bind", "source": "/host/models", "target": "/volume/models"}, + {"type": "volume", "source": "cache-volume", "target": "/cache"}, + {"type": "tmpfs", "source": "", "target": "/tmp"}, ] } - - with mock.patch.object(client, '_make_request') as mock_make_request: + + with mock.patch.object(client, "_make_request") as mock_make_request: mock_make_request.return_value = mock_response - + result = await client.prepare_volume( - job_uuid="test-job-789", - volume=huggingface_volume, - job_metadata={"image": "test-image"} + job_uuid="test-job-789", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - + assert isinstance(result, VolumeManagerResponse) assert len(result.mounts) == 3 - + # Check first mount assert result.mounts[0].type == "bind" assert result.mounts[0].source == "/host/models" assert result.mounts[0].target == "/volume/models" - + # Check second mount assert result.mounts[1].type == "volume" assert result.mounts[1].source == "cache-volume" assert result.mounts[1].target == "/cache" - + # Check third mount assert result.mounts[2].type == "tmpfs" assert result.mounts[2].source == "" @@ -708,7 +668,7 @@ def test_create_volume_manager_client_with_headers(self): """Test creating a client with headers.""" headers = {"Authorization": "Bearer token"} client = create_volume_manager_client("http://localhost:8080", headers) - + assert isinstance(client, VolumeManagerClient) assert client.base_url == "http://localhost:8080" assert client.headers == headers @@ -716,7 +676,7 @@ def test_create_volume_manager_client_with_headers(self): def test_create_volume_manager_client_no_headers(self): """Test creating a client without headers.""" client = create_volume_manager_client("http://localhost:8080") - + assert isinstance(client, VolumeManagerClient) assert client.base_url == "http://localhost:8080" - assert client.headers == {} \ No newline at end of file + assert client.headers == {} diff --git a/executor/app/src/compute_horde_executor/executor/job_runner.py b/executor/app/src/compute_horde_executor/executor/job_runner.py index 75af86eb1..b20859710 100644 --- a/executor/app/src/compute_horde_executor/executor/job_runner.py +++ b/executor/app/src/compute_horde_executor/executor/job_runner.py @@ -26,18 +26,16 @@ Volume, VolumeDownloader, VolumeDownloadFailed, -) -from django.conf import settings - -from compute_horde_executor.executor.miner_client import ExecutionResult, JobError, JobResult -from compute_horde_executor.executor.utils import temporary_process -from compute_horde_core.volume import ( - create_volume_manager_client, VolumeManagerClient, VolumeManagerError, VolumeManagerMount, + create_volume_manager_client, get_volume_manager_headers, ) +from django.conf import settings + +from compute_horde_executor.executor.miner_client import ExecutionResult, JobError, JobResult +from compute_horde_executor.executor.utils import temporary_process logger = logging.getLogger(__name__) @@ -147,8 +145,10 @@ def __init__(self): self.volume_manager_client: VolumeManagerClient | None = None if settings.VOLUME_MANAGER_ADDRESS: headers = get_volume_manager_headers() - self.volume_manager_client = create_volume_manager_client(settings.VOLUME_MANAGER_ADDRESS, headers) - + self.volume_manager_client = create_volume_manager_client( + settings.VOLUME_MANAGER_ADDRESS, headers + ) + # Track volume manager mounts for cleanup self.volume_manager_mounts: list[VolumeManagerMount] = [] @@ -514,26 +514,24 @@ async def _prepare_volume_with_manager(self, volume: Volume): assert self.volume_manager_client is not None job_uuid = self.initial_job_request.job_uuid - + # Prepare job metadata job_metadata = self.full_job_request.model_dump() try: logger.debug(f"Requesting volume preparation from Volume Manager for job {job_uuid}") response = await self.volume_manager_client.prepare_volume( - job_uuid=job_uuid, - volume=volume, - job_metadata=job_metadata + job_uuid=job_uuid, volume=volume, job_metadata=job_metadata ) - + # Store the mounts for later use in Docker command self.volume_manager_mounts = response.mounts logger.debug(f"Volume Manager provided {len(response.mounts)} mounts") - + except VolumeManagerError as exc: logger.warning(f"Volume Manager failed to prepare volume for job {job_uuid}: {exc}") logger.info("Falling back to direct volume download") - + # Fallback to direct download await self._download_volume_directly(volume) diff --git a/local_stack/fallback_job/send_fallback_job.py b/local_stack/fallback_job/send_fallback_job.py index 221176eca..7d866c9b3 100644 --- a/local_stack/fallback_job/send_fallback_job.py +++ b/local_stack/fallback_job/send_fallback_job.py @@ -8,7 +8,11 @@ from compute_horde_sdk._internal.fallback.job import FallbackJobSpec from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec from compute_horde_sdk.v1 import ExecutorClass -from compute_horde_sdk._internal.models import InlineInputVolume, HTTPOutputVolume, HTTPInputVolume +from compute_horde_sdk._internal.models import ( + InlineInputVolume, + HTTPOutputVolume, + HTTPInputVolume, +) logging.basicConfig(level=logging.DEBUG) @@ -16,29 +20,37 @@ INPUT_FILE_CONTENT = b"This is the input file content.\n" -def get_presigned_urls(bucket: str, post_object_key: str, put_object_key: str, expires_in: int = 3600): + +def get_presigned_urls( + bucket: str, post_object_key: str, put_object_key: str, expires_in: int = 3600 +): """ Generate presigned POST and PUT URLs for the given S3 bucket and object keys. """ - s3_client = boto3.client('s3') - presigned_post = s3_client.generate_presigned_post(Bucket=bucket, Key=post_object_key, ExpiresIn=expires_in) + s3_client = boto3.client("s3") + presigned_post = s3_client.generate_presigned_post( + Bucket=bucket, Key=post_object_key, ExpiresIn=expires_in + ) presigned_put = s3_client.generate_presigned_url( "put_object", Params={"Bucket": bucket, "Key": put_object_key}, - ExpiresIn=expires_in + ExpiresIn=expires_in, ) if not presigned_post: raise RuntimeError("Failed to generate presigned POST URL") return presigned_post, presigned_put + async def main(): bucket_name = os.environ.get("S3_BUCKET_NAME", "compute-horde-integration-tests") post_object_key = f"{uuid.uuid4().hex}_output_post.txt" put_object_key = f"{uuid.uuid4().hex}_output_put.txt" - presigned_post, presigned_put = get_presigned_urls(bucket_name, post_object_key, put_object_key) + presigned_post, presigned_put = get_presigned_urls( + bucket_name, post_object_key, put_object_key + ) logger.info(f"Presigned POST: {presigned_post}") - logger.info(f"Presigned PUT URL: {presigned_put}") + logger.info(f"Presigned PUT URL: {presigned_put}") compute_horde_job_spec = ComputeHordeJobSpec( executor_class=ExecutorClass.spin_up_4min__gpu_24gb, @@ -55,8 +67,7 @@ async def main(): ], input_volumes={ "/volume/": InlineInputVolume.from_file_contents( - filename="input.txt", - contents=INPUT_FILE_CONTENT + filename="input.txt", contents=INPUT_FILE_CONTENT ), "/volume/data/dataset.json": HTTPInputVolume( url="https://jsonplaceholder.typicode.com/todos/1", @@ -80,8 +91,8 @@ async def main(): ) fallback_job_spec = FallbackJobSpec.from_job_spec( - compute_horde_job_spec, - work_dir="/output") + compute_horde_job_spec, work_dir="/output" + ) with FallbackClient(cloud="runpod", idle_minutes=1) as fallback_client: job = await fallback_client.create_job(fallback_job_spec) @@ -92,7 +103,9 @@ async def main(): # Verification step expected_content = b"Read from input: " + INPUT_FILE_CONTENT if not len(job.result.artifacts) == 2: - raise RuntimeError("Expected to find 2 artifacts, but found %d", len(job.result.artifacts)) + raise RuntimeError( + "Expected to find 2 artifacts, but found %d", len(job.result.artifacts) + ) for _, content in job.result.artifacts.items(): try: decoded = base64.b64decode(content) @@ -104,5 +117,6 @@ async def main(): ) logger.info("Success!") + if __name__ == "__main__": asyncio.run(main()) diff --git a/local_stack/fallback_job/send_fallback_job_streaming.py b/local_stack/fallback_job/send_fallback_job_streaming.py index 7a344f496..df20ce11c 100644 --- a/local_stack/fallback_job/send_fallback_job_streaming.py +++ b/local_stack/fallback_job/send_fallback_job_streaming.py @@ -30,6 +30,7 @@ def shutdown(): return {"message": "Server is shutting down."} """ + async def main(): compute_horde_job_spec = ComputeHordeJobSpec( executor_class=ExecutorClass.spin_up_4min__gpu_24gb, @@ -48,15 +49,13 @@ async def main(): streaming_start_time_limit_sec=10, input_volumes={ "/volume/": InlineInputVolume.from_file_contents( - "app.py", - STREAMING_SERVER_CODE.encode('utf-8') + "app.py", STREAMING_SERVER_CODE.encode("utf-8") ) - } + }, ) fallback_job_spec = FallbackJobSpec.from_job_spec( - compute_horde_job_spec, - work_dir="/" + compute_horde_job_spec, work_dir="/" ) with FallbackClient(cloud="runpod", idle_minutes=1) as fallback_client: @@ -76,7 +75,9 @@ async def main(): else: raise RuntimeError(f"[Fallback] Unexpected message content: {data}") else: - raise RuntimeError(f"[Fallback] Failed to get /message: {resp.status_code}") + raise RuntimeError( + f"[Fallback] Failed to get /message: {resp.status_code}" + ) # Terminate the server terminate_url = f"{url}/terminate" @@ -85,10 +86,13 @@ async def main(): if resp.status_code == 200: logger.info("[Fallback] Server terminated successfully") else: - raise RuntimeError(f"[Fallback] Failed to terminate server: {resp.status_code}") + raise RuntimeError( + f"[Fallback] Failed to terminate server: {resp.status_code}" + ) await job.wait(timeout=60) logger.info("Success!") + if __name__ == "__main__": asyncio.run(main()) diff --git a/local_stack/send_hello_world_job.py b/local_stack/send_hello_world_job.py index 391204cce..fec670b4e 100644 --- a/local_stack/send_hello_world_job.py +++ b/local_stack/send_hello_world_job.py @@ -26,7 +26,7 @@ wallet = bittensor.wallet( name="validator", hotkey="default", - path=(pathlib.Path(__file__).parent / "wallets").as_posix() + path=(pathlib.Path(__file__).parent / "wallets").as_posix(), ) compute_horde_client = ComputeHordeClient( hotkey=wallet.hotkey, @@ -35,16 +35,22 @@ ) -def get_presigned_urls(bucket: str, post_object_key: str, put_object_key: str, expires_in: int = 3600): +def get_presigned_urls( + bucket: str, post_object_key: str, put_object_key: str, expires_in: int = 3600 +): """ Generate presigned POST and PUT URLs for the given S3 bucket and object keys. """ - s3_client = boto3.client('s3', region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1")) - presigned_post = s3_client.generate_presigned_post(Bucket=bucket, Key=post_object_key, ExpiresIn=expires_in) + s3_client = boto3.client( + "s3", region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1") + ) + presigned_post = s3_client.generate_presigned_post( + Bucket=bucket, Key=post_object_key, ExpiresIn=expires_in + ) presigned_put = s3_client.generate_presigned_url( "put_object", Params={"Bucket": bucket, "Key": put_object_key}, - ExpiresIn=expires_in + ExpiresIn=expires_in, ) if not presigned_post: raise RuntimeError("Failed to generate presigned POST URL") @@ -52,25 +58,25 @@ def get_presigned_urls(bucket: str, post_object_key: str, put_object_key: str, e def create_mt_client_with_tempfiles( - client_cert: Certificate, - client_key: rsa.RSAPrivateKey, - server_cert_pem: str + client_cert: Certificate, client_key: rsa.RSAPrivateKey, server_cert_pem: str ) -> httpx.Client: # Serialize cert and key to PEM strings - cert_pem = client_cert.public_bytes( - encoding=serialization.Encoding.PEM - ).decode("utf-8") + cert_pem = client_cert.public_bytes(encoding=serialization.Encoding.PEM).decode( + "utf-8" + ) key_pem = client_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption() + encryption_algorithm=serialization.NoEncryption(), ).decode("utf-8") client = None - with tempfile.NamedTemporaryFile("w+", delete=False) as cert_file, \ - tempfile.NamedTemporaryFile("w+", delete=False) as key_file, \ - tempfile.NamedTemporaryFile("w+", delete=False) as ca_file: + with ( + tempfile.NamedTemporaryFile("w+", delete=False) as cert_file, + tempfile.NamedTemporaryFile("w+", delete=False) as key_file, + tempfile.NamedTemporaryFile("w+", delete=False) as ca_file, + ): cert_file.write(cert_pem) cert_file.flush() key_file.write(key_pem) @@ -81,10 +87,7 @@ def create_mt_client_with_tempfiles( ctx = ssl.create_default_context( cafile=ca_file.name, ) - client = httpx.Client( - cert=(cert_file.name, key_file.name), - verify=ctx - ) + client = httpx.Client(cert=(cert_file.name, key_file.name), verify=ctx) return client @@ -106,29 +109,43 @@ async def main() -> None: streaming_start_time_limit_sec=5, upload_time_limit_sec=5, ) - streaming_job = await compute_horde_client.create_job(compute_horde_streaming_job_spec) + streaming_job = await compute_horde_client.create_job( + compute_horde_streaming_job_spec + ) await streaming_job.wait_for_streaming(timeout=60) - logger.info(f"Streaming server: {streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}") + logger.info( + f"Streaming server: {streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}" + ) client = create_mt_client_with_tempfiles( streaming_job.streaming_public_cert, streaming_job.streaming_private_key, - streaming_job.streaming_server_cert + streaming_job.streaming_server_cert, ) - + max_terminate_retries = 3 for attempt in range(1, max_terminate_retries + 1): try: - response = client.get(f"https://{streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}/terminate") + response = client.get( + f"https://{streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}/terminate" + ) if 200 <= response.status_code < 300: - logger.info(f"Successfully terminated streaming server: {response.text}") + logger.info( + f"Successfully terminated streaming server: {response.text}" + ) break else: - logger.warning(f"Attempt {attempt}: Non-2xx response when terminating streaming server: {response.status_code} - {response.text}") + logger.warning( + f"Attempt {attempt}: Non-2xx response when terminating streaming server: {response.status_code} - {response.text}" + ) except Exception as e: - logger.warning(f"Attempt {attempt}: Exception during streaming server termination: {e}") + logger.warning( + f"Attempt {attempt}: Exception during streaming server termination: {e}" + ) if attempt == max_terminate_retries: - raise RuntimeError(f"Failed to terminate streaming server after {max_terminate_retries} attempts") + raise RuntimeError( + f"Failed to terminate streaming server after {max_terminate_retries} attempts" + ) backoff = 2 ** (attempt - 1) logger.info(f"Retrying streaming server termination in {backoff} seconds...") time.sleep(backoff) @@ -139,8 +156,8 @@ async def main() -> None: var in os.environ for var in ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"] ) - # TODO: There should not be a need to sleep here. - # Figure out why the job fails to start immediately after the + # TODO: There should not be a need to sleep here. + # Figure out why the job fails to start immediately after the # previous job is finished. await asyncio.sleep(15) @@ -156,17 +173,22 @@ async def main() -> None: ) if verify_http_output_volumes: - bucket_name = os.environ.get("S3_BUCKET_NAME", "compute-horde-integration-tests") + bucket_name = os.environ.get( + "S3_BUCKET_NAME", "compute-horde-integration-tests" + ) available_characters = string.ascii_letters + string.digits filename = "".join(random.choices(available_characters, k=32)) post_object_key = f"{filename}_post.txt" put_object_key = f"{filename}_put.txt" - presigned_post, presigned_put = get_presigned_urls(bucket_name, post_object_key, put_object_key) + presigned_post, presigned_put = get_presigned_urls( + bucket_name, post_object_key, put_object_key + ) # Update job spec for CI to include S3 uploads. compute_horde_job_spec.args = [ - "sh", "-c", - f"echo 'Hello, World!' | tee -a /artifacts/stuff /output/{post_object_key} /output/{put_object_key}" + "sh", + "-c", + f"echo 'Hello, World!' | tee -a /artifacts/stuff /output/{post_object_key} /output/{put_object_key}", ] compute_horde_job_spec.output_volumes = { f"/output/{post_object_key}": HTTPOutputVolume( @@ -185,15 +207,19 @@ async def main() -> None: await job.wait(timeout=10 * 60) # Validate job completion and output. - expected_artifacts = {'/artifacts/stuff': b'Hello, World!\n'} + expected_artifacts = {"/artifacts/stuff": b"Hello, World!\n"} if job.status != "Completed" or job.result.artifacts != expected_artifacts: - raise RuntimeError(f"Job failed: status={job.status}, artifacts={job.result.artifacts}") + raise RuntimeError( + f"Job failed: status={job.status}, artifacts={job.result.artifacts}" + ) if verify_http_output_volumes: post_object_path = f"/output/{post_object_key}" put_object_path = f"/output/{put_object_key}" if len(job.result.upload_results) != 2: - raise RuntimeError(f"Expected 2 keys in upload results, found {len(job.result.upload_results)}") + raise RuntimeError( + f"Expected 2 keys in upload results, found {len(job.result.upload_results)}" + ) if post_object_path not in job.result.upload_results: raise RuntimeError(f"Missing key: {post_object_path}") if put_object_path not in job.result.upload_results: diff --git a/local_stack/uv.lock b/local_stack/uv.lock index 99362dda1..e6dc33115 100644 --- a/local_stack/uv.lock +++ b/local_stack/uv.lock @@ -530,7 +530,7 @@ wheels = [ [[package]] name = "compute-horde" -version = "0.0.24" +version = "0.0.25" source = { editable = "../compute_horde" } dependencies = [ { name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, diff --git a/tests/integration_tests/test_miner_on_dev_executor_manager.py b/tests/integration_tests/test_miner_on_dev_executor_manager.py index 0d8f9026d..788d7f97f 100644 --- a/tests/integration_tests/test_miner_on_dev_executor_manager.py +++ b/tests/integration_tests/test_miner_on_dev_executor_manager.py @@ -95,7 +95,7 @@ def miner_preparation_tasks(cls): subprocess.check_call( args, env={**os.environ, "DATABASE_SUFFIX": "_integration_test"} ) - if os.getenv('GITHUB_ACTIONS') == 'true': + if os.getenv("GITHUB_ACTIONS") == "true": # we change system files here so only do so in GitHub Actions # developer has to setup is machine correctly to run integration test nvidia_container_toolkit_mock = """#!/bin/bash @@ -104,13 +104,22 @@ def miner_preparation_tasks(cls): """ # Write the script - with open('/tmp/nvidia-container-toolkit', 'w') as f: + with open("/tmp/nvidia-container-toolkit", "w") as f: f.write(nvidia_container_toolkit_mock) - subprocess.check_call(['sudo', 'mv', '/tmp/nvidia-container-toolkit', '/bin/nvidia-container-toolkit']) + subprocess.check_call( + [ + "sudo", + "mv", + "/tmp/nvidia-container-toolkit", + "/bin/nvidia-container-toolkit", + ] + ) # Make it executable - subprocess.check_call(['sudo', 'chmod', '+x', '/bin/nvidia-container-toolkit']) + subprocess.check_call( + ["sudo", "chmod", "+x", "/bin/nvidia-container-toolkit"] + ) @classmethod def miner_environ(cls) -> dict[str, str]: From 3273301dd6571cbf9253e88b4fd972b3d6c3dd73 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Thu, 31 Jul 2025 06:25:55 -0300 Subject: [PATCH 10/25] Enhance Volume Manager integration. --- compute_horde_sdk/README.md | 24 ++-- .../src/compute_horde_core/volume/__init__.py | 2 - .../src/compute_horde_core/volume/_manager.py | 64 ++++++----- .../tests/unit/core/test_volume.py | 57 +++++----- .../executor/job_runner.py | 28 +++-- local_stack/prepare.sh | 5 +- local_stack/send_hello_world_job.py | 106 ++++++++++++++---- local_stack/send_huggingface_job.py | 74 ------------ 8 files changed, 182 insertions(+), 178 deletions(-) delete mode 100644 local_stack/send_huggingface_job.py diff --git a/compute_horde_sdk/README.md b/compute_horde_sdk/README.md index 264d40073..237d492b9 100644 --- a/compute_horde_sdk/README.md +++ b/compute_horde_sdk/README.md @@ -268,12 +268,21 @@ Set the volume manager address as an environment variable: export VOLUME_MANAGER_ADDRESS="http://localhost:8080" ``` -Add custom headers for authentication: +You can also add custom headers for authentication or any other purpose. Any environment variable starting with `VOLUME_MANAGER_HEADER_` will be added as a header to each request sent to the volume manager. + +**Examples:** ```bash -export VOLUME_MANAGER_HEADER_Authorization="Bearer your-token" -export VOLUME_MANAGER_HEADER_X-Custom-Header="custom-value" +# Authentication header +export VOLUME_MANAGER_HEADER_AUTHORIZATION='Bearer dupadupakupa' + +# Custom API key +export VOLUME_MANAGER_HEADER_X_API_KEY='your-api-key-here' + +# Custom metadata +export VOLUME_MANAGER_HEADER_X_CUSTOM_METADATA='project:compute-horde' ``` +**Local development**: The local stack setup script [`prepare.sh`](https://github.com/backend-developers-ltd/ComputeHorde/blob/master/local_stack/prepare.sh) includes a commented volume manager configuration. To test volume manager functionality, uncomment the section and run [`send_hello_world_job.py`](https://github.com/backend-developers-ltd/ComputeHorde/blob/master/local_stack/send_hello_world_job.py) with the `--test-volume` flag. ### Volume Manager API @@ -315,11 +324,10 @@ Your volume manager must implement these endpoints: ```json { "mounts": [ - { - "type": "bind", - "source": "/host/path/to/cached/model", - "target": "/volume/models" - } + ["-v", "/host/path/to/cached/model:/volume/models"], + ["--tmpfs", "/tmp/cache:size=100m"], + ["-v", "my_named_volume:/data", "--volume-driver", "local"], + ["--device", "/dev/gpu0:/dev/gpu0:rwm"] ] } ``` diff --git a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py index 7252bae1f..f7143b4e5 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/__init__.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/__init__.py @@ -10,8 +10,6 @@ from ._manager import ( VolumeManagerClient, VolumeManagerError, - VolumeManagerMount, - VolumeManagerResponse, create_volume_manager_client, get_volume_manager_headers, ) diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py index 63ea9c040..f4abf4140 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py @@ -3,7 +3,6 @@ import json import logging import os -from dataclasses import dataclass from typing import Any import httpx @@ -14,7 +13,7 @@ logger = logging.getLogger(__name__) -def get_volume_manager_headers(): +def get_volume_manager_headers() -> dict[str, str]: """Extract volume manager headers from environment variables.""" headers = {} prefix = "COMPUTE_HORDE_VOLUME_MANAGER_HEADER_" @@ -25,22 +24,6 @@ def get_volume_manager_headers(): return headers -@dataclass -class VolumeManagerMount: - """Represents a Docker mount option returned by the volume manager.""" - - type: str - source: str - target: str - - -@dataclass -class VolumeManagerResponse: - """Response from volume manager prepare_volume endpoint.""" - - mounts: list[VolumeManagerMount] - - class VolumeManagerError(Exception): """Exception raised when volume manager operations fail.""" @@ -60,10 +43,23 @@ class VolumeManagerClient: def __init__(self, base_url: str, headers: dict[str, str] | None = None): self.base_url = base_url.rstrip("/") self.headers = headers or {} + self._client: httpx.AsyncClient | None = None + + def _get_client(self) -> httpx.AsyncClient: + """Get or create the HTTP client.""" + if self._client is None: + self._client = httpx.AsyncClient() + return self._client + + async def close(self) -> None: + """Close the HTTP client.""" + if self._client is not None: + await self._client.aclose() + self._client = None async def prepare_volume( - self, job_uuid: str, volume: Volume, job_metadata: dict[str, Any] - ) -> VolumeManagerResponse: + self, job_uuid: str, volume: Volume, job_metadata: dict[str, Any], timeout: float = 60.0 + ) -> list[list[str]]: """ Request the volume manager to prepare a volume for the job. @@ -71,9 +67,10 @@ async def prepare_volume( job_uuid: Unique identifier for the job volume: Volume specification to prepare job_metadata: Additional metadata about the job + timeout: Request timeout in seconds Returns: - VolumeManagerResponse with mount options + List of mount flag lists for Docker Raises: VolumeManagerError: If the request fails @@ -83,9 +80,14 @@ async def prepare_volume( volume_data = volume.model_dump() payload = {"job_uuid": job_uuid, "volume": volume_data, "job_metadata": job_metadata} - response_data = await self._make_request(url, payload, "prepare_volume") - mounts = [VolumeManagerMount(**mount) for mount in response_data["mounts"]] - return VolumeManagerResponse(mounts=mounts) + response_data = await self._make_request(url, payload, "prepare_volume", timeout=timeout) + + # Convert string mount types to MountType objects + mounts = [] + for mount_data in response_data["mounts"]: + mounts.append(mount_data) + + return mounts async def job_finished(self, job_uuid: str) -> None: """ @@ -109,8 +111,8 @@ async def job_finished(self, job_uuid: str) -> None: retry=tenacity.retry_if_exception_type((httpx.RequestError, httpx.HTTPStatusError)), ) async def _make_request( - self, url: str, payload: dict[str, Any], operation: str, timeout: float = 300.0 - ) -> dict[str, Any]: + self, url: str, payload: dict[str, Any], operation: str, timeout: float = 60.0 + ) -> dict[str, list[list[str]]]: """ Make a POST request to the Volume Manager with standardized error handling. @@ -121,7 +123,7 @@ async def _make_request( timeout: Request timeout in seconds Returns: - Parsed JSON response + Mount options for the job Raises: VolumeManagerError: If the request fails or response is invalid @@ -130,9 +132,9 @@ async def _make_request( logger.debug(f"Making {operation} request to Volume Manager at {url}") try: - async with httpx.AsyncClient(timeout=timeout) as client: - response = await client.post(url, json=payload, headers=self.headers) - response.raise_for_status() + client = self._get_client() + response = await client.post(url, json=payload, headers=self.headers, timeout=timeout) + response.raise_for_status() logger.debug(f"Volume Manager {operation} response status: {response.status_code}") logger.debug(f"Volume Manager {operation} response: {response.text}") @@ -152,6 +154,8 @@ async def _make_request( error_detail = e.response.text raise VolumeManagerError(error_msg, error_detail=error_detail) + except httpx.RequestError as e: + raise VolumeManagerError(f"Network error during Volume Manager {operation}: {e}", error_detail=str(e)) except json.JSONDecodeError as e: raise VolumeManagerError(f"Invalid JSON response from Volume Manager {operation}: {e}", error_detail=str(e)) diff --git a/compute_horde_sdk/tests/unit/core/test_volume.py b/compute_horde_sdk/tests/unit/core/test_volume.py index 5720d89e7..b822e39ca 100644 --- a/compute_horde_sdk/tests/unit/core/test_volume.py +++ b/compute_horde_sdk/tests/unit/core/test_volume.py @@ -22,7 +22,6 @@ VolumeDownloadFailed, VolumeManagerClient, VolumeManagerError, - VolumeManagerResponse, ZipUrlVolume, ZipUrlVolumeDownloader, create_volume_manager_client, @@ -546,7 +545,7 @@ def huggingface_volume(self): @pytest.mark.asyncio async def test_prepare_volume_success(self, client, huggingface_volume): """Test successful prepare_volume call.""" - mock_response = {"mounts": [{"type": "bind", "source": "/host/path", "target": "/container/path"}]} + mock_response = {"mounts": [["-v", "/host/path:/container/path"]]} with mock.patch.object(client, "_make_request") as mock_make_request: mock_make_request.return_value = mock_response @@ -555,9 +554,11 @@ async def test_prepare_volume_success(self, client, huggingface_volume): job_uuid="test-job-123", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - assert isinstance(result, VolumeManagerResponse) - assert len(result.mounts) == 1 - assert result.mounts[0].type == "bind" + assert isinstance(result, list) + assert all(isinstance(item, list) for item in result) + assert all(all(isinstance(subitem, str) for subitem in item) for item in result) + assert len(result) == 1 + assert result[0] == ["-v", "/host/path:/container/path"] @pytest.mark.asyncio async def test_job_finished_success(self, client): @@ -580,12 +581,12 @@ async def test_error_handling(self, client, huggingface_volume): http_error = httpx.HTTPStatusError("500 Internal Server Error", request=None, response=mock_response) - with mock.patch("httpx.AsyncClient") as mock_client_class: - mock_client = mock.AsyncMock() - mock_client_class.return_value.__aenter__.return_value = mock_client - mock_client.post.side_effect = http_error + # Mock the _get_client method to return a mock client that always raises the error + mock_client = mock.AsyncMock() + mock_client.post = mock.AsyncMock(side_effect=http_error) - # Test prepare_volume error + with mock.patch.object(client, "_get_client", return_value=mock_client): + # Test prepare_volume error - should retry and eventually raise VolumeManagerError with pytest.raises(VolumeManagerError) as exc_info: await client.prepare_volume( job_uuid="test-job-123", volume=huggingface_volume, job_metadata={"image": "test-image"} @@ -595,7 +596,7 @@ async def test_error_handling(self, client, huggingface_volume): assert "Volume Manager prepare_volume returned status 500" in error.description assert error.error_detail == "Server error" - # Test job_finished error + # Test job_finished error - should retry and eventually raise VolumeManagerError with pytest.raises(VolumeManagerError) as exc_info: await client.job_finished("test-job-123") @@ -611,7 +612,7 @@ async def test_prepare_volume_with_different_volume_types(self, client): relative_path="data", ) - mock_response = {"mounts": [{"type": "bind", "source": "/tmp/inline-data", "target": "/volume/data"}]} + mock_response = {"mounts": [["-v", "/tmp/inline-data:/volume/data"]]} with mock.patch.object(client, "_make_request") as mock_make_request: mock_make_request.return_value = mock_response @@ -620,18 +621,20 @@ async def test_prepare_volume_with_different_volume_types(self, client): job_uuid="test-job-456", volume=inline_volume, job_metadata={"image": "test-image"} ) - assert isinstance(result, VolumeManagerResponse) - assert len(result.mounts) == 1 - assert result.mounts[0].type == "bind" + assert isinstance(result, list) + assert all(isinstance(item, list) for item in result) + assert all(all(isinstance(subitem, str) for subitem in item) for item in result) + assert len(result) == 1 + assert result[0] == ["-v", "/tmp/inline-data:/volume/data"] @pytest.mark.asyncio async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_volume): """Test prepare_volume with multiple mounts in response.""" mock_response = { "mounts": [ - {"type": "bind", "source": "/host/models", "target": "/volume/models"}, - {"type": "volume", "source": "cache-volume", "target": "/cache"}, - {"type": "tmpfs", "source": "", "target": "/tmp"}, + ["-v", "/host/models:/volume/models"], + ["-v", "cache-volume:/cache"], + ["--tmpfs", "/tmp"], ] } @@ -642,23 +645,19 @@ async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_vol job_uuid="test-job-789", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - assert isinstance(result, VolumeManagerResponse) - assert len(result.mounts) == 3 + assert isinstance(result, list) + assert all(isinstance(item, list) for item in result) + assert all(all(isinstance(subitem, str) for subitem in item) for item in result) + assert len(result) == 3 # Check first mount - assert result.mounts[0].type == "bind" - assert result.mounts[0].source == "/host/models" - assert result.mounts[0].target == "/volume/models" + assert result[0] == ["-v", "/host/models:/volume/models"] # Check second mount - assert result.mounts[1].type == "volume" - assert result.mounts[1].source == "cache-volume" - assert result.mounts[1].target == "/cache" + assert result[1] == ["-v", "cache-volume:/cache"] # Check third mount - assert result.mounts[2].type == "tmpfs" - assert result.mounts[2].source == "" - assert result.mounts[2].target == "/tmp" + assert result[2] == ["--tmpfs", "/tmp"] class TestCreateVolumeManagerClient: diff --git a/executor/app/src/compute_horde_executor/executor/job_runner.py b/executor/app/src/compute_horde_executor/executor/job_runner.py index b20859710..f1b275328 100644 --- a/executor/app/src/compute_horde_executor/executor/job_runner.py +++ b/executor/app/src/compute_horde_executor/executor/job_runner.py @@ -28,7 +28,6 @@ VolumeDownloadFailed, VolumeManagerClient, VolumeManagerError, - VolumeManagerMount, create_volume_manager_client, get_volume_manager_headers, ) @@ -150,7 +149,7 @@ def __init__(self): ) # Track volume manager mounts for cleanup - self.volume_manager_mounts: list[VolumeManagerMount] = [] + self.volume_manager_mounts: list[list[str]] = [] def generate_streaming_certificate(self, executor_ip: str, public_key: str): """ @@ -262,9 +261,9 @@ async def start_job(self) -> AsyncGenerator[None, Any]: volume_flags = [] if self.volume_manager_mounts: # Use volume manager mounts - for mount in self.volume_manager_mounts: - volume_flags.extend(["-v", f"{mount.source}:{mount.target}"]) - logger.debug(f"Adding volume manager mount: {mount.source}:{mount.target}") + for mount_flags in self.volume_manager_mounts: + volume_flags.extend(mount_flags) + logger.debug(f"Adding volume manager mount: {mount_flags}") else: # Use default volume mount volume_flags = ["-v", f"{self.volume_mount_dir.as_posix()}/:/volume/"] @@ -437,9 +436,18 @@ async def upload_results(self) -> JobResult: ) async def clean(self): - # Notify volume manager if configured - if self.volume_manager_client and self.initial_job_request: - await self._notify_volume_manager_job_finished() + # Close volume manager client to prevent resource leaks + if self.volume_manager_client: + if self.initial_job_request: + # Notify volume manager if configured + await self._notify_volume_manager_job_finished() + + try: + await self.volume_manager_client.close() + except Exception as e: + logger.warning(f"Failed to close volume manager client: {e}") + finally: + self.volume_manager_client = None # remove input/output directories with docker, to deal with funky file permissions root_for_remove = pathlib.Path("/temp_dir/") @@ -525,8 +533,8 @@ async def _prepare_volume_with_manager(self, volume: Volume): ) # Store the mounts for later use in Docker command - self.volume_manager_mounts = response.mounts - logger.debug(f"Volume Manager provided {len(response.mounts)} mounts") + self.volume_manager_mounts = response + logger.debug(f"Volume Manager provided {len(response)} mounts") except VolumeManagerError as exc: logger.warning(f"Volume Manager failed to prepare volume for job {job_uuid}: {exc}") diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index ae734c79c..52fbad5a6 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -61,8 +61,9 @@ PROJECT_ROOT=$(git rev-parse --show-toplevel) cd $PROJECT_ROOT/executor ./setup-dev.sh update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" -# Use for volume manager testing, not a requirement for the local stack setup -update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" + +# Uncomment the line below only if you have a volume manager service running +# update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner diff --git a/local_stack/send_hello_world_job.py b/local_stack/send_hello_world_job.py index fec670b4e..ee9131f5a 100644 --- a/local_stack/send_hello_world_job.py +++ b/local_stack/send_hello_world_job.py @@ -7,6 +7,7 @@ import tempfile import time import ssl +import argparse import boto3 import bittensor @@ -16,7 +17,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec -from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HTTPOutputVolume +from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HTTPOutputVolume, HuggingfaceInputVolume # Configure logging logging.basicConfig(level=logging.INFO) @@ -91,37 +92,76 @@ def create_mt_client_with_tempfiles( return client -async def main() -> None: - # Pull required Docker images - os.system("docker pull alpine") - os.system("docker pull backenddevelopersltd/compute-horde-executor:v1-latest") - os.system("docker pull backenddevelopersltd/compute-horde-streaming-job-test:v0-latest") - - compute_horde_streaming_job_spec = ComputeHordeJobSpec( +async def run_volume_test(): + """Run a test job that downloads a HuggingFace model.""" + logger.info("Running volume download test...") + + # Pull required Docker image + os.system("docker pull python:3.11-slim") + + # Create a job spec that downloads a small HuggingFace model + compute_horde_job_spec = ComputeHordeJobSpec( executor_class=ExecutorClass.always_on__llm__a6000, job_namespace="SN123.0", - docker_image="backenddevelopersltd/compute-horde-streaming-job-test:v0-latest", - args=["python", "./mock_streaming_job.py"], + docker_image="python:3.11-slim", + args=[ + "bash", + "-c", + "echo 'Testing volume download...' && " + "ls -la /volume && " + "echo 'Job completed successfully' > /artifacts/stuff", + ], artifacts_dir="/artifacts", - streaming=True, - download_time_limit_sec=5, - execution_time_limit_sec=15, - streaming_start_time_limit_sec=5, - upload_time_limit_sec=5, + input_volumes={ + "/volume/model": HuggingfaceInputVolume( + repo_id="prajjwal1/bert-tiny", + repo_type="model", + ), + }, + download_time_limit_sec=60, + execution_time_limit_sec=30, + upload_time_limit_sec=10, ) + + # Create and submit the job. + job = await compute_horde_client.create_job(compute_horde_job_spec) + await job.wait(timeout=10 * 60) + + # Validate job completion and output. + expected_artifacts = {"/artifacts/stuff": b"Job completed successfully\n"} + if job.status != "Completed" or job.result.artifacts != expected_artifacts: + raise RuntimeError( + f"Job failed: status={job.status}, artifacts={job.result.artifacts}" + ) + + logger.info("Volume test success!") + +async def run_hello_world_job(): + compute_horde_streaming_job_spec = ComputeHordeJobSpec( + executor_class=ExecutorClass.always_on__llm__a6000, + job_namespace="SN123.0", + docker_image="backenddevelopersltd/compute-horde-streaming-job-test:v0-latest", + args=["python", "./mock_streaming_job.py"], + artifacts_dir="/artifacts", + streaming=True, + download_time_limit_sec=5, + execution_time_limit_sec=15, + streaming_start_time_limit_sec=5, + upload_time_limit_sec=5, + ) streaming_job = await compute_horde_client.create_job( - compute_horde_streaming_job_spec - ) + compute_horde_streaming_job_spec + ) await streaming_job.wait_for_streaming(timeout=60) logger.info( - f"Streaming server: {streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}" - ) + f"Streaming server: {streaming_job.streaming_server_address}:{streaming_job.streaming_server_port}" + ) client = create_mt_client_with_tempfiles( - streaming_job.streaming_public_cert, - streaming_job.streaming_private_key, - streaming_job.streaming_server_cert, - ) + streaming_job.streaming_public_cert, + streaming_job.streaming_private_key, + streaming_job.streaming_server_cert, + ) max_terminate_retries = 3 for attempt in range(1, max_terminate_retries + 1): @@ -231,6 +271,26 @@ async def main() -> None: logger.info("Success!") +async def main() -> None: + parser = argparse.ArgumentParser(description="Run ComputeHorde integration tests") + parser.add_argument( + "--test-volume", + action="store_true", + help="Include volume download test" + ) + args = parser.parse_args() + + # Pull required Docker images + os.system("docker pull alpine") + os.system("docker pull backenddevelopersltd/compute-horde-executor:v1-latest") + os.system("docker pull backenddevelopersltd/compute-horde-streaming-job-test:v0-latest") + + # Run volume test if requested + if args.test_volume: + await run_volume_test() + else: + await run_hello_world_job() + if __name__ == "__main__": asyncio.run(main()) diff --git a/local_stack/send_huggingface_job.py b/local_stack/send_huggingface_job.py deleted file mode 100644 index 4b905e28f..000000000 --- a/local_stack/send_huggingface_job.py +++ /dev/null @@ -1,74 +0,0 @@ -import asyncio -import pathlib -import os -import logging - -import bittensor -from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec -from compute_horde_sdk.v1 import ( - ComputeHordeClient, - ExecutorClass, - HuggingfaceInputVolume, -) - -# Configure logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -# Initialize wallet and Compute Horde client. -wallet = bittensor.wallet( - name="validator", - hotkey="default", - path=(pathlib.Path(__file__).parent / "wallets").as_posix(), -) -compute_horde_client = ComputeHordeClient( - hotkey=wallet.hotkey, - compute_horde_validator_hotkey=wallet.hotkey.ss58_address, - facilitator_url="http://localhost:9000", -) - - -async def main() -> None: - os.system("docker pull python:3.11-slim") - - # Create a job spec that downloads a small Huggingface model - compute_horde_job_spec = ComputeHordeJobSpec( - executor_class=ExecutorClass.always_on__llm__a6000, - job_namespace="SN123.0", - docker_image="python:3.11-slim", - args=[ - "bash", - "-c", - "echo 'Hello, world!' && echo 'Job completed successfully' > /output/result.txt", - ], - artifacts_dir="/artifacts", - input_volumes={ - "/volume/model": HuggingfaceInputVolume( - repo_id="prajjwal1/bert-tiny", - repo_type="model", - # usage_type="reusable", - ), - }, - download_time_limit_sec=60, - execution_time_limit_sec=30, - upload_time_limit_sec=10, - ) - - # Create and submit the job - logger.info("Creating job...") - job = await compute_horde_client.create_job(compute_horde_job_spec) - logger.info("Waiting for job completion...") - await job.wait(timeout=10 * 60) - - # Validate job completion and output - if job.status != "Completed": - logger.error(f"Job failed with status: {job.status}") - if hasattr(job.result, "stderr"): - logger.error(f"Job stderr: {job.result.stderr}") - raise RuntimeError(f"Job failed: status={job.status}") - - logger.info(f"Job completed successfully! Artifacts: {job.result.artifacts}") - - -if __name__ == "__main__": - asyncio.run(main()) From 9df7f24104bdc30e96d8cc22cddcbb50d9037829 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Thu, 31 Jul 2025 06:26:16 -0300 Subject: [PATCH 11/25] add type ignore --- compute_horde_sdk/src/compute_horde_core/volume/_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py index f4abf4140..95b8a2e89 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py @@ -139,7 +139,7 @@ async def _make_request( logger.debug(f"Volume Manager {operation} response status: {response.status_code}") logger.debug(f"Volume Manager {operation} response: {response.text}") - return response.json() + return response.json() # type: ignore except httpx.HTTPStatusError as e: # Handle non-retryable status codes From 4eb46c06929dd5c0b51b95fb218de706076758f5 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 05:14:18 -0300 Subject: [PATCH 12/25] Testing Volume Manager CI integration --- .github/workflows/integration_ci.yml | 5 + .../src/compute_horde_core/volume/_manager.py | 28 +- .../tests/unit/core/test_volume.py | 24 +- examples/volume_manager/.dockerignore | 5 + examples/volume_manager/Dockerfile | 31 + examples/volume_manager/README.md | 63 + examples/volume_manager/docker-compose.yml | 21 + examples/volume_manager/pyproject.toml | 43 + examples/volume_manager/run_tests.sh | 102 + examples/volume_manager/test_endpoints.py | 86 + examples/volume_manager/test_integration.py | 95 + examples/volume_manager/uv.lock | 1770 +++++++++++++++++ .../volume_manager/volume_manager/__init__.py | 0 .../volume_manager/volume_manager/__main__.py | 37 + examples/volume_manager/volume_manager/api.py | 153 ++ .../volume_manager/volume_manager/schemas.py | 60 + .../volume_manager/volume_manager/storage.py | 149 ++ .../volume_manager/volume_manager/utils.py | 23 + examples/volume_manager/wallets/coldkey | 1 + .../volume_manager/wallets/coldkeypub.txt | 1 + .../volume_manager/wallets/hotkeys/default | 1 + .../volume_manager/wallets/validator/coldkey | 1 + .../wallets/validator/coldkeypub.txt | 1 + .../wallets/validator/hotkeys/default | 1 + local_stack/prepare.sh | 2 +- local_stack/send_hello_world_job.py | 62 +- 26 files changed, 2669 insertions(+), 96 deletions(-) create mode 100644 examples/volume_manager/.dockerignore create mode 100644 examples/volume_manager/Dockerfile create mode 100644 examples/volume_manager/README.md create mode 100644 examples/volume_manager/docker-compose.yml create mode 100644 examples/volume_manager/pyproject.toml create mode 100755 examples/volume_manager/run_tests.sh create mode 100644 examples/volume_manager/test_endpoints.py create mode 100644 examples/volume_manager/test_integration.py create mode 100644 examples/volume_manager/uv.lock create mode 100644 examples/volume_manager/volume_manager/__init__.py create mode 100644 examples/volume_manager/volume_manager/__main__.py create mode 100644 examples/volume_manager/volume_manager/api.py create mode 100644 examples/volume_manager/volume_manager/schemas.py create mode 100644 examples/volume_manager/volume_manager/storage.py create mode 100644 examples/volume_manager/volume_manager/utils.py create mode 100644 examples/volume_manager/wallets/coldkey create mode 100644 examples/volume_manager/wallets/coldkeypub.txt create mode 100644 examples/volume_manager/wallets/hotkeys/default create mode 100644 examples/volume_manager/wallets/validator/coldkey create mode 100644 examples/volume_manager/wallets/validator/coldkeypub.txt create mode 100644 examples/volume_manager/wallets/validator/hotkeys/default diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index 23df83ac5..402878c3c 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -5,6 +5,7 @@ on: push: branches: [master, main] pull_request: + branches: [master, main, ci_test] env: PYTHON_DEFAULT_VERSION: "3.11" @@ -41,6 +42,10 @@ jobs: AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} + - name: test volume manager integration + working-directory: ./examples/volume_manager + run: ./run_tests.sh -i -d + - name: Upload integration test logs uses: actions/upload-artifact@v4 if: always() diff --git a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py index 95b8a2e89..f2e58963d 100644 --- a/compute_horde_sdk/src/compute_horde_core/volume/_manager.py +++ b/compute_horde_sdk/src/compute_horde_core/volume/_manager.py @@ -57,9 +57,7 @@ async def close(self) -> None: await self._client.aclose() self._client = None - async def prepare_volume( - self, job_uuid: str, volume: Volume, job_metadata: dict[str, Any], timeout: float = 60.0 - ) -> list[list[str]]: + async def prepare_volume(self, job_uuid: str, volume: Volume, job_metadata: dict[str, Any]) -> list[list[str]]: """ Request the volume manager to prepare a volume for the job. @@ -67,7 +65,6 @@ async def prepare_volume( job_uuid: Unique identifier for the job volume: Volume specification to prepare job_metadata: Additional metadata about the job - timeout: Request timeout in seconds Returns: List of mount flag lists for Docker @@ -80,14 +77,9 @@ async def prepare_volume( volume_data = volume.model_dump() payload = {"job_uuid": job_uuid, "volume": volume_data, "job_metadata": job_metadata} - response_data = await self._make_request(url, payload, "prepare_volume", timeout=timeout) + response_data = await self._make_request(url, payload, "prepare_volume") - # Convert string mount types to MountType objects - mounts = [] - for mount_data in response_data["mounts"]: - mounts.append(mount_data) - - return mounts + return response_data["mounts"] async def job_finished(self, job_uuid: str) -> None: """ @@ -103,16 +95,14 @@ async def job_finished(self, job_uuid: str) -> None: url = f"{self.base_url}/job_finished" payload = {"job_uuid": job_uuid} - await self._make_request(url, payload, "job_finished", timeout=30.0) + await self._make_request(url, payload, "job_finished") @tenacity.retry( stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1), retry=tenacity.retry_if_exception_type((httpx.RequestError, httpx.HTTPStatusError)), ) - async def _make_request( - self, url: str, payload: dict[str, Any], operation: str, timeout: float = 60.0 - ) -> dict[str, list[list[str]]]: + async def _make_request(self, url: str, payload: dict[str, Any], operation: str) -> dict[str, list[list[str]]]: """ Make a POST request to the Volume Manager with standardized error handling. @@ -120,7 +110,6 @@ async def _make_request( url: The endpoint URL payload: The JSON payload to send operation: Operation name for error messages - timeout: Request timeout in seconds Returns: Mount options for the job @@ -133,7 +122,12 @@ async def _make_request( try: client = self._get_client() - response = await client.post(url, json=payload, headers=self.headers, timeout=timeout) + response = await client.post( + url, + json=payload, + headers=self.headers, + timeout=httpx.Timeout(connect=30.0, read=None, write=None, pool=None), + ) response.raise_for_status() logger.debug(f"Volume Manager {operation} response status: {response.status_code}") diff --git a/compute_horde_sdk/tests/unit/core/test_volume.py b/compute_horde_sdk/tests/unit/core/test_volume.py index b822e39ca..671d29c8e 100644 --- a/compute_horde_sdk/tests/unit/core/test_volume.py +++ b/compute_horde_sdk/tests/unit/core/test_volume.py @@ -621,11 +621,7 @@ async def test_prepare_volume_with_different_volume_types(self, client): job_uuid="test-job-456", volume=inline_volume, job_metadata={"image": "test-image"} ) - assert isinstance(result, list) - assert all(isinstance(item, list) for item in result) - assert all(all(isinstance(subitem, str) for subitem in item) for item in result) - assert len(result) == 1 - assert result[0] == ["-v", "/tmp/inline-data:/volume/data"] + assert result == [["-v", "/tmp/inline-data:/volume/data"]] @pytest.mark.asyncio async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_volume): @@ -645,19 +641,11 @@ async def test_prepare_volume_with_multiple_mounts(self, client, huggingface_vol job_uuid="test-job-789", volume=huggingface_volume, job_metadata={"image": "test-image"} ) - assert isinstance(result, list) - assert all(isinstance(item, list) for item in result) - assert all(all(isinstance(subitem, str) for subitem in item) for item in result) - assert len(result) == 3 - - # Check first mount - assert result[0] == ["-v", "/host/models:/volume/models"] - - # Check second mount - assert result[1] == ["-v", "cache-volume:/cache"] - - # Check third mount - assert result[2] == ["--tmpfs", "/tmp"] + assert result == [ + ["-v", "/host/models:/volume/models"], + ["-v", "cache-volume:/cache"], + ["--tmpfs", "/tmp"], + ] class TestCreateVolumeManagerClient: diff --git a/examples/volume_manager/.dockerignore b/examples/volume_manager/.dockerignore new file mode 100644 index 000000000..b659b67ee --- /dev/null +++ b/examples/volume_manager/.dockerignore @@ -0,0 +1,5 @@ +* + +!compute_horde_sdk/src/ +!compute_horde_sdk/pyproject.toml +!examples/volume_manager/ \ No newline at end of file diff --git a/examples/volume_manager/Dockerfile b/examples/volume_manager/Dockerfile new file mode 100644 index 000000000..ec70f6ead --- /dev/null +++ b/examples/volume_manager/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.11-slim + +ENV PYTHONUNBUFFERED=1 \ + VOLUME_MANAGER_CACHE_DIR=/tmp/volume_manager/cache + +WORKDIR /app + +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Install uv +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + mv /root/.local/bin/uv /usr/local/bin/uv && \ + rm -rf /root/.local + +# Set up volume manager cache directory with proper permissions +RUN mkdir -p /tmp/volume_manager/cache && \ + chmod -R 777 /tmp/volume_manager + +COPY compute_horde_sdk /app/compute_horde_sdk +COPY examples/volume_manager /app/volume_manager + +WORKDIR /app/volume_manager + +RUN uv sync + +EXPOSE 8001 + +CMD ["uv", "run", "volume_manager"] \ No newline at end of file diff --git a/examples/volume_manager/README.md b/examples/volume_manager/README.md new file mode 100644 index 000000000..8df4382b1 --- /dev/null +++ b/examples/volume_manager/README.md @@ -0,0 +1,63 @@ +# Volume Manager Example + +An example implementation of a volume manager for Compute Horde. This demonstrates how to build an external volume management service that can download, cache, and prepare volumes for compute jobs. + +## Quick Start + +### Prerequisites +- Docker (required for running the service) +- Python 3.11+ and UV package manager (only needed for local development) + +### Usage +```bash +# Start the volume manager with Docker Compose +docker-compose up -d + +# Check logs +docker-compose logs -f volume-manager + +# Stop the service +docker-compose down +``` + +**Note:** The port where the volume manager service listens on can be configured using environment variables: + +```bash +# Set env variable directly +export VOLUME_MANAGER_PORT=9000 + +# Or use it with docker-compose +VOLUME_MANAGER_PORT=9000 docker-compose up -d +``` + +## Monitoring + +```bash +# Check health (uses VOLUME_MANAGER_PORT or defaults to 8001) +curl http://localhost:${VOLUME_MANAGER_PORT:-8001}/health + +# Check cache status +curl http://localhost:${VOLUME_MANAGER_PORT:-8001}/cache/status +``` + +## Local Testing + +After setting everything up with `local_stack/run_in_screen.sh`, test the + +```bash +./run_tests.sh + +# Test with custom port +VOLUME_MANAGER_PORT=9000 ./run_tests.sh + +# Deploy the volume manager example +./run_tests.sh -d + +# Test the whole ComputeHorde integration instead of endpoints only +./run_tests.sh -i +``` + +**Available flags:** +- `-d, --deploy`: Deploy volume manager example +- `-i, --integration`: Test complete job integration +- `-h, --help`: Show help message \ No newline at end of file diff --git a/examples/volume_manager/docker-compose.yml b/examples/volume_manager/docker-compose.yml new file mode 100644 index 000000000..28581a168 --- /dev/null +++ b/examples/volume_manager/docker-compose.yml @@ -0,0 +1,21 @@ +services: + volume-manager: + build: + context: ../.. # Point to project root, important for the COPY command in the Dockerfile + dockerfile: examples/volume_manager/Dockerfile + ports: + - "${VOLUME_MANAGER_PORT:-8001}:${VOLUME_MANAGER_PORT:-8001}" + environment: + - PYTHONUNBUFFERED=1 + - VOLUME_MANAGER_CACHE_DIR=/tmp/volume_manager/cache + - VOLUME_MANAGER_PORT=${VOLUME_MANAGER_PORT:-8001} + volumes: + # Mount volume manager cache to host for testing + - /tmp/volume_manager/cache:/tmp/volume_manager/cache + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:${VOLUME_MANAGER_PORT:-8001}/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s \ No newline at end of file diff --git a/examples/volume_manager/pyproject.toml b/examples/volume_manager/pyproject.toml new file mode 100644 index 000000000..2c749f95e --- /dev/null +++ b/examples/volume_manager/pyproject.toml @@ -0,0 +1,43 @@ +[project] +name = "volume-manager-poc" +version = "0.1.0" +description = "POC Volume Manager for Compute Horde" +authors = [ + {name = "Compute Horde Team"} +] +dependencies = [ + "fastapi>=0.104.0", + "uvicorn[standard]>=0.24.0", + "pydantic>=2.0.0", + "httpx>=0.25.0", + "huggingface-hub>=0.19.0", + "pytest>=8.4.1", + "bittensor>=9.8.2", + "compute-horde-sdk", +] +requires-python = ">=3.11" + +[project.scripts] +volume-manager = "volume_manager.__main__:main" + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-asyncio>=0.21.0", + "httpx>=0.25.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + + + +[tool.hatch.build.targets.wheel] +packages = ["volume_manager"] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" diff --git a/examples/volume_manager/run_tests.sh b/examples/volume_manager/run_tests.sh new file mode 100755 index 000000000..4046ed54d --- /dev/null +++ b/examples/volume_manager/run_tests.sh @@ -0,0 +1,102 @@ +#!/bin/bash +set -e +set -o pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd "$SCRIPT_DIR" + +# Parse command line arguments +TEST_INTEGRATION=false +DEPLOY_VOLUME_MANAGER=false + +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -i, --integration Test complete job integration (default: test endpoints only)" + echo " -d, --deploy Deploy volume manager example" + echo " -h, --help Show this help message" + exit 1 +} + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--integration) + TEST_INTEGRATION=true + shift + ;; + -d|--deploy) + DEPLOY_VOLUME_MANAGER=true + shift + ;; + -h|--help) + show_usage + ;; + *) + echo "Unknown option: $1" + show_usage + ;; + esac +done + +# Use the newer 'docker compose' command +if docker compose version &> /dev/null; then + DOCKER_COMPOSE_COMMAND="docker compose" +elif command -v docker-compose &> /dev/null; then + DOCKER_COMPOSE_COMMAND="docker-compose" +else + echo "Error: Neither 'docker compose' nor 'docker-compose' is available on this system." + exit 1 +fi + +if [ "$DEPLOY_VOLUME_MANAGER" = true ]; then + echo "Starting volume manager with docker-compose..." + uv sync + $DOCKER_COMPOSE_COMMAND up -d + + echo "Waiting for volume manager to be ready..." + timeout 60s bash -c "until curl -f http://localhost:${VOLUME_MANAGER_PORT:-8001}/health; do sleep 2; done" +else + echo "Assuming volume manager is already running..." +fi + +echo "Running tests..." +if [ "$TEST_INTEGRATION" = true ]; then + echo "Testing integration..." + uv run python test_integration.py + PYTHON_EXIT_CODE=$? + + # Check cache status immediately after the test + CACHE_RESPONSE=$(curl -s http://localhost:8001/cache/status 2>/dev/null || echo '{"cached_volumes_count":0,"cached_volumes":[]}') + CACHE_COUNT=$(echo "$CACHE_RESPONSE" | grep -o '"cached_volumes_count":[0-9]*' | grep -o '[0-9]*' || echo "0") + CACHED_MODELS=$(echo "$CACHE_RESPONSE" | grep -o '"cached_volumes":\[[^]]*\]' | sed 's/"cached_volumes":\[//' | sed 's/\]//' | sed 's/"//g' || echo "[]") +else + echo "Testing endpoints..." + uv run python test_endpoints.py + PYTHON_EXIT_CODE=$? +fi + +# Test Review +echo "" +echo "=== TEST REVIEW ===" +if [ $PYTHON_EXIT_CODE -eq 0 ]; then + if [ "$TEST_INTEGRATION" = true ] && [ "$CACHE_COUNT" -gt 0 ]; then + echo "✅ PASSED - Volume manager integration working" + echo "📊 Cache has $CACHE_COUNT volumes" + echo "🔍 Cached models: $CACHED_MODELS" + elif [ "$TEST_INTEGRATION" = true ] && [ "$CACHE_COUNT" -eq 0 ]; then + echo "❌ FAILED - Volume manager not used (cache empty)" + echo "⚠️ Cache is empty - integration test failed" + exit 1 + else + echo "✅ PASSED - Endpoint test completed" + fi +else + echo "❌ FAILED" +fi +echo "==================" +echo "" + +if [ "$DEPLOY_VOLUME_MANAGER" = true ]; then + echo "Stopping volume manager..." + $DOCKER_COMPOSE_COMMAND down +fi \ No newline at end of file diff --git a/examples/volume_manager/test_endpoints.py b/examples/volume_manager/test_endpoints.py new file mode 100644 index 000000000..1f55ce455 --- /dev/null +++ b/examples/volume_manager/test_endpoints.py @@ -0,0 +1,86 @@ +import logging +import os +import requests +import uuid + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +VOLUME_MANAGER_PORT = os.environ.get("VOLUME_MANAGER_PORT", "8001") +VOLUME_MANAGER_URL = f"http://localhost:{VOLUME_MANAGER_PORT}" +TEST_MODEL = "prajjwal1/bert-tiny" +JOB_UUID = str(uuid.uuid4()) + +def test_prepare_volume_endpoint(): + """Test the prepare_volume API contract with valid request.""" + logger.info("Testing prepare_volume endpoint...") + + + # Generate a unique job UUID for each test run + + request_data = { + "job_uuid": JOB_UUID, + "volume": { + "volume_type": "huggingface_volume", + "repo_id": TEST_MODEL, + "revision": "main", + "relative_path": "models", + "usage_type": "reusable" + }, + "job_metadata": { + "message_type": "V0JobRequest", + "job_uuid": JOB_UUID, + "executor_class": "always_on__llm__a6000", + "docker_image": "example/image:latest", + "raw_script": None, + "docker_run_options_preset": "nvidia_all", + "docker_run_cmd": ["python", "main.py"], + "volume": None, + "output_upload": { + "output_upload_type": "single_file_put", + "relative_path": "results.json", + "url": "https://s3.example.com/results.json" + }, + "artifacts_dir": "/artifacts" + } + } + + response = requests.post(f"{VOLUME_MANAGER_URL}/prepare_volume", json=request_data) + response.raise_for_status() + + data = response.json() + assert data == {"mounts":[["-v","/tmp/volume_manager/cache/hf-prajjwal1_bert-tiny:/volume/models"]]}, data + + logger.info("✓ Prepare volume endpoint working") + + +def test_job_finished_endpoint(): + """Test the job_finished endpoint.""" + logger.info("Testing job_finished endpoint...") + + # Generate a unique job UUID for each test run + + request_data = { + "job_uuid": JOB_UUID + } + + response = requests.post(f"{VOLUME_MANAGER_URL}/job_finished", json=request_data) + response.raise_for_status() + + logger.info("✓ Job finished endpoint working") + + +def main(): + """Test the required volume manager endpoints.""" + logger.info("Testing required volume manager endpoints...") + + # Test basic API endpoints + test_prepare_volume_endpoint() + test_job_finished_endpoint() + + logger.info("All required endpoints are working! ✓") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/volume_manager/test_integration.py b/examples/volume_manager/test_integration.py new file mode 100644 index 000000000..50db4c1ee --- /dev/null +++ b/examples/volume_manager/test_integration.py @@ -0,0 +1,95 @@ +import asyncio +import pathlib +import os +import logging +import requests + +import bittensor + +import sys +import pathlib + +# Add the local compute_horde_sdk to the Python path +sdk_path = pathlib.Path(__file__).parent.parent.parent / "compute_horde_sdk" / "src" +sys.path.insert(0, str(sdk_path)) + +from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec +from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HuggingfaceInputVolume + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Initialize wallet and Compute Horde client. +wallet = bittensor.wallet( + name="validator", + hotkey="default", + path=(pathlib.Path(__file__).parent.parent.parent / "local_stack" / "wallets").as_posix(), +) +compute_horde_client = ComputeHordeClient( + hotkey=wallet.hotkey, + compute_horde_validator_hotkey=wallet.hotkey.ss58_address, + facilitator_url="http://localhost:9000", +) + + +async def run_volume_test(): + """Run a test job that downloads a HuggingFace model.""" + logger.info("Running volume download test...") + + # Pull required Docker images + os.system("docker pull python:3.11-slim") + os.system("docker pull us-central1-docker.pkg.dev/twistlock-secresearch/public/can-ctr-escape-cve-2022-0492:latest") + + # Create a job spec that downloads a small HuggingFace model + compute_horde_job_spec = ComputeHordeJobSpec( + executor_class=ExecutorClass.always_on__llm__a6000, + job_namespace="SN123.0", + docker_image="python:3.11-slim", + args=[ + "bash", + "-c", + "echo 'Testing volume download...' && " + "ls -la /volume && " + "echo 'Job completed successfully' > /artifacts/stuff", + ], + artifacts_dir="/artifacts", + input_volumes={ + "/volume/model": HuggingfaceInputVolume( + repo_id="prajjwal1/bert-tiny", + repo_type="model", + ), + }, + download_time_limit_sec=60, + execution_time_limit_sec=30, + upload_time_limit_sec=10, + ) + + # Create and submit the job. + job = await compute_horde_client.create_job(compute_horde_job_spec) + await job.wait(timeout=60) + + # Validate job completion and output. + expected_artifacts = {"/artifacts/stuff": b"Job completed successfully\n"} + if job.status != "Completed" or job.result.artifacts != expected_artifacts: + raise RuntimeError( + f"Job failed: status={job.status}, artifacts={job.result.artifacts}" + ) + + # Verify volume manager cache + try: + response = requests.get("http://localhost:8001/cache/status") + if response.status_code == 200: + cache_data = response.json() + logger.info(f"Volume manager cache status: {cache_data}") + if "hf-prajjwal1_bert-tiny" in cache_data.get("cached_volumes", []): + logger.info("✓ Volume manager successfully cached the model") + else: + logger.error("Volume manager cache is empty") + except Exception as e: + logger.error(f"Could not check volume manager status: {e}") + + logger.info("Volume test success!") + +if __name__ == "__main__": + asyncio.run(run_volume_test()) \ No newline at end of file diff --git a/examples/volume_manager/uv.lock b/examples/volume_manager/uv.lock new file mode 100644 index 000000000..166399a45 --- /dev/null +++ b/examples/volume_manager/uv.lock @@ -0,0 +1,1770 @@ +version = 1 +revision = 2 +requires-python = ">=3.11" + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.12.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/0b/e39ad954107ebf213a2325038a3e7a506be3d98e1435e1f82086eec4cde2/aiohttp-3.12.14.tar.gz", hash = "sha256:6e06e120e34d93100de448fd941522e11dafa78ef1a893c179901b7d66aa29f2", size = 7822921, upload-time = "2025-07-10T13:05:33.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/e1/8029b29316971c5fa89cec170274582619a01b3d82dd1036872acc9bc7e8/aiohttp-3.12.14-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f4552ff7b18bcec18b60a90c6982049cdb9dac1dba48cf00b97934a06ce2e597", size = 709960, upload-time = "2025-07-10T13:03:11.936Z" }, + { url = "https://files.pythonhosted.org/packages/96/bd/4f204cf1e282041f7b7e8155f846583b19149e0872752711d0da5e9cc023/aiohttp-3.12.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8283f42181ff6ccbcf25acaae4e8ab2ff7e92b3ca4a4ced73b2c12d8cd971393", size = 482235, upload-time = "2025-07-10T13:03:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0f/2a580fcdd113fe2197a3b9df30230c7e85bb10bf56f7915457c60e9addd9/aiohttp-3.12.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:040afa180ea514495aaff7ad34ec3d27826eaa5d19812730fe9e529b04bb2179", size = 470501, upload-time = "2025-07-10T13:03:16.153Z" }, + { url = "https://files.pythonhosted.org/packages/38/78/2c1089f6adca90c3dd74915bafed6d6d8a87df5e3da74200f6b3a8b8906f/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b413c12f14c1149f0ffd890f4141a7471ba4b41234fe4fd4a0ff82b1dc299dbb", size = 1740696, upload-time = "2025-07-10T13:03:18.4Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c8/ce6c7a34d9c589f007cfe064da2d943b3dee5aabc64eaecd21faf927ab11/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d6f607ce2e1a93315414e3d448b831238f1874b9968e1195b06efaa5c87e245", size = 1689365, upload-time = "2025-07-10T13:03:20.629Z" }, + { url = "https://files.pythonhosted.org/packages/18/10/431cd3d089de700756a56aa896faf3ea82bee39d22f89db7ddc957580308/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:565e70d03e924333004ed101599902bba09ebb14843c8ea39d657f037115201b", size = 1788157, upload-time = "2025-07-10T13:03:22.44Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b2/26f4524184e0f7ba46671c512d4b03022633bcf7d32fa0c6f1ef49d55800/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4699979560728b168d5ab63c668a093c9570af2c7a78ea24ca5212c6cdc2b641", size = 1827203, upload-time = "2025-07-10T13:03:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e0/30/aadcdf71b510a718e3d98a7bfeaea2396ac847f218b7e8edb241b09bd99a/aiohttp-3.12.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad5fdf6af93ec6c99bf800eba3af9a43d8bfd66dce920ac905c817ef4a712afe", size = 1729664, upload-time = "2025-07-10T13:03:26.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/7f/7ccf11756ae498fdedc3d689a0c36ace8fc82f9d52d3517da24adf6e9a74/aiohttp-3.12.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ac76627c0b7ee0e80e871bde0d376a057916cb008a8f3ffc889570a838f5cc7", size = 1666741, upload-time = "2025-07-10T13:03:28.167Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4d/35ebc170b1856dd020c92376dbfe4297217625ef4004d56587024dc2289c/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:798204af1180885651b77bf03adc903743a86a39c7392c472891649610844635", size = 1715013, upload-time = "2025-07-10T13:03:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/7b/24/46dc0380146f33e2e4aa088b92374b598f5bdcde1718c77e8d1a0094f1a4/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4f1205f97de92c37dd71cf2d5bcfb65fdaed3c255d246172cce729a8d849b4da", size = 1710172, upload-time = "2025-07-10T13:03:31.821Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0a/46599d7d19b64f4d0fe1b57bdf96a9a40b5c125f0ae0d8899bc22e91fdce/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:76ae6f1dd041f85065d9df77c6bc9c9703da9b5c018479d20262acc3df97d419", size = 1690355, upload-time = "2025-07-10T13:03:34.754Z" }, + { url = "https://files.pythonhosted.org/packages/08/86/b21b682e33d5ca317ef96bd21294984f72379454e689d7da584df1512a19/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a194ace7bc43ce765338ca2dfb5661489317db216ea7ea700b0332878b392cab", size = 1783958, upload-time = "2025-07-10T13:03:36.53Z" }, + { url = "https://files.pythonhosted.org/packages/4f/45/f639482530b1396c365f23c5e3b1ae51c9bc02ba2b2248ca0c855a730059/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:16260e8e03744a6fe3fcb05259eeab8e08342c4c33decf96a9dad9f1187275d0", size = 1804423, upload-time = "2025-07-10T13:03:38.504Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e5/39635a9e06eed1d73671bd4079a3caf9cf09a49df08490686f45a710b80e/aiohttp-3.12.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c779e5ebbf0e2e15334ea404fcce54009dc069210164a244d2eac8352a44b28", size = 1717479, upload-time = "2025-07-10T13:03:40.158Z" }, + { url = "https://files.pythonhosted.org/packages/51/e1/7f1c77515d369b7419c5b501196526dad3e72800946c0099594c1f0c20b4/aiohttp-3.12.14-cp311-cp311-win32.whl", hash = "sha256:a289f50bf1bd5be227376c067927f78079a7bdeccf8daa6a9e65c38bae14324b", size = 427907, upload-time = "2025-07-10T13:03:41.801Z" }, + { url = "https://files.pythonhosted.org/packages/06/24/a6bf915c85b7a5b07beba3d42b3282936b51e4578b64a51e8e875643c276/aiohttp-3.12.14-cp311-cp311-win_amd64.whl", hash = "sha256:0b8a69acaf06b17e9c54151a6c956339cf46db4ff72b3ac28516d0f7068f4ced", size = 452334, upload-time = "2025-07-10T13:03:43.485Z" }, + { url = "https://files.pythonhosted.org/packages/c3/0d/29026524e9336e33d9767a1e593ae2b24c2b8b09af7c2bd8193762f76b3e/aiohttp-3.12.14-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a0ecbb32fc3e69bc25efcda7d28d38e987d007096cbbeed04f14a6662d0eee22", size = 701055, upload-time = "2025-07-10T13:03:45.59Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b8/a5e8e583e6c8c1056f4b012b50a03c77a669c2e9bf012b7cf33d6bc4b141/aiohttp-3.12.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0400f0ca9bb3e0b02f6466421f253797f6384e9845820c8b05e976398ac1d81a", size = 475670, upload-time = "2025-07-10T13:03:47.249Z" }, + { url = "https://files.pythonhosted.org/packages/29/e8/5202890c9e81a4ec2c2808dd90ffe024952e72c061729e1d49917677952f/aiohttp-3.12.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a56809fed4c8a830b5cae18454b7464e1529dbf66f71c4772e3cfa9cbec0a1ff", size = 468513, upload-time = "2025-07-10T13:03:49.377Z" }, + { url = "https://files.pythonhosted.org/packages/23/e5/d11db8c23d8923d3484a27468a40737d50f05b05eebbb6288bafcb467356/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f2e373276e4755691a963e5d11756d093e346119f0627c2d6518208483fb6d", size = 1715309, upload-time = "2025-07-10T13:03:51.556Z" }, + { url = "https://files.pythonhosted.org/packages/53/44/af6879ca0eff7a16b1b650b7ea4a827301737a350a464239e58aa7c387ef/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ca39e433630e9a16281125ef57ece6817afd1d54c9f1bf32e901f38f16035869", size = 1697961, upload-time = "2025-07-10T13:03:53.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/94/18457f043399e1ec0e59ad8674c0372f925363059c276a45a1459e17f423/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c748b3f8b14c77720132b2510a7d9907a03c20ba80f469e58d5dfd90c079a1c", size = 1753055, upload-time = "2025-07-10T13:03:55.368Z" }, + { url = "https://files.pythonhosted.org/packages/26/d9/1d3744dc588fafb50ff8a6226d58f484a2242b5dd93d8038882f55474d41/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a568abe1b15ce69d4cc37e23020720423f0728e3cb1f9bcd3f53420ec3bfe7", size = 1799211, upload-time = "2025-07-10T13:03:57.216Z" }, + { url = "https://files.pythonhosted.org/packages/73/12/2530fb2b08773f717ab2d249ca7a982ac66e32187c62d49e2c86c9bba9b4/aiohttp-3.12.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9888e60c2c54eaf56704b17feb558c7ed6b7439bca1e07d4818ab878f2083660", size = 1718649, upload-time = "2025-07-10T13:03:59.469Z" }, + { url = "https://files.pythonhosted.org/packages/b9/34/8d6015a729f6571341a311061b578e8b8072ea3656b3d72329fa0faa2c7c/aiohttp-3.12.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3006a1dc579b9156de01e7916d38c63dc1ea0679b14627a37edf6151bc530088", size = 1634452, upload-time = "2025-07-10T13:04:01.698Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4b/08b83ea02595a582447aeb0c1986792d0de35fe7a22fb2125d65091cbaf3/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa8ec5c15ab80e5501a26719eb48a55f3c567da45c6ea5bb78c52c036b2655c7", size = 1695511, upload-time = "2025-07-10T13:04:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/9c7c31037a063eec13ecf1976185c65d1394ded4a5120dd5965e3473cb21/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:39b94e50959aa07844c7fe2206b9f75d63cc3ad1c648aaa755aa257f6f2498a9", size = 1716967, upload-time = "2025-07-10T13:04:06.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/02/84406e0ad1acb0fb61fd617651ab6de760b2d6a31700904bc0b33bd0894d/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:04c11907492f416dad9885d503fbfc5dcb6768d90cad8639a771922d584609d3", size = 1657620, upload-time = "2025-07-10T13:04:07.944Z" }, + { url = "https://files.pythonhosted.org/packages/07/53/da018f4013a7a179017b9a274b46b9a12cbeb387570f116964f498a6f211/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:88167bd9ab69bb46cee91bd9761db6dfd45b6e76a0438c7e884c3f8160ff21eb", size = 1737179, upload-time = "2025-07-10T13:04:10.182Z" }, + { url = "https://files.pythonhosted.org/packages/49/e8/ca01c5ccfeaafb026d85fa4f43ceb23eb80ea9c1385688db0ef322c751e9/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:791504763f25e8f9f251e4688195e8b455f8820274320204f7eafc467e609425", size = 1765156, upload-time = "2025-07-10T13:04:12.029Z" }, + { url = "https://files.pythonhosted.org/packages/22/32/5501ab525a47ba23c20613e568174d6c63aa09e2caa22cded5c6ea8e3ada/aiohttp-3.12.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2785b112346e435dd3a1a67f67713a3fe692d288542f1347ad255683f066d8e0", size = 1724766, upload-time = "2025-07-10T13:04:13.961Z" }, + { url = "https://files.pythonhosted.org/packages/06/af/28e24574801fcf1657945347ee10df3892311c2829b41232be6089e461e7/aiohttp-3.12.14-cp312-cp312-win32.whl", hash = "sha256:15f5f4792c9c999a31d8decf444e79fcfd98497bf98e94284bf390a7bb8c1729", size = 422641, upload-time = "2025-07-10T13:04:16.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/d5/7ac2464aebd2eecac38dbe96148c9eb487679c512449ba5215d233755582/aiohttp-3.12.14-cp312-cp312-win_amd64.whl", hash = "sha256:3b66e1a182879f579b105a80d5c4bd448b91a57e8933564bf41665064796a338", size = 449316, upload-time = "2025-07-10T13:04:18.289Z" }, + { url = "https://files.pythonhosted.org/packages/06/48/e0d2fa8ac778008071e7b79b93ab31ef14ab88804d7ba71b5c964a7c844e/aiohttp-3.12.14-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3143a7893d94dc82bc409f7308bc10d60285a3cd831a68faf1aa0836c5c3c767", size = 695471, upload-time = "2025-07-10T13:04:20.124Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e7/f73206afa33100804f790b71092888f47df65fd9a4cd0e6800d7c6826441/aiohttp-3.12.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3d62ac3d506cef54b355bd34c2a7c230eb693880001dfcda0bf88b38f5d7af7e", size = 473128, upload-time = "2025-07-10T13:04:21.928Z" }, + { url = "https://files.pythonhosted.org/packages/df/e2/4dd00180be551a6e7ee979c20fc7c32727f4889ee3fd5b0586e0d47f30e1/aiohttp-3.12.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48e43e075c6a438937c4de48ec30fa8ad8e6dfef122a038847456bfe7b947b63", size = 465426, upload-time = "2025-07-10T13:04:24.071Z" }, + { url = "https://files.pythonhosted.org/packages/de/dd/525ed198a0bb674a323e93e4d928443a680860802c44fa7922d39436b48b/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:077b4488411a9724cecc436cbc8c133e0d61e694995b8de51aaf351c7578949d", size = 1704252, upload-time = "2025-07-10T13:04:26.049Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b1/01e542aed560a968f692ab4fc4323286e8bc4daae83348cd63588e4f33e3/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d8c35632575653f297dcbc9546305b2c1133391089ab925a6a3706dfa775ccab", size = 1685514, upload-time = "2025-07-10T13:04:28.186Z" }, + { url = "https://files.pythonhosted.org/packages/b3/06/93669694dc5fdabdc01338791e70452d60ce21ea0946a878715688d5a191/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b8ce87963f0035c6834b28f061df90cf525ff7c9b6283a8ac23acee6502afd4", size = 1737586, upload-time = "2025-07-10T13:04:30.195Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3a/18991048ffc1407ca51efb49ba8bcc1645961f97f563a6c480cdf0286310/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0a2cf66e32a2563bb0766eb24eae7e9a269ac0dc48db0aae90b575dc9583026", size = 1786958, upload-time = "2025-07-10T13:04:32.482Z" }, + { url = "https://files.pythonhosted.org/packages/30/a8/81e237f89a32029f9b4a805af6dffc378f8459c7b9942712c809ff9e76e5/aiohttp-3.12.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdea089caf6d5cde975084a884c72d901e36ef9c2fd972c9f51efbbc64e96fbd", size = 1709287, upload-time = "2025-07-10T13:04:34.493Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e3/bd67a11b0fe7fc12c6030473afd9e44223d456f500f7cf526dbaa259ae46/aiohttp-3.12.14-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7865f27db67d49e81d463da64a59365ebd6b826e0e4847aa111056dcb9dc88", size = 1622990, upload-time = "2025-07-10T13:04:36.433Z" }, + { url = "https://files.pythonhosted.org/packages/83/ba/e0cc8e0f0d9ce0904e3cf2d6fa41904e379e718a013c721b781d53dcbcca/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0ab5b38a6a39781d77713ad930cb5e7feea6f253de656a5f9f281a8f5931b086", size = 1676015, upload-time = "2025-07-10T13:04:38.958Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b3/1e6c960520bda094c48b56de29a3d978254637ace7168dd97ddc273d0d6c/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b3b15acee5c17e8848d90a4ebc27853f37077ba6aec4d8cb4dbbea56d156933", size = 1707678, upload-time = "2025-07-10T13:04:41.275Z" }, + { url = "https://files.pythonhosted.org/packages/0a/19/929a3eb8c35b7f9f076a462eaa9830b32c7f27d3395397665caa5e975614/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e4c972b0bdaac167c1e53e16a16101b17c6d0ed7eac178e653a07b9f7fad7151", size = 1650274, upload-time = "2025-07-10T13:04:43.483Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/81682a6f20dd1b18ce3d747de8eba11cbef9b270f567426ff7880b096b48/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7442488b0039257a3bdbc55f7209587911f143fca11df9869578db6c26feeeb8", size = 1726408, upload-time = "2025-07-10T13:04:45.577Z" }, + { url = "https://files.pythonhosted.org/packages/8c/17/884938dffaa4048302985483f77dfce5ac18339aad9b04ad4aaa5e32b028/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f68d3067eecb64c5e9bab4a26aa11bd676f4c70eea9ef6536b0a4e490639add3", size = 1759879, upload-time = "2025-07-10T13:04:47.663Z" }, + { url = "https://files.pythonhosted.org/packages/95/78/53b081980f50b5cf874359bde707a6eacd6c4be3f5f5c93937e48c9d0025/aiohttp-3.12.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f88d3704c8b3d598a08ad17d06006cb1ca52a1182291f04979e305c8be6c9758", size = 1708770, upload-time = "2025-07-10T13:04:49.944Z" }, + { url = "https://files.pythonhosted.org/packages/ed/91/228eeddb008ecbe3ffa6c77b440597fdf640307162f0c6488e72c5a2d112/aiohttp-3.12.14-cp313-cp313-win32.whl", hash = "sha256:a3c99ab19c7bf375c4ae3debd91ca5d394b98b6089a03231d4c580ef3c2ae4c5", size = 421688, upload-time = "2025-07-10T13:04:51.993Z" }, + { url = "https://files.pythonhosted.org/packages/66/5f/8427618903343402fdafe2850738f735fd1d9409d2a8f9bcaae5e630d3ba/aiohttp-3.12.14-cp313-cp313-win_amd64.whl", hash = "sha256:3f8aad695e12edc9d571f878c62bedc91adf30c760c8632f09663e5f564f4baa", size = 448098, upload-time = "2025-07-10T13:04:53.999Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, +] + +[[package]] +name = "asgiref" +version = "3.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, +] + +[[package]] +name = "async-substrate-interface" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bt-decode" }, + { name = "scalecodec" }, + { name = "websockets" }, + { name = "wheel" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/e3/6c322d4e2138c96b53ce92efa5ea68a6c49ee94b5bac04d6fc9ffde2e343/async_substrate_interface-1.4.1.tar.gz", hash = "sha256:67586fc73cb429e2ddaf7a81cedbf633222b744884f0840de51b2a86298a885f", size = 78730, upload-time = "2025-07-09T21:34:00.756Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/30/ddf530616017795a252170adb66959fed2aa93b3d89b86a4c15faeed13d0/async_substrate_interface-1.4.1-py3-none-any.whl", hash = "sha256:7f4dafbc4ac00f2f4da1f07e462a9b86e98d9bc721dbc4e0e354cd4d82ab19de", size = 81212, upload-time = "2025-07-09T21:33:58.088Z" }, +] + +[[package]] +name = "asyncstdlib" +version = "3.13.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/50/e1/72e388631c85233a2fd890d024fc20a8a9961dbba8614d78266636218f1f/asyncstdlib-3.13.1.tar.gz", hash = "sha256:f47564b9a3566f8f9172631d88c75fe074b0ce2127963b7265d310df9aeed03a", size = 49752, upload-time = "2025-03-09T07:52:51.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/4a/c86c045bc7bb0244044935ba80c83998f1fdee4f4cef64c6b078e043b0e6/asyncstdlib-3.13.1-py3-none-any.whl", hash = "sha256:a64da68176af1da8c699026cad98f70b184f82b4cb39739e0b9701a2a7541cf9", size = 43993, upload-time = "2025-03-09T07:52:50.108Z" }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, +] + +[[package]] +name = "base58" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c", size = 6528, upload-time = "2021-10-30T22:12:17.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2", size = 5621, upload-time = "2021-10-30T22:12:16.658Z" }, +] + +[[package]] +name = "bittensor" +version = "9.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "async-substrate-interface" }, + { name = "asyncstdlib" }, + { name = "bittensor-drand" }, + { name = "bittensor-wallet" }, + { name = "colorama" }, + { name = "fastapi" }, + { name = "msgpack-numpy-opentensor" }, + { name = "munch" }, + { name = "nest-asyncio" }, + { name = "netaddr" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pycryptodome" }, + { name = "pydantic" }, + { name = "python-statemachine" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "retry" }, + { name = "scalecodec" }, + { name = "setuptools" }, + { name = "uvicorn" }, + { name = "wheel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/6c/b17ccb7e7310ef37bcb864cac6112ea76464c5507b2cdd95bd798ed931f3/bittensor-9.8.2.tar.gz", hash = "sha256:c1320e2510eecf600e9fe332a9fe70b1c38f5738dfbcf4253271b9a6840cc8b6", size = 270471, upload-time = "2025-07-10T23:14:12.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/10/0d7672131dca04a368298b830068751b2fb8c09a2fa90125c020de73c827/bittensor-9.8.2-py3-none-any.whl", hash = "sha256:0c608b7c17fd24bf24a0b47f5de6ac397ddfcb0e4004f75d618840a842851cf7", size = 322434, upload-time = "2025-07-10T23:14:10.711Z" }, +] + +[[package]] +name = "bittensor-drand" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ca/b42116013633ad5effa35ff763f3ad658b7eb19f2882e93d13505dd31560/bittensor_drand-0.5.1.tar.gz", hash = "sha256:64ff31eda409062e4f8465d48b20e15b5bfaaff4d9e2088b1ced9ef49d8abf1c", size = 44367, upload-time = "2025-06-26T15:01:19.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/ea/b805380a11fcb9e488f665f6e0b6c51cd0b4d136ff6469f155fb13997e4c/bittensor_drand-0.5.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d516308f492e6b67da726dd10e70d11f6d7d71d392577aa9ce5e2b7eb36d1c24", size = 1948791, upload-time = "2025-06-26T15:01:12.497Z" }, + { url = "https://files.pythonhosted.org/packages/45/fe/1293accc0ef0d479fe5fa8b56e0505eee9439de39adf7b197da743b70468/bittensor_drand-0.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd97b34c0ecac7a0a04b68f035b5e814c6698c15a252beff18d8380c0fe8e7f0", size = 1854974, upload-time = "2025-06-26T15:01:03.352Z" }, + { url = "https://files.pythonhosted.org/packages/87/de/18d7ed1138d21652e7f02e096ac8a573c1889fa9c945c7eb91667e5e5642/bittensor_drand-0.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e1f16d5371d458fa0e8137f9eb420c6095bba436e9cd7225b52a8fd6b23e0a", size = 2078776, upload-time = "2025-06-26T15:00:33.9Z" }, + { url = "https://files.pythonhosted.org/packages/de/bd/d2edfd221ae461f9527411a71fc4ab5bb1c8aaca26d5ae85112584500a8f/bittensor_drand-0.5.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8e1f385c8f8686112ae7f6f1a78bc000c9d085592acebd7fc53f106f6036b48", size = 2178892, upload-time = "2025-06-26T15:00:43.918Z" }, + { url = "https://files.pythonhosted.org/packages/70/68/97f19159f576fad63bd8ae5a3632d488a3173a434771e4ad830c55dab1ed/bittensor_drand-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bd0c93d4aa2a6738e9d162e9d60091e8a577df2a2a9fa917ca3d3b431fcb89f", size = 2113052, upload-time = "2025-06-26T15:00:53.73Z" }, + { url = "https://files.pythonhosted.org/packages/14/8c/372f66da07934816e34a330c014b3c2425f563b787c4e5ebf04fd9005c28/bittensor_drand-0.5.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:edc8264a4f7f685b9b4b587ba7ac315fba9ea36140facd2530bebdb2c732a48c", size = 1947071, upload-time = "2025-06-26T15:01:14.18Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a5/abb3cca09ef1d9e342695d3b66fed922223389e269980ee9f61770b7d33f/bittensor_drand-0.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5afd0f05e2022653e122f46885c64127be01aad47d93006806d5bec7706f7dd7", size = 1849508, upload-time = "2025-06-26T15:01:05.525Z" }, + { url = "https://files.pythonhosted.org/packages/68/2d/299076860f86b0fc5b6679a455a670c905f66158571369f5c9a95e20d3e3/bittensor_drand-0.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f980348177236575f210f1d9ddf3fefb8cb1a5c0d44daa1dc8cfc62a3f78b1", size = 2074567, upload-time = "2025-06-26T15:00:35.681Z" }, + { url = "https://files.pythonhosted.org/packages/be/48/0a7e7c7ecd9a1542836c05ef696943a6196441a811867baf185f888b0ea0/bittensor_drand-0.5.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b349f7f39354dc8780f6d2a1f1221c98d40a8a03c1ea0d765277488cf0d374aa", size = 2172966, upload-time = "2025-06-26T15:00:45.824Z" }, + { url = "https://files.pythonhosted.org/packages/e7/73/3199c94cc3632efb766628ca2d23b9b6f7636778cb740d92bec3550394fc/bittensor_drand-0.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12fd7200073311be6dbecea83a8500f68b84fae6db6a643b4cbf1c61daaf3073", size = 2111652, upload-time = "2025-06-26T15:00:55.718Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ac/a73f901f9e408550301c40efa53604579da00ac6f0a0dc465e9edce41f1a/bittensor_drand-0.5.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:323c3840ab78a4818c6ee2eb681416d9fbfdcb6fabadebedf0c2916e9c52d0de", size = 1946768, upload-time = "2025-06-26T15:01:15.892Z" }, + { url = "https://files.pythonhosted.org/packages/84/46/7216ba0d6f12a754fdd8fad93a3ade42db81131ad98b9ae332ca911eddab/bittensor_drand-0.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6ab6ed02430599358c961819f75c59755dc43f88655f681645c0646f7c2ab94b", size = 1848719, upload-time = "2025-06-26T15:01:07.22Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f6/408fe65420d07574260646ed9e529ca3fb3f836e5b9a3f613866e5077f24/bittensor_drand-0.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af39fb2b157eb52163cd43eaa0a072b84d174b3f9c3bb9547233f52701c53f92", size = 2074320, upload-time = "2025-06-26T15:00:37.835Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/e568408c5fa3f10834112f42513355a6987972012310d21d88573fb8b671/bittensor_drand-0.5.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbaedb5eed2b19f8c85bfa4c907a0c0242a64ba482ebab3a5f1a35add994a842", size = 2172193, upload-time = "2025-06-26T15:00:47.686Z" }, + { url = "https://files.pythonhosted.org/packages/7e/58/d7ec00649041d5eb401c69ecb241d41232da6d5052b08c66215d0be26d20/bittensor_drand-0.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48585bda80b22853ec93bae428a03d16af20dc2a1cf25e432a75fe1f7be570", size = 2110752, upload-time = "2025-06-26T15:00:57.57Z" }, +] + +[[package]] +name = "bittensor-wallet" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/b2/d120b53ff896f1b19d4275a647249c6f0d400bb92636cd6c77738765fb08/bittensor_wallet-3.1.0.tar.gz", hash = "sha256:d18eff3ab8ae64aa8c5641466026f0dc839cc690b55c9de36a9aa3dabf4c86f1", size = 82566, upload-time = "2025-07-07T21:30:32.699Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/75/6a5d6dfd3ab9449a25660428243f6f6f1d33f1d37465a09ca8eda48337b2/bittensor_wallet-3.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5bcf71ece972738cb8c1489ecdcc41e79f80359585fc380db80da2f15e5d4d22", size = 828626, upload-time = "2025-07-07T21:30:28.131Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/059d9adef65aaa3149af16b69363259b1a4e8e7120b74748026cb0f0db6c/bittensor_wallet-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f568a2f5735e0d14885947d4b32dea1c1473037a42e0ab0ceeb2ece4a5866832", size = 774700, upload-time = "2025-07-07T21:30:21.827Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/7fcefa531a3664a1e09347ed176f80cf94299cb12f164bd0097d1ed5925c/bittensor_wallet-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2fca662df88a6e33aa054bea587a1161c93c70f96e5ae7e5545c865d0fcf5ba", size = 2640452, upload-time = "2025-07-07T21:29:58.876Z" }, + { url = "https://files.pythonhosted.org/packages/20/08/c4905736aadbb0d0196220705c5ef5a00d926c2aec4e24e9e60559468e67/bittensor_wallet-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd43596a37a1630a34c44288f853c048825e0a56ba48129fc3b05ff40b631524", size = 3171483, upload-time = "2025-07-07T21:30:13.897Z" }, + { url = "https://files.pythonhosted.org/packages/c1/77/7dcde20a6f4b0a1696ac32f23744832a3ac10e8f05daf8c98452e5056da7/bittensor_wallet-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7a1ee9d89f2e65365879a6ca82bab9d13c354585a0523dfda6496115dd67a335", size = 2973710, upload-time = "2025-07-07T21:30:06.552Z" }, + { url = "https://files.pythonhosted.org/packages/9a/12/a093276e52914b5a3fa7a1e71c417f6d351e61d41bcb0026c883a86a64da/bittensor_wallet-3.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3189aa67b44bcac6758f69697b3ea9046b1b1d2f09408e314633ad6f1c19e8f", size = 827056, upload-time = "2025-07-07T21:30:29.337Z" }, + { url = "https://files.pythonhosted.org/packages/8f/98/51ded830443a16f446c35653fa1dcf770b28b828ef2ef7ce800ca515ec16/bittensor_wallet-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3bfae7e226ec6414d36f15637499ae71d5f3d85ef49c83485a649e7d60e7be29", size = 774828, upload-time = "2025-07-07T21:30:23.39Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/45dfb0597f7836c1fbf4e7d58c36faaeb5b46db2ddef555ffe3abf3e3ce1/bittensor_wallet-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:897b38db021e1ff2c6d7acecda51603074982fdd79c2cfdb38dc3fb4b7e23eeb", size = 2641037, upload-time = "2025-07-07T21:30:00.441Z" }, + { url = "https://files.pythonhosted.org/packages/d8/4d/22c0172adf3833d31321cf889bdc46b8397bf759f958cb5fc043bb219fa4/bittensor_wallet-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e5ba7c71638dc63b9f4d9b050c871712493e809a8bacfd00d2b0ac77e7990d", size = 3171800, upload-time = "2025-07-07T21:30:15.677Z" }, + { url = "https://files.pythonhosted.org/packages/02/e3/b2447fb415d820332c066f4954f1ec862d8daec9e31da0920ba44dd5fbd5/bittensor_wallet-3.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8b0a9ef2427d1e2c9039722883e4576ab3330fc8fa995b5a3421875bc400473", size = 2973520, upload-time = "2025-07-07T21:30:07.919Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e0/edbb15d1705637f57045fba2690e551854a04778f0e5ea292937fc9b58e6/bittensor_wallet-3.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d6249f2b4c65525bd1a43c55b040eaadc661601ae9c4b66b02e588f2a226854a", size = 827318, upload-time = "2025-07-07T21:30:30.584Z" }, + { url = "https://files.pythonhosted.org/packages/99/ba/146a4eb0bfeb1349f3354c6c3aaa3f6f71717ed671f2a663067895502489/bittensor_wallet-3.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0add05532377fb283ccde7d414ed933fbb04ee9a7a3730548ab7af5b61e4087", size = 774719, upload-time = "2025-07-07T21:30:24.468Z" }, + { url = "https://files.pythonhosted.org/packages/12/05/86d1c9051c8ce6e124939122212f408afe12783cfda7272d07987963fb46/bittensor_wallet-3.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60312c08452a0a23387e08df27767acf6960b9d389abca7c7bb9fcf58f3941c3", size = 2641026, upload-time = "2025-07-07T21:30:01.7Z" }, + { url = "https://files.pythonhosted.org/packages/00/d6/1700aee30f4034346e230c067b846b534bad5257971299ffbe1f102e5ebb/bittensor_wallet-3.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e8277bf3c6c3c1acfc6ebdf995f5789c4d12ec41666ea256ec8bfd4940139a", size = 3171655, upload-time = "2025-07-07T21:30:17.165Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/aac0038856609f07421ac92560809c3dbb883832c1a7a1cb5cb96289b699/bittensor_wallet-3.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3c1508df98ddc2499e8c47face30af01311412fe1b05ea4de237a0e07eed16e", size = 2972636, upload-time = "2025-07-07T21:30:09.576Z" }, +] + +[[package]] +name = "bt-decode" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "toml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/cbbe3201561b1467e53bb5a111d968d3364d58633c58009343db9a5c2915/bt_decode-0.6.0.tar.gz", hash = "sha256:05e67b5ab018af7a31651bb9c0fb838c3a1733806823019d14c287922869f84e", size = 1199355, upload-time = "2025-04-03T19:37:21.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/46/2ebd45707fdce2fd6420aff577a7c8205fc787220085de250869906cd5fb/bt_decode-0.6.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cc64e90ca61cb8e512ae503b6d4047ec9f8175f9263ad9241129a39684434dd6", size = 602989, upload-time = "2025-04-03T19:37:15.485Z" }, + { url = "https://files.pythonhosted.org/packages/6d/25/d79c43c11bcca09b7e0310beadd34a4067e286796dc1c6f4be53edc73649/bt_decode-0.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66f69d7309106f1daf44c2d58f646a140b71afac1466bd22ee3dd68776404a40", size = 584933, upload-time = "2025-04-03T19:37:08.944Z" }, + { url = "https://files.pythonhosted.org/packages/de/c8/6aa8913ee67c6c0e18b1ec55235a2c6834c1c1ebebd16b0b50d7c4128231/bt_decode-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc0f027904057002297fda957db88d8d274c2295440e8398171411edc4d7fa92", size = 651808, upload-time = "2025-04-03T19:36:56.346Z" }, + { url = "https://files.pythonhosted.org/packages/8c/06/2e49134be61e4ec263f092b97116d7799d8f7b69b845949086add717984e/bt_decode-0.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c2c45eb2e35fcb931ac49f9ce6472c145fe5b4fc764765d5955f66be5d5c079", size = 711296, upload-time = "2025-04-03T19:36:41.269Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/dc6a34a99da142976feb66f75337ef93068c31f8e9be2b58da82059ade0f/bt_decode-0.6.0-cp311-cp311-win32.whl", hash = "sha256:fedaf3677177b5501ace9bf939966cd7d2d5522d96d27c2021336f58ecdc5e30", size = 416800, upload-time = "2025-04-03T19:37:36.954Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f6/e1ca254d9da56aa1cb28eb16abb627e276092b17eb67c3fa33661d153a35/bt_decode-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:0ba45eef8fd6c27ca236783aa86233ad4792cd0a496d745143d699ab1f4ff456", size = 443888, upload-time = "2025-04-03T19:37:23.919Z" }, + { url = "https://files.pythonhosted.org/packages/dc/00/f5100765c4da5bdca0aff952497eba9eaa98505fae6ba8d60d7e67919e1a/bt_decode-0.6.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b61e4237b58977f6bd4500a0ca52b1ffc405f47498a5d89c70f6a37087e8cabc", size = 595757, upload-time = "2025-04-03T19:37:16.956Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5f/0a973a374bb51bb05e8e665641dd7f65ac600f3904a74a71f7e4251ae2c2/bt_decode-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78534a941d136715a33bdbbbf9d5f7d72ca3d75abe888f449b39e56396cea823", size = 580301, upload-time = "2025-04-03T19:37:10.439Z" }, + { url = "https://files.pythonhosted.org/packages/fb/4a/6333713c66b801f1b15a9548a3444d15de38a1e69f17722bb931a35dfc0b/bt_decode-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e8a90524af52be75778daa91a3bbfeb2da8c7a2791e5adfb5dedbceb5be8889", size = 650787, upload-time = "2025-04-03T19:36:57.761Z" }, + { url = "https://files.pythonhosted.org/packages/57/6d/32e3799dfaf6f57ce61ed2fcf52e97e5883b2bd86ac90cbc8dc53b7c60bd/bt_decode-0.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:636e8a2c5074f43f1a68b75f225ad5434cbffed66d7e289c7efec5f0dde4bb54", size = 711889, upload-time = "2025-04-03T19:36:42.871Z" }, + { url = "https://files.pythonhosted.org/packages/59/a8/3b8ede0181be5dc5e7656ef1575c06dd9616ba35dac90f20baff66cec0d2/bt_decode-0.6.0-cp312-cp312-win32.whl", hash = "sha256:ecbf71fb8786727b7c7835441419c74104473d984505d2bfd5aaa2a78402d47a", size = 416317, upload-time = "2025-04-03T19:37:38.243Z" }, + { url = "https://files.pythonhosted.org/packages/71/d2/ab0f965be493051235d37f3577bad1d4970205d0d942db59b58870231525/bt_decode-0.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:70568c723b2cc900952a5349dddf158f85284b7da37aa0ad398392a34e09e468", size = 443973, upload-time = "2025-04-03T19:37:26.544Z" }, + { url = "https://files.pythonhosted.org/packages/be/1b/60ce86f630b12213e908b2621ffd82aa4b86e46c515caa7cc5a83e4c5a83/bt_decode-0.6.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:859dfa6ab4bfaae733a38d1d4a12a19771e7988bd8681cedc77445ba2bac0ccb", size = 595193, upload-time = "2025-04-03T19:37:18.134Z" }, + { url = "https://files.pythonhosted.org/packages/48/90/4b6397c97224100264167d8a781461daee1f4cecba309d6564fab4cdec58/bt_decode-0.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6c1e55efcaa803897bf262fd4f689e2c3075737614a89545efef7b7ba8a5d10", size = 580131, upload-time = "2025-04-03T19:37:11.616Z" }, + { url = "https://files.pythonhosted.org/packages/e0/96/5bac9e173d861d49048c77695a3698762686c17ccf81c184ea6ac08e6c4e/bt_decode-0.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e217d2e06a03e9d17235ae732c124f6a38717f2eb5e1bf142702c798462712", size = 650040, upload-time = "2025-04-03T19:36:59.098Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5e/a31983fa7fac1a58ca2cd8c2a5f71c986b928af0737778e9cc9def8feedc/bt_decode-0.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f940017dd4a5566fd3be423c5caa38b0c470a65df1da6214bf093f2ca51aa8d5", size = 711143, upload-time = "2025-04-03T19:36:44.509Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f3/bc025f32f33a251b1503809ceb266ce764d7bbb6131814eb4b85b57b5c71/bt_decode-0.6.0-cp313-cp313-win32.whl", hash = "sha256:11ed668036d0df39f49276b4c51b829874f885e4c69c5d159fa88be2e1004f99", size = 416188, upload-time = "2025-04-03T19:37:39.394Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/578e168db9a939f191b88ccf5434cf1c80a6d9e785b93d58db66adb2c950/bt_decode-0.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:5295537d7938bc6d5c138c0ac05d447ae51fd5d8c177ed3bee732c78ce4bc8c6", size = 443417, upload-time = "2025-04-03T19:37:27.714Z" }, +] + +[[package]] +name = "certifi" +version = "2025.7.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386, upload-time = "2025-07-09T02:13:58.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230, upload-time = "2025-07-09T02:13:57.007Z" }, +] + +[[package]] +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, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { 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, upload-time = "2024-09-04T20:44:28.956Z" }, + { 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, upload-time = "2024-09-04T20:44:30.289Z" }, + { 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, upload-time = "2024-09-04T20:44:32.01Z" }, + { 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, upload-time = "2024-09-04T20:44:33.606Z" }, + { 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, upload-time = "2024-09-04T20:44:35.191Z" }, + { 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, upload-time = "2024-09-04T20:44:36.743Z" }, + { 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, upload-time = "2024-09-04T20:44:38.492Z" }, + { 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, upload-time = "2024-09-04T20:44:40.046Z" }, + { 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, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "compute-horde-sdk" +version = "0.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "asgiref" }, + { name = "bittensor-wallet" }, + { name = "cryptography" }, + { name = "httpx" }, + { name = "huggingface-hub", extra = ["hf-transfer"] }, + { name = "pydantic" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/0e/c747a85da26fda8ca622b0c095a8a12aac6457886cc64dfa93a47fa3be89/compute_horde_sdk-0.0.12.tar.gz", hash = "sha256:a8932762670e9033cac1eca1d60c4ef0cd9b48b99de1b1f6fd28f4f39eeaf224", size = 43854, upload-time = "2025-07-23T08:55:34.131Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/1b/8902379da75652ced51ab9773c8e8ff69e37b142e713784708d0abf06a85/compute_horde_sdk-0.0.12-py3-none-any.whl", hash = "sha256:28cce0b68a120386343e62a7646c7926874d06ed896777c5dc996917fe2ea43a", size = 48080, upload-time = "2025-07-23T08:55:32.711Z" }, +] + +[[package]] +name = "cryptography" +version = "45.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/1e/49527ac611af559665f71cbb8f92b332b5ec9c6fbc4e88b0f8e92f5e85df/cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a", size = 744903, upload-time = "2025-07-02T13:06:25.941Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/fb/09e28bc0c46d2c547085e60897fea96310574c70fb21cd58a730a45f3403/cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8", size = 7043092, upload-time = "2025-07-02T13:05:01.514Z" }, + { url = "https://files.pythonhosted.org/packages/b1/05/2194432935e29b91fb649f6149c1a4f9e6d3d9fc880919f4ad1bcc22641e/cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d", size = 4205926, upload-time = "2025-07-02T13:05:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/07/8b/9ef5da82350175e32de245646b1884fc01124f53eb31164c77f95a08d682/cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5", size = 4429235, upload-time = "2025-07-02T13:05:07.084Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e1/c809f398adde1994ee53438912192d92a1d0fc0f2d7582659d9ef4c28b0c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57", size = 4209785, upload-time = "2025-07-02T13:05:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8b/07eb6bd5acff58406c5e806eff34a124936f41a4fb52909ffa4d00815f8c/cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0", size = 3893050, upload-time = "2025-07-02T13:05:11.069Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ef/3333295ed58d900a13c92806b67e62f27876845a9a908c939f040887cca9/cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d", size = 4457379, upload-time = "2025-07-02T13:05:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9d/44080674dee514dbb82b21d6fa5d1055368f208304e2ab1828d85c9de8f4/cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9", size = 4209355, upload-time = "2025-07-02T13:05:15.017Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d8/0749f7d39f53f8258e5c18a93131919ac465ee1f9dccaf1b3f420235e0b5/cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27", size = 4456087, upload-time = "2025-07-02T13:05:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/09/d7/92acac187387bf08902b0bf0699816f08553927bdd6ba3654da0010289b4/cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e", size = 4332873, upload-time = "2025-07-02T13:05:18.743Z" }, + { url = "https://files.pythonhosted.org/packages/03/c2/840e0710da5106a7c3d4153c7215b2736151bba60bf4491bdb421df5056d/cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174", size = 4564651, upload-time = "2025-07-02T13:05:21.382Z" }, + { url = "https://files.pythonhosted.org/packages/2e/92/cc723dd6d71e9747a887b94eb3827825c6c24b9e6ce2bb33b847d31d5eaa/cryptography-45.0.5-cp311-abi3-win32.whl", hash = "sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9", size = 2929050, upload-time = "2025-07-02T13:05:23.39Z" }, + { url = "https://files.pythonhosted.org/packages/1f/10/197da38a5911a48dd5389c043de4aec4b3c94cb836299b01253940788d78/cryptography-45.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63", size = 3403224, upload-time = "2025-07-02T13:05:25.202Z" }, + { url = "https://files.pythonhosted.org/packages/fe/2b/160ce8c2765e7a481ce57d55eba1546148583e7b6f85514472b1d151711d/cryptography-45.0.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8", size = 7017143, upload-time = "2025-07-02T13:05:27.229Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e7/2187be2f871c0221a81f55ee3105d3cf3e273c0a0853651d7011eada0d7e/cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd", size = 4197780, upload-time = "2025-07-02T13:05:29.299Z" }, + { url = "https://files.pythonhosted.org/packages/b9/cf/84210c447c06104e6be9122661159ad4ce7a8190011669afceeaea150524/cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e", size = 4420091, upload-time = "2025-07-02T13:05:31.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6a/cb8b5c8bb82fafffa23aeff8d3a39822593cee6e2f16c5ca5c2ecca344f7/cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0", size = 4198711, upload-time = "2025-07-02T13:05:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/04/f7/36d2d69df69c94cbb2473871926daf0f01ad8e00fe3986ac3c1e8c4ca4b3/cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135", size = 3883299, upload-time = "2025-07-02T13:05:34.94Z" }, + { url = "https://files.pythonhosted.org/packages/82/c7/f0ea40f016de72f81288e9fe8d1f6748036cb5ba6118774317a3ffc6022d/cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7", size = 4450558, upload-time = "2025-07-02T13:05:37.288Z" }, + { url = "https://files.pythonhosted.org/packages/06/ae/94b504dc1a3cdf642d710407c62e86296f7da9e66f27ab12a1ee6fdf005b/cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42", size = 4198020, upload-time = "2025-07-02T13:05:39.102Z" }, + { url = "https://files.pythonhosted.org/packages/05/2b/aaf0adb845d5dabb43480f18f7ca72e94f92c280aa983ddbd0bcd6ecd037/cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492", size = 4449759, upload-time = "2025-07-02T13:05:41.398Z" }, + { url = "https://files.pythonhosted.org/packages/91/e4/f17e02066de63e0100a3a01b56f8f1016973a1d67551beaf585157a86b3f/cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0", size = 4319991, upload-time = "2025-07-02T13:05:43.64Z" }, + { url = "https://files.pythonhosted.org/packages/f2/2e/e2dbd629481b499b14516eed933f3276eb3239f7cee2dcfa4ee6b44d4711/cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a", size = 4554189, upload-time = "2025-07-02T13:05:46.045Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ea/a78a0c38f4c8736287b71c2ea3799d173d5ce778c7d6e3c163a95a05ad2a/cryptography-45.0.5-cp37-abi3-win32.whl", hash = "sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f", size = 2911769, upload-time = "2025-07-02T13:05:48.329Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/28ac139109d9005ad3f6b6f8976ffede6706a6478e21c889ce36c840918e/cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97", size = 3390016, upload-time = "2025-07-02T13:05:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/c0/71/9bdbcfd58d6ff5084687fe722c58ac718ebedbc98b9f8f93781354e6d286/cryptography-45.0.5-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e", size = 3587878, upload-time = "2025-07-02T13:06:06.339Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/83516cfb87f4a8756eaa4203f93b283fda23d210fc14e1e594bd5f20edb6/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6", size = 4152447, upload-time = "2025-07-02T13:06:08.345Z" }, + { url = "https://files.pythonhosted.org/packages/22/11/d2823d2a5a0bd5802b3565437add16f5c8ce1f0778bf3822f89ad2740a38/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18", size = 4386778, upload-time = "2025-07-02T13:06:10.263Z" }, + { url = "https://files.pythonhosted.org/packages/5f/38/6bf177ca6bce4fe14704ab3e93627c5b0ca05242261a2e43ef3168472540/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463", size = 4151627, upload-time = "2025-07-02T13:06:13.097Z" }, + { url = "https://files.pythonhosted.org/packages/38/6a/69fc67e5266bff68a91bcb81dff8fb0aba4d79a78521a08812048913e16f/cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1", size = 4385593, upload-time = "2025-07-02T13:06:15.689Z" }, + { url = "https://files.pythonhosted.org/packages/f6/34/31a1604c9a9ade0fdab61eb48570e09a796f4d9836121266447b0eaf7feb/cryptography-45.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f", size = 3331106, upload-time = "2025-07-02T13:06:18.058Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "fastapi" +version = "0.110.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/22/7b9ee50b0a8c48f076a111d6e4071a9d4c25623dc67689c5f3aa375f779b/fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626", size = 287508, upload-time = "2024-04-30T00:38:02.576Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/d1/5958526c3bdbed74f88bf69b86506db5b25a600207f0f688473667690de6/fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32", size = 91834, upload-time = "2024-04-30T00:37:58.935Z" }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" }, + { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" }, + { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" }, + { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" }, + { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" }, + { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" }, + { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" }, + { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" }, + { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" }, + { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" }, + { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" }, + { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" }, + { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" }, + { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" }, + { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" }, + { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" }, + { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" }, + { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" }, + { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" }, + { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" }, + { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" }, + { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" }, + { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" }, + { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" }, + { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" }, + { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" }, + { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" }, + { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" }, + { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" }, + { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" }, + { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" }, + { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" }, + { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" }, + { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" }, + { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" }, + { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" }, + { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" }, + { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" }, + { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" }, + { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" }, + { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" }, + { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" }, + { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" }, + { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" }, + { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" }, + { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" }, + { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" }, + { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, +] + +[[package]] +name = "fsspec" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033, upload-time = "2025-05-24T12:03:23.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052, upload-time = "2025-05-24T12:03:21.66Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-transfer" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/eb/8fc64f40388c29ce8ce3b2b180a089d4d6b25b1d0d232d016704cb852104/hf_transfer-0.1.9.tar.gz", hash = "sha256:035572865dab29d17e783fbf1e84cf1cb24f3fcf8f1b17db1cfc7fdf139f02bf", size = 25201, upload-time = "2025-01-07T10:05:12.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/78/0dce00208f585fae675f40033ef9a30dedfa83665d5ac79f16beb4a0a6c2/hf_transfer-0.1.9-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:6e94e8822da79573c9b6ae4d6b2f847c59a7a06c5327d7db20751b68538dc4f6", size = 1386084, upload-time = "2025-01-07T10:04:47.874Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2e/3d60b1a9e9f29a2152aa66c823bf5e399ae7be3fef310ff0de86779c5d2d/hf_transfer-0.1.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ebc4ab9023414880c8b1d3c38174d1c9989eb5022d37e814fa91a3060123eb0", size = 1343558, upload-time = "2025-01-07T10:04:42.313Z" }, + { url = "https://files.pythonhosted.org/packages/fb/38/130a5ac3747f104033591bcac1c961cb1faadfdc91704f59b09c0b465ff2/hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8674026f21ed369aa2a0a4b46000aca850fc44cd2b54af33a172ce5325b4fc82", size = 3726676, upload-time = "2025-01-07T10:04:11.539Z" }, + { url = "https://files.pythonhosted.org/packages/15/a1/f4e27c5ad17aac616ae0849e2aede5aae31db8267a948c6b3eeb9fd96446/hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a736dfbb2c84f5a2c975478ad200c0c8bfcb58a25a35db402678fb87ce17fa4", size = 3062920, upload-time = "2025-01-07T10:04:16.297Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0d/727abdfba39bc3f1132cfa4c970588c2c0bb0d82fe2d645cc10f4e2f8e0b/hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:504b8427fd785dd8546d53b9fafe6e436bd7a3adf76b9dce556507650a7b4567", size = 3578681, upload-time = "2025-01-07T10:04:29.702Z" }, + { url = "https://files.pythonhosted.org/packages/50/d0/2b213eb1ea8b1252ccaf1a6c804d0aba03fea38aae4124df6a3acb70511a/hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c7fc1b85f4d0f76e452765d7648c9f4bfd0aedb9ced2ae1ebfece2d8cfaf8e2", size = 3398837, upload-time = "2025-01-07T10:04:22.778Z" }, + { url = "https://files.pythonhosted.org/packages/8c/8a/79dbce9006e0bd6b74516f97451a7b7c64dbbb426df15d901dd438cfeee3/hf_transfer-0.1.9-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d991376f0eac70a60f0cbc95602aa708a6f7c8617f28b4945c1431d67b8e3c8", size = 3546986, upload-time = "2025-01-07T10:04:36.415Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f7/9ac239b6ee6fe0bad130325d987a93ea58c4118e50479f0786f1733b37e8/hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e6ac4eddcd99575ed3735ed911ddf9d1697e2bd13aa3f0ad7e3904dd4863842e", size = 4071715, upload-time = "2025-01-07T10:04:53.224Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a3/0ed697279f5eeb7a40f279bd783cf50e6d0b91f24120dcf66ef2cf8822b4/hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:57fd9880da1ee0f47250f735f791fab788f0aa1ee36afc49f761349869c8b4d9", size = 3388081, upload-time = "2025-01-07T10:04:57.818Z" }, + { url = "https://files.pythonhosted.org/packages/dc/eb/47e477bdf1d784f31c7540db6cc8c354b777e51a186897a7abda34517f36/hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:5d561f0520f493c66b016d99ceabe69c23289aa90be38dd802d2aef279f15751", size = 3658654, upload-time = "2025-01-07T10:05:03.168Z" }, + { url = "https://files.pythonhosted.org/packages/45/07/6661e43fbee09594a8a5e9bb778107d95fe38dac4c653982afe03d32bd4d/hf_transfer-0.1.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a5b366d34cd449fe9b20ef25941e6eef0460a2f74e7389f02e673e1f88ebd538", size = 3690551, upload-time = "2025-01-07T10:05:09.238Z" }, + { url = "https://files.pythonhosted.org/packages/81/f5/461d2e5f307e5048289b1168d5c642ae3bb2504e88dff1a38b92ed990a21/hf_transfer-0.1.9-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e66acf91df4a8b72f60223059df3003062a5ae111757187ed1a06750a30e911b", size = 1393046, upload-time = "2025-01-07T10:04:51.003Z" }, + { url = "https://files.pythonhosted.org/packages/41/ba/8d9fd9f1083525edfcb389c93738c802f3559cb749324090d7109c8bf4c2/hf_transfer-0.1.9-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8669dbcc7a3e2e8d61d42cd24da9c50d57770bd74b445c65123291ca842a7e7a", size = 1348126, upload-time = "2025-01-07T10:04:45.712Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a2/cd7885bc9959421065a6fae0fe67b6c55becdeda4e69b873e52976f9a9f0/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fd0167c4407a3bc4cdd0307e65ada2294ec04f1813d8a69a5243e379b22e9d8", size = 3728604, upload-time = "2025-01-07T10:04:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2e/a072cf196edfeda3310c9a5ade0a0fdd785e6154b3ce24fc738c818da2a7/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee8b10afedcb75f71091bcc197c526a6ebf5c58bbbadb34fdeee6160f55f619f", size = 3064995, upload-time = "2025-01-07T10:04:18.663Z" }, + { url = "https://files.pythonhosted.org/packages/c2/84/aec9ef4c0fab93c1ea2b1badff38c78b4b2f86f0555b26d2051dbc920cde/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5828057e313de59300dd1abb489444bc452efe3f479d3c55b31a8f680936ba42", size = 3580908, upload-time = "2025-01-07T10:04:32.834Z" }, + { url = "https://files.pythonhosted.org/packages/29/63/b560d39651a56603d64f1a0212d0472a44cbd965db2fa62b99d99cb981bf/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc6bd19e1cc177c66bdef15ef8636ad3bde79d5a4f608c158021153b4573509d", size = 3400839, upload-time = "2025-01-07T10:04:26.122Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d8/f87ea6f42456254b48915970ed98e993110521e9263472840174d32c880d/hf_transfer-0.1.9-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca9bfb89e6f8f281890cc61a8aff2d3cecaff7e1a4d275574d96ca70098557", size = 3552664, upload-time = "2025-01-07T10:04:40.123Z" }, + { url = "https://files.pythonhosted.org/packages/d6/56/1267c39b65fc8f4e2113b36297320f102718bf5799b544a6cbe22013aa1d/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:89a23f58b7b7effbc047b8ca286f131b17728c99a9f972723323003ffd1bb916", size = 4073732, upload-time = "2025-01-07T10:04:55.624Z" }, + { url = "https://files.pythonhosted.org/packages/82/1a/9c748befbe3decf7cb415e34f8a0c3789a0a9c55910dea73d581e48c0ce5/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dc7fff1345980d6c0ebb92c811d24afa4b98b3e07ed070c8e38cc91fd80478c5", size = 3390096, upload-time = "2025-01-07T10:04:59.98Z" }, + { url = "https://files.pythonhosted.org/packages/72/85/4c03da147b6b4b7cb12e074d3d44eee28604a387ed0eaf7eaaead5069c57/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:1a6bd16c667ebe89a069ca163060127a794fa3a3525292c900b8c8cc47985b0d", size = 3664743, upload-time = "2025-01-07T10:05:05.416Z" }, + { url = "https://files.pythonhosted.org/packages/e7/6e/e597b04f753f1b09e6893075d53a82a30c13855cbaa791402695b01e369f/hf_transfer-0.1.9-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d2fde99d502093ade3ab1b53f80da18480e9902aa960dab7f74fb1b9e5bc5746", size = 3695243, upload-time = "2025-01-07T10:05:11.411Z" }, + { url = "https://files.pythonhosted.org/packages/09/89/d4e234727a26b2546c8fb70a276cd924260d60135f2165bf8b9ed67bb9a4/hf_transfer-0.1.9-cp38-abi3-win32.whl", hash = "sha256:435cc3cdc8524ce57b074032b8fd76eed70a4224d2091232fa6a8cef8fd6803e", size = 1086605, upload-time = "2025-01-07T10:05:18.873Z" }, + { url = "https://files.pythonhosted.org/packages/a1/14/f1e15b851d1c2af5b0b1a82bf8eb10bda2da62d98180220ba6fd8879bb5b/hf_transfer-0.1.9-cp38-abi3-win_amd64.whl", hash = "sha256:16f208fc678911c37e11aa7b586bc66a37d02e636208f18b6bc53d29b5df40ad", size = 1160240, upload-time = "2025-01-07T10:05:14.324Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/d4/7685999e85945ed0d7f0762b686ae7015035390de1161dcea9d5276c134c/hf_xet-1.1.5.tar.gz", hash = "sha256:69ebbcfd9ec44fdc2af73441619eeb06b94ee34511bbcf57cd423820090f5694", size = 495969, upload-time = "2025-06-20T21:48:38.007Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/89/a1119eebe2836cb25758e7661d6410d3eae982e2b5e974bcc4d250be9012/hf_xet-1.1.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f52c2fa3635b8c37c7764d8796dfa72706cc4eded19d638331161e82b0792e23", size = 2687929, upload-time = "2025-06-20T21:48:32.284Z" }, + { url = "https://files.pythonhosted.org/packages/de/5f/2c78e28f309396e71ec8e4e9304a6483dcbc36172b5cea8f291994163425/hf_xet-1.1.5-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9fa6e3ee5d61912c4a113e0708eaaef987047616465ac7aa30f7121a48fc1af8", size = 2556338, upload-time = "2025-06-20T21:48:30.079Z" }, + { url = "https://files.pythonhosted.org/packages/6d/2f/6cad7b5fe86b7652579346cb7f85156c11761df26435651cbba89376cd2c/hf_xet-1.1.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc874b5c843e642f45fd85cda1ce599e123308ad2901ead23d3510a47ff506d1", size = 3102894, upload-time = "2025-06-20T21:48:28.114Z" }, + { url = "https://files.pythonhosted.org/packages/d0/54/0fcf2b619720a26fbb6cc941e89f2472a522cd963a776c089b189559447f/hf_xet-1.1.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dbba1660e5d810bd0ea77c511a99e9242d920790d0e63c0e4673ed36c4022d18", size = 3002134, upload-time = "2025-06-20T21:48:25.906Z" }, + { url = "https://files.pythonhosted.org/packages/f3/92/1d351ac6cef7c4ba8c85744d37ffbfac2d53d0a6c04d2cabeba614640a78/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ab34c4c3104133c495785d5d8bba3b1efc99de52c02e759cf711a91fd39d3a14", size = 3171009, upload-time = "2025-06-20T21:48:33.987Z" }, + { url = "https://files.pythonhosted.org/packages/c9/65/4b2ddb0e3e983f2508528eb4501288ae2f84963586fbdfae596836d5e57a/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:83088ecea236d5113de478acb2339f92c95b4fb0462acaa30621fac02f5a534a", size = 3279245, upload-time = "2025-06-20T21:48:36.051Z" }, + { url = "https://files.pythonhosted.org/packages/f0/55/ef77a85ee443ae05a9e9cba1c9f0dd9241eb42da2aeba1dc50f51154c81a/hf_xet-1.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:73e167d9807d166596b4b2f0b585c6d5bd84a26dea32843665a8b58f6edba245", size = 2738931, upload-time = "2025-06-20T21:48:39.482Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/26/bb526d4d14c2774fe07113ca1db7255737ffbb119315839af2065abfdac3/httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069", size = 199029, upload-time = "2024-10-16T19:44:18.427Z" }, + { url = "https://files.pythonhosted.org/packages/a6/17/3e0d3e9b901c732987a45f4f94d4e2c62b89a041d93db89eafb262afd8d5/httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a", size = 103492, upload-time = "2024-10-16T19:44:19.515Z" }, + { url = "https://files.pythonhosted.org/packages/b7/24/0fe235d7b69c42423c7698d086d4db96475f9b50b6ad26a718ef27a0bce6/httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975", size = 462891, upload-time = "2024-10-16T19:44:21.067Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2f/205d1f2a190b72da6ffb5f41a3736c26d6fa7871101212b15e9b5cd8f61d/httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636", size = 459788, upload-time = "2024-10-16T19:44:22.958Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4c/d09ce0eff09057a206a74575ae8f1e1e2f0364d20e2442224f9e6612c8b9/httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721", size = 433214, upload-time = "2024-10-16T19:44:24.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/84c9e23edbccc4a4c6f96a1b8d99dfd2350289e94f00e9ccc7aadde26fb5/httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988", size = 434120, upload-time = "2024-10-16T19:44:26.295Z" }, + { url = "https://files.pythonhosted.org/packages/d0/46/4d8e7ba9581416de1c425b8264e2cadd201eb709ec1584c381f3e98f51c1/httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17", size = 88565, upload-time = "2024-10-16T19:44:29.188Z" }, + { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683, upload-time = "2024-10-16T19:44:30.175Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337, upload-time = "2024-10-16T19:44:31.786Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796, upload-time = "2024-10-16T19:44:32.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837, upload-time = "2024-10-16T19:44:33.974Z" }, + { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289, upload-time = "2024-10-16T19:44:35.111Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779, upload-time = "2024-10-16T19:44:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634, upload-time = "2024-10-16T19:44:37.357Z" }, + { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, + { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, + { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "0.33.2" +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/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637, upload-time = "2025-07-02T06:26:05.156Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373, upload-time = "2025-07-02T06:26:03.072Z" }, +] + +[package.optional-dependencies] +hf-transfer = [ + { name = "hf-transfer" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/a0/834b0cebabbfc7e311f30b46c8188790a37f89fc8d756660346fe5abfd09/more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", size = 127671, upload-time = "2025-04-22T14:17:41.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" }, +] + +[[package]] +name = "msgpack" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/b1/ea4f68038a18c77c9467400d166d74c4ffa536f34761f7983a104357e614/msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd", size = 173555, upload-time = "2025-06-13T06:52:51.324Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/83/97f24bf9848af23fe2ba04380388216defc49a8af6da0c28cc636d722502/msgpack-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:71ef05c1726884e44f8b1d1773604ab5d4d17729d8491403a705e649116c9558", size = 82728, upload-time = "2025-06-13T06:51:50.68Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/2eaa388267a78401f6e182662b08a588ef4f3de6f0eab1ec09736a7aaa2b/msgpack-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:36043272c6aede309d29d56851f8841ba907a1a3d04435e43e8a19928e243c1d", size = 79279, upload-time = "2025-06-13T06:51:51.72Z" }, + { url = "https://files.pythonhosted.org/packages/f8/46/31eb60f4452c96161e4dfd26dbca562b4ec68c72e4ad07d9566d7ea35e8a/msgpack-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a32747b1b39c3ac27d0670122b57e6e57f28eefb725e0b625618d1b59bf9d1e0", size = 423859, upload-time = "2025-06-13T06:51:52.749Z" }, + { url = "https://files.pythonhosted.org/packages/45/16/a20fa8c32825cc7ae8457fab45670c7a8996d7746ce80ce41cc51e3b2bd7/msgpack-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a8b10fdb84a43e50d38057b06901ec9da52baac6983d3f709d8507f3889d43f", size = 429975, upload-time = "2025-06-13T06:51:53.97Z" }, + { url = "https://files.pythonhosted.org/packages/86/ea/6c958e07692367feeb1a1594d35e22b62f7f476f3c568b002a5ea09d443d/msgpack-1.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0c325c3f485dc54ec298d8b024e134acf07c10d494ffa24373bea729acf704", size = 413528, upload-time = "2025-06-13T06:51:55.507Z" }, + { url = "https://files.pythonhosted.org/packages/75/05/ac84063c5dae79722bda9f68b878dc31fc3059adb8633c79f1e82c2cd946/msgpack-1.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:88daaf7d146e48ec71212ce21109b66e06a98e5e44dca47d853cbfe171d6c8d2", size = 413338, upload-time = "2025-06-13T06:51:57.023Z" }, + { url = "https://files.pythonhosted.org/packages/69/e8/fe86b082c781d3e1c09ca0f4dacd457ede60a13119b6ce939efe2ea77b76/msgpack-1.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8b55ea20dc59b181d3f47103f113e6f28a5e1c89fd5b67b9140edb442ab67f2", size = 422658, upload-time = "2025-06-13T06:51:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2b/bafc9924df52d8f3bb7c00d24e57be477f4d0f967c0a31ef5e2225e035c7/msgpack-1.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a28e8072ae9779f20427af07f53bbb8b4aa81151054e882aee333b158da8752", size = 427124, upload-time = "2025-06-13T06:51:59.969Z" }, + { url = "https://files.pythonhosted.org/packages/a2/3b/1f717e17e53e0ed0b68fa59e9188f3f610c79d7151f0e52ff3cd8eb6b2dc/msgpack-1.1.1-cp311-cp311-win32.whl", hash = "sha256:7da8831f9a0fdb526621ba09a281fadc58ea12701bc709e7b8cbc362feabc295", size = 65016, upload-time = "2025-06-13T06:52:01.294Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/9d1780768d3b249accecc5a38c725eb1e203d44a191f7b7ff1941f7df60c/msgpack-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fd1b58e1431008a57247d6e7cc4faa41c3607e8e7d4aaf81f7c29ea013cb458", size = 72267, upload-time = "2025-06-13T06:52:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/389b9c593eda2b8551b2e7126ad3a06af6f9b44274eb3a4f054d48ff7e47/msgpack-1.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ae497b11f4c21558d95de9f64fff7053544f4d1a17731c866143ed6bb4591238", size = 82359, upload-time = "2025-06-13T06:52:03.909Z" }, + { url = "https://files.pythonhosted.org/packages/ab/65/7d1de38c8a22cf8b1551469159d4b6cf49be2126adc2482de50976084d78/msgpack-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33be9ab121df9b6b461ff91baac6f2731f83d9b27ed948c5b9d1978ae28bf157", size = 79172, upload-time = "2025-06-13T06:52:05.246Z" }, + { url = "https://files.pythonhosted.org/packages/0f/bd/cacf208b64d9577a62c74b677e1ada005caa9b69a05a599889d6fc2ab20a/msgpack-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f64ae8fe7ffba251fecb8408540c34ee9df1c26674c50c4544d72dbf792e5ce", size = 425013, upload-time = "2025-06-13T06:52:06.341Z" }, + { url = "https://files.pythonhosted.org/packages/4d/ec/fd869e2567cc9c01278a736cfd1697941ba0d4b81a43e0aa2e8d71dab208/msgpack-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a494554874691720ba5891c9b0b39474ba43ffb1aaf32a5dac874effb1619e1a", size = 426905, upload-time = "2025-06-13T06:52:07.501Z" }, + { url = "https://files.pythonhosted.org/packages/55/2a/35860f33229075bce803a5593d046d8b489d7ba2fc85701e714fc1aaf898/msgpack-1.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb643284ab0ed26f6957d969fe0dd8bb17beb567beb8998140b5e38a90974f6c", size = 407336, upload-time = "2025-06-13T06:52:09.047Z" }, + { url = "https://files.pythonhosted.org/packages/8c/16/69ed8f3ada150bf92745fb4921bd621fd2cdf5a42e25eb50bcc57a5328f0/msgpack-1.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d275a9e3c81b1093c060c3837e580c37f47c51eca031f7b5fb76f7b8470f5f9b", size = 409485, upload-time = "2025-06-13T06:52:10.382Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b6/0c398039e4c6d0b2e37c61d7e0e9d13439f91f780686deb8ee64ecf1ae71/msgpack-1.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fd6b577e4541676e0cc9ddc1709d25014d3ad9a66caa19962c4f5de30fc09ef", size = 412182, upload-time = "2025-06-13T06:52:11.644Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/0cf4a6ecb9bc960d624c93effaeaae75cbf00b3bc4a54f35c8507273cda1/msgpack-1.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb29aaa613c0a1c40d1af111abf025f1732cab333f96f285d6a93b934738a68a", size = 419883, upload-time = "2025-06-13T06:52:12.806Z" }, + { url = "https://files.pythonhosted.org/packages/62/83/9697c211720fa71a2dfb632cad6196a8af3abea56eece220fde4674dc44b/msgpack-1.1.1-cp312-cp312-win32.whl", hash = "sha256:870b9a626280c86cff9c576ec0d9cbcc54a1e5ebda9cd26dab12baf41fee218c", size = 65406, upload-time = "2025-06-13T06:52:14.271Z" }, + { url = "https://files.pythonhosted.org/packages/c0/23/0abb886e80eab08f5e8c485d6f13924028602829f63b8f5fa25a06636628/msgpack-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5692095123007180dca3e788bb4c399cc26626da51629a31d40207cb262e67f4", size = 72558, upload-time = "2025-06-13T06:52:15.252Z" }, + { url = "https://files.pythonhosted.org/packages/a1/38/561f01cf3577430b59b340b51329803d3a5bf6a45864a55f4ef308ac11e3/msgpack-1.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3765afa6bd4832fc11c3749be4ba4b69a0e8d7b728f78e68120a157a4c5d41f0", size = 81677, upload-time = "2025-06-13T06:52:16.64Z" }, + { url = "https://files.pythonhosted.org/packages/09/48/54a89579ea36b6ae0ee001cba8c61f776451fad3c9306cd80f5b5c55be87/msgpack-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8ddb2bcfd1a8b9e431c8d6f4f7db0773084e107730ecf3472f1dfe9ad583f3d9", size = 78603, upload-time = "2025-06-13T06:52:17.843Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/daba2699b308e95ae792cdc2ef092a38eb5ee422f9d2fbd4101526d8a210/msgpack-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:196a736f0526a03653d829d7d4c5500a97eea3648aebfd4b6743875f28aa2af8", size = 420504, upload-time = "2025-06-13T06:52:18.982Z" }, + { url = "https://files.pythonhosted.org/packages/20/22/2ebae7ae43cd8f2debc35c631172ddf14e2a87ffcc04cf43ff9df9fff0d3/msgpack-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d592d06e3cc2f537ceeeb23d38799c6ad83255289bb84c2e5792e5a8dea268a", size = 423749, upload-time = "2025-06-13T06:52:20.211Z" }, + { url = "https://files.pythonhosted.org/packages/40/1b/54c08dd5452427e1179a40b4b607e37e2664bca1c790c60c442c8e972e47/msgpack-1.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4df2311b0ce24f06ba253fda361f938dfecd7b961576f9be3f3fbd60e87130ac", size = 404458, upload-time = "2025-06-13T06:52:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/2e/60/6bb17e9ffb080616a51f09928fdd5cac1353c9becc6c4a8abd4e57269a16/msgpack-1.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e4141c5a32b5e37905b5940aacbc59739f036930367d7acce7a64e4dec1f5e0b", size = 405976, upload-time = "2025-06-13T06:52:22.995Z" }, + { url = "https://files.pythonhosted.org/packages/ee/97/88983e266572e8707c1f4b99c8fd04f9eb97b43f2db40e3172d87d8642db/msgpack-1.1.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b1ce7f41670c5a69e1389420436f41385b1aa2504c3b0c30620764b15dded2e7", size = 408607, upload-time = "2025-06-13T06:52:24.152Z" }, + { url = "https://files.pythonhosted.org/packages/bc/66/36c78af2efaffcc15a5a61ae0df53a1d025f2680122e2a9eb8442fed3ae4/msgpack-1.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4147151acabb9caed4e474c3344181e91ff7a388b888f1e19ea04f7e73dc7ad5", size = 424172, upload-time = "2025-06-13T06:52:25.704Z" }, + { url = "https://files.pythonhosted.org/packages/8c/87/a75eb622b555708fe0427fab96056d39d4c9892b0c784b3a721088c7ee37/msgpack-1.1.1-cp313-cp313-win32.whl", hash = "sha256:500e85823a27d6d9bba1d057c871b4210c1dd6fb01fbb764e37e4e8847376323", size = 65347, upload-time = "2025-06-13T06:52:26.846Z" }, + { url = "https://files.pythonhosted.org/packages/ca/91/7dc28d5e2a11a5ad804cf2b7f7a5fcb1eb5a4966d66a5d2b41aee6376543/msgpack-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:6d489fba546295983abd142812bda76b57e33d0b9f5d5b71c09a583285506f69", size = 72341, upload-time = "2025-06-13T06:52:27.835Z" }, +] + +[[package]] +name = "msgpack-numpy-opentensor" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msgpack" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/69/2a6af13c3be6934a9ba149120a78bf63cf1455ddb1d11ec2cc5e5d6f8186/msgpack-numpy-opentensor-0.5.0.tar.gz", hash = "sha256:213232c20e2efd528ec8a9882b605e8ad87cfc35b57dfcfefe05d33aaaabe574", size = 9661, upload-time = "2023-10-02T19:01:38.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/22/590508afb85d5c27ebcb2837410413f4613eebdda6e4e02997fe08ba78e4/msgpack_numpy_opentensor-0.5.0-py2.py3-none-any.whl", hash = "sha256:8a61c597a976425a87094d8e89846aa9528eb1f037e97ff1428fe3cd61a238e7", size = 7209, upload-time = "2023-10-02T19:01:37.417Z" }, +] + +[[package]] +name = "multidict" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006, upload-time = "2025-06-30T15:53:46.929Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/f0/1a39863ced51f639c81a5463fbfa9eb4df59c20d1a8769ab9ef4ca57ae04/multidict-6.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18f4eba0cbac3546b8ae31e0bbc55b02c801ae3cbaf80c247fcdd89b456ff58c", size = 76445, upload-time = "2025-06-30T15:51:24.01Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0e/a7cfa451c7b0365cd844e90b41e21fab32edaa1e42fc0c9f68461ce44ed7/multidict-6.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef43b5dd842382329e4797c46f10748d8c2b6e0614f46b4afe4aee9ac33159df", size = 44610, upload-time = "2025-06-30T15:51:25.158Z" }, + { url = "https://files.pythonhosted.org/packages/c6/bb/a14a4efc5ee748cc1904b0748be278c31b9295ce5f4d2ef66526f410b94d/multidict-6.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bd1fd5eec01494e0f2e8e446a74a85d5e49afb63d75a9934e4a5423dba21d", size = 44267, upload-time = "2025-06-30T15:51:26.326Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f8/410677d563c2d55e063ef74fe578f9d53fe6b0a51649597a5861f83ffa15/multidict-6.6.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5bd8d6f793a787153956cd35e24f60485bf0651c238e207b9a54f7458b16d539", size = 230004, upload-time = "2025-06-30T15:51:27.491Z" }, + { url = "https://files.pythonhosted.org/packages/fd/df/2b787f80059314a98e1ec6a4cc7576244986df3e56b3c755e6fc7c99e038/multidict-6.6.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bf99b4daf908c73856bd87ee0a2499c3c9a3d19bb04b9c6025e66af3fd07462", size = 247196, upload-time = "2025-06-30T15:51:28.762Z" }, + { url = "https://files.pythonhosted.org/packages/05/f2/f9117089151b9a8ab39f9019620d10d9718eec2ac89e7ca9d30f3ec78e96/multidict-6.6.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b9e59946b49dafaf990fd9c17ceafa62976e8471a14952163d10a7a630413a9", size = 225337, upload-time = "2025-06-30T15:51:30.025Z" }, + { url = "https://files.pythonhosted.org/packages/93/2d/7115300ec5b699faa152c56799b089a53ed69e399c3c2d528251f0aeda1a/multidict-6.6.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e2db616467070d0533832d204c54eea6836a5e628f2cb1e6dfd8cd6ba7277cb7", size = 257079, upload-time = "2025-06-30T15:51:31.716Z" }, + { url = "https://files.pythonhosted.org/packages/15/ea/ff4bab367623e39c20d3b07637225c7688d79e4f3cc1f3b9f89867677f9a/multidict-6.6.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7394888236621f61dcdd25189b2768ae5cc280f041029a5bcf1122ac63df79f9", size = 255461, upload-time = "2025-06-30T15:51:33.029Z" }, + { url = "https://files.pythonhosted.org/packages/74/07/2c9246cda322dfe08be85f1b8739646f2c4c5113a1422d7a407763422ec4/multidict-6.6.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f114d8478733ca7388e7c7e0ab34b72547476b97009d643644ac33d4d3fe1821", size = 246611, upload-time = "2025-06-30T15:51:34.47Z" }, + { url = "https://files.pythonhosted.org/packages/a8/62/279c13d584207d5697a752a66ffc9bb19355a95f7659140cb1b3cf82180e/multidict-6.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdf22e4db76d323bcdc733514bf732e9fb349707c98d341d40ebcc6e9318ef3d", size = 243102, upload-time = "2025-06-30T15:51:36.525Z" }, + { url = "https://files.pythonhosted.org/packages/69/cc/e06636f48c6d51e724a8bc8d9e1db5f136fe1df066d7cafe37ef4000f86a/multidict-6.6.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e995a34c3d44ab511bfc11aa26869b9d66c2d8c799fa0e74b28a473a692532d6", size = 238693, upload-time = "2025-06-30T15:51:38.278Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/66c9d8fb9acf3b226cdd468ed009537ac65b520aebdc1703dd6908b19d33/multidict-6.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:766a4a5996f54361d8d5a9050140aa5362fe48ce51c755a50c0bc3706460c430", size = 246582, upload-time = "2025-06-30T15:51:39.709Z" }, + { url = "https://files.pythonhosted.org/packages/cf/01/c69e0317be556e46257826d5449feb4e6aa0d18573e567a48a2c14156f1f/multidict-6.6.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3893a0d7d28a7fe6ca7a1f760593bc13038d1d35daf52199d431b61d2660602b", size = 253355, upload-time = "2025-06-30T15:51:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/c0/da/9cc1da0299762d20e626fe0042e71b5694f9f72d7d3f9678397cbaa71b2b/multidict-6.6.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:934796c81ea996e61914ba58064920d6cad5d99140ac3167901eb932150e2e56", size = 247774, upload-time = "2025-06-30T15:51:42.291Z" }, + { url = "https://files.pythonhosted.org/packages/e6/91/b22756afec99cc31105ddd4a52f95ab32b1a4a58f4d417979c570c4a922e/multidict-6.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ed948328aec2072bc00f05d961ceadfd3e9bfc2966c1319aeaf7b7c21219183", size = 242275, upload-time = "2025-06-30T15:51:43.642Z" }, + { url = "https://files.pythonhosted.org/packages/be/f1/adcc185b878036a20399d5be5228f3cbe7f823d78985d101d425af35c800/multidict-6.6.3-cp311-cp311-win32.whl", hash = "sha256:9f5b28c074c76afc3e4c610c488e3493976fe0e596dd3db6c8ddfbb0134dcac5", size = 41290, upload-time = "2025-06-30T15:51:45.264Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d4/27652c1c6526ea6b4f5ddd397e93f4232ff5de42bea71d339bc6a6cc497f/multidict-6.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc7f6fbc61b1c16050a389c630da0b32fc6d4a3d191394ab78972bf5edc568c2", size = 45942, upload-time = "2025-06-30T15:51:46.377Z" }, + { url = "https://files.pythonhosted.org/packages/16/18/23f4932019804e56d3c2413e237f866444b774b0263bcb81df2fdecaf593/multidict-6.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:d4e47d8faffaae822fb5cba20937c048d4f734f43572e7079298a6c39fb172cb", size = 42880, upload-time = "2025-06-30T15:51:47.561Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a0/6b57988ea102da0623ea814160ed78d45a2645e4bbb499c2896d12833a70/multidict-6.6.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:056bebbeda16b2e38642d75e9e5310c484b7c24e3841dc0fb943206a72ec89d6", size = 76514, upload-time = "2025-06-30T15:51:48.728Z" }, + { url = "https://files.pythonhosted.org/packages/07/7a/d1e92665b0850c6c0508f101f9cf0410c1afa24973e1115fe9c6a185ebf7/multidict-6.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e5f481cccb3c5c5e5de5d00b5141dc589c1047e60d07e85bbd7dea3d4580d63f", size = 45394, upload-time = "2025-06-30T15:51:49.986Z" }, + { url = "https://files.pythonhosted.org/packages/52/6f/dd104490e01be6ef8bf9573705d8572f8c2d2c561f06e3826b081d9e6591/multidict-6.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10bea2ee839a759ee368b5a6e47787f399b41e70cf0c20d90dfaf4158dfb4e55", size = 43590, upload-time = "2025-06-30T15:51:51.331Z" }, + { url = "https://files.pythonhosted.org/packages/44/fe/06e0e01b1b0611e6581b7fd5a85b43dacc08b6cea3034f902f383b0873e5/multidict-6.6.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2334cfb0fa9549d6ce2c21af2bfbcd3ac4ec3646b1b1581c88e3e2b1779ec92b", size = 237292, upload-time = "2025-06-30T15:51:52.584Z" }, + { url = "https://files.pythonhosted.org/packages/ce/71/4f0e558fb77696b89c233c1ee2d92f3e1d5459070a0e89153c9e9e804186/multidict-6.6.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8fee016722550a2276ca2cb5bb624480e0ed2bd49125b2b73b7010b9090e888", size = 258385, upload-time = "2025-06-30T15:51:53.913Z" }, + { url = "https://files.pythonhosted.org/packages/e3/25/cca0e68228addad24903801ed1ab42e21307a1b4b6dd2cf63da5d3ae082a/multidict-6.6.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5511cb35f5c50a2db21047c875eb42f308c5583edf96bd8ebf7d770a9d68f6d", size = 242328, upload-time = "2025-06-30T15:51:55.672Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a3/46f2d420d86bbcb8fe660b26a10a219871a0fbf4d43cb846a4031533f3e0/multidict-6.6.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:712b348f7f449948e0a6c4564a21c7db965af900973a67db432d724619b3c680", size = 268057, upload-time = "2025-06-30T15:51:57.037Z" }, + { url = "https://files.pythonhosted.org/packages/9e/73/1c743542fe00794a2ec7466abd3f312ccb8fad8dff9f36d42e18fb1ec33e/multidict-6.6.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e15d2138ee2694e038e33b7c3da70e6b0ad8868b9f8094a72e1414aeda9c1a", size = 269341, upload-time = "2025-06-30T15:51:59.111Z" }, + { url = "https://files.pythonhosted.org/packages/a4/11/6ec9dcbe2264b92778eeb85407d1df18812248bf3506a5a1754bc035db0c/multidict-6.6.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8df25594989aebff8a130f7899fa03cbfcc5d2b5f4a461cf2518236fe6f15961", size = 256081, upload-time = "2025-06-30T15:52:00.533Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2b/631b1e2afeb5f1696846d747d36cda075bfdc0bc7245d6ba5c319278d6c4/multidict-6.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:159ca68bfd284a8860f8d8112cf0521113bffd9c17568579e4d13d1f1dc76b65", size = 253581, upload-time = "2025-06-30T15:52:02.43Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0e/7e3b93f79efeb6111d3bf9a1a69e555ba1d07ad1c11bceb56b7310d0d7ee/multidict-6.6.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e098c17856a8c9ade81b4810888c5ad1914099657226283cab3062c0540b0643", size = 250750, upload-time = "2025-06-30T15:52:04.26Z" }, + { url = "https://files.pythonhosted.org/packages/ad/9e/086846c1d6601948e7de556ee464a2d4c85e33883e749f46b9547d7b0704/multidict-6.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:67c92ed673049dec52d7ed39f8cf9ebbadf5032c774058b4406d18c8f8fe7063", size = 251548, upload-time = "2025-06-30T15:52:06.002Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7b/86ec260118e522f1a31550e87b23542294880c97cfbf6fb18cc67b044c66/multidict-6.6.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:bd0578596e3a835ef451784053cfd327d607fc39ea1a14812139339a18a0dbc3", size = 262718, upload-time = "2025-06-30T15:52:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bd/22ce8f47abb0be04692c9fc4638508b8340987b18691aa7775d927b73f72/multidict-6.6.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:346055630a2df2115cd23ae271910b4cae40f4e336773550dca4889b12916e75", size = 259603, upload-time = "2025-06-30T15:52:09.58Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/91b7ac1691be95cd1f4a26e36a74b97cda6aa9820632d31aab4410f46ebd/multidict-6.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:555ff55a359302b79de97e0468e9ee80637b0de1fce77721639f7cd9440b3a10", size = 251351, upload-time = "2025-06-30T15:52:10.947Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5c/4d7adc739884f7a9fbe00d1eac8c034023ef8bad71f2ebe12823ca2e3649/multidict-6.6.3-cp312-cp312-win32.whl", hash = "sha256:73ab034fb8d58ff85c2bcbadc470efc3fafeea8affcf8722855fb94557f14cc5", size = 41860, upload-time = "2025-06-30T15:52:12.334Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a3/0fbc7afdf7cb1aa12a086b02959307848eb6bcc8f66fcb66c0cb57e2a2c1/multidict-6.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:04cbcce84f63b9af41bad04a54d4cc4e60e90c35b9e6ccb130be2d75b71f8c17", size = 45982, upload-time = "2025-06-30T15:52:13.6Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/8c825bd70ff9b02462dc18d1295dd08d3e9e4eb66856d292ffa62cfe1920/multidict-6.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:0f1130b896ecb52d2a1e615260f3ea2af55fa7dc3d7c3003ba0c3121a759b18b", size = 43210, upload-time = "2025-06-30T15:52:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/52/1d/0bebcbbb4f000751fbd09957257903d6e002943fc668d841a4cf2fb7f872/multidict-6.6.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55", size = 75843, upload-time = "2025-06-30T15:52:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/07/8f/cbe241b0434cfe257f65c2b1bcf9e8d5fb52bc708c5061fb29b0fed22bdf/multidict-6.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b", size = 45053, upload-time = "2025-06-30T15:52:17.429Z" }, + { url = "https://files.pythonhosted.org/packages/32/d2/0b3b23f9dbad5b270b22a3ac3ea73ed0a50ef2d9a390447061178ed6bdb8/multidict-6.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65", size = 43273, upload-time = "2025-06-30T15:52:19.346Z" }, + { url = "https://files.pythonhosted.org/packages/fd/fe/6eb68927e823999e3683bc49678eb20374ba9615097d085298fd5b386564/multidict-6.6.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3", size = 237124, upload-time = "2025-06-30T15:52:20.773Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/320d8507e7726c460cb77117848b3834ea0d59e769f36fdae495f7669929/multidict-6.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c", size = 256892, upload-time = "2025-06-30T15:52:22.242Z" }, + { url = "https://files.pythonhosted.org/packages/76/60/38ee422db515ac69834e60142a1a69111ac96026e76e8e9aa347fd2e4591/multidict-6.6.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6", size = 240547, upload-time = "2025-06-30T15:52:23.736Z" }, + { url = "https://files.pythonhosted.org/packages/27/fb/905224fde2dff042b030c27ad95a7ae744325cf54b890b443d30a789b80e/multidict-6.6.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8", size = 266223, upload-time = "2025-06-30T15:52:25.185Z" }, + { url = "https://files.pythonhosted.org/packages/76/35/dc38ab361051beae08d1a53965e3e1a418752fc5be4d3fb983c5582d8784/multidict-6.6.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca", size = 267262, upload-time = "2025-06-30T15:52:26.969Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a3/0a485b7f36e422421b17e2bbb5a81c1af10eac1d4476f2ff92927c730479/multidict-6.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884", size = 254345, upload-time = "2025-06-30T15:52:28.467Z" }, + { url = "https://files.pythonhosted.org/packages/b4/59/bcdd52c1dab7c0e0d75ff19cac751fbd5f850d1fc39172ce809a74aa9ea4/multidict-6.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7", size = 252248, upload-time = "2025-06-30T15:52:29.938Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a4/2d96aaa6eae8067ce108d4acee6f45ced5728beda55c0f02ae1072c730d1/multidict-6.6.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b", size = 250115, upload-time = "2025-06-30T15:52:31.416Z" }, + { url = "https://files.pythonhosted.org/packages/25/d2/ed9f847fa5c7d0677d4f02ea2c163d5e48573de3f57bacf5670e43a5ffaa/multidict-6.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c", size = 249649, upload-time = "2025-06-30T15:52:32.996Z" }, + { url = "https://files.pythonhosted.org/packages/1f/af/9155850372563fc550803d3f25373308aa70f59b52cff25854086ecb4a79/multidict-6.6.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b", size = 261203, upload-time = "2025-06-30T15:52:34.521Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/c6a728f699896252cf309769089568a33c6439626648843f78743660709d/multidict-6.6.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1", size = 258051, upload-time = "2025-06-30T15:52:35.999Z" }, + { url = "https://files.pythonhosted.org/packages/d0/60/689880776d6b18fa2b70f6cc74ff87dd6c6b9b47bd9cf74c16fecfaa6ad9/multidict-6.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6", size = 249601, upload-time = "2025-06-30T15:52:37.473Z" }, + { url = "https://files.pythonhosted.org/packages/75/5e/325b11f2222a549019cf2ef879c1f81f94a0d40ace3ef55cf529915ba6cc/multidict-6.6.3-cp313-cp313-win32.whl", hash = "sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e", size = 41683, upload-time = "2025-06-30T15:52:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ad/cf46e73f5d6e3c775cabd2a05976547f3f18b39bee06260369a42501f053/multidict-6.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9", size = 45811, upload-time = "2025-06-30T15:52:40.207Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c9/2e3fe950db28fb7c62e1a5f46e1e38759b072e2089209bc033c2798bb5ec/multidict-6.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600", size = 43056, upload-time = "2025-06-30T15:52:41.575Z" }, + { url = "https://files.pythonhosted.org/packages/3a/58/aaf8114cf34966e084a8cc9517771288adb53465188843d5a19862cb6dc3/multidict-6.6.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134", size = 82811, upload-time = "2025-06-30T15:52:43.281Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/5402e7b58a1f5b987a07ad98f2501fdba2a4f4b4c30cf114e3ce8db64c87/multidict-6.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37", size = 48304, upload-time = "2025-06-30T15:52:45.026Z" }, + { url = "https://files.pythonhosted.org/packages/39/65/ab3c8cafe21adb45b24a50266fd747147dec7847425bc2a0f6934b3ae9ce/multidict-6.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8", size = 46775, upload-time = "2025-06-30T15:52:46.459Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/9fcc1b332f67cc0c0c8079e263bfab6660f87fe4e28a35921771ff3eea0d/multidict-6.6.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1", size = 229773, upload-time = "2025-06-30T15:52:47.88Z" }, + { url = "https://files.pythonhosted.org/packages/a4/14/0145a251f555f7c754ce2dcbcd012939bbd1f34f066fa5d28a50e722a054/multidict-6.6.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373", size = 250083, upload-time = "2025-06-30T15:52:49.366Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d4/d5c0bd2bbb173b586c249a151a26d2fb3ec7d53c96e42091c9fef4e1f10c/multidict-6.6.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e", size = 228980, upload-time = "2025-06-30T15:52:50.903Z" }, + { url = "https://files.pythonhosted.org/packages/21/32/c9a2d8444a50ec48c4733ccc67254100c10e1c8ae8e40c7a2d2183b59b97/multidict-6.6.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f", size = 257776, upload-time = "2025-06-30T15:52:52.764Z" }, + { url = "https://files.pythonhosted.org/packages/68/d0/14fa1699f4ef629eae08ad6201c6b476098f5efb051b296f4c26be7a9fdf/multidict-6.6.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0", size = 256882, upload-time = "2025-06-30T15:52:54.596Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/84a27570fbe303c65607d517a5f147cd2fc046c2d1da02b84b17b9bdc2aa/multidict-6.6.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc", size = 247816, upload-time = "2025-06-30T15:52:56.175Z" }, + { url = "https://files.pythonhosted.org/packages/1c/60/dca352a0c999ce96a5d8b8ee0b2b9f729dcad2e0b0c195f8286269a2074c/multidict-6.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f", size = 245341, upload-time = "2025-06-30T15:52:57.752Z" }, + { url = "https://files.pythonhosted.org/packages/50/ef/433fa3ed06028f03946f3993223dada70fb700f763f70c00079533c34578/multidict-6.6.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471", size = 235854, upload-time = "2025-06-30T15:52:59.74Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1f/487612ab56fbe35715320905215a57fede20de7db40a261759690dc80471/multidict-6.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2", size = 243432, upload-time = "2025-06-30T15:53:01.602Z" }, + { url = "https://files.pythonhosted.org/packages/da/6f/ce8b79de16cd885c6f9052c96a3671373d00c59b3ee635ea93e6e81b8ccf/multidict-6.6.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648", size = 252731, upload-time = "2025-06-30T15:53:03.517Z" }, + { url = "https://files.pythonhosted.org/packages/bb/fe/a2514a6aba78e5abefa1624ca85ae18f542d95ac5cde2e3815a9fbf369aa/multidict-6.6.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d", size = 247086, upload-time = "2025-06-30T15:53:05.48Z" }, + { url = "https://files.pythonhosted.org/packages/8c/22/b788718d63bb3cce752d107a57c85fcd1a212c6c778628567c9713f9345a/multidict-6.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c", size = 243338, upload-time = "2025-06-30T15:53:07.522Z" }, + { url = "https://files.pythonhosted.org/packages/22/d6/fdb3d0670819f2228f3f7d9af613d5e652c15d170c83e5f1c94fbc55a25b/multidict-6.6.3-cp313-cp313t-win32.whl", hash = "sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e", size = 47812, upload-time = "2025-06-30T15:53:09.263Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d6/a9d2c808f2c489ad199723197419207ecbfbc1776f6e155e1ecea9c883aa/multidict-6.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d", size = 53011, upload-time = "2025-06-30T15:53:11.038Z" }, + { url = "https://files.pythonhosted.org/packages/f2/40/b68001cba8188dd267590a111f9661b6256debc327137667e832bf5d66e8/multidict-6.6.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb", size = 45254, upload-time = "2025-06-30T15:53:12.421Z" }, + { url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313, upload-time = "2025-06-30T15:53:45.437Z" }, +] + +[[package]] +name = "munch" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/a1/ec48010724eedfe2add68eb7592a0d238590e14e08b95a4ffb3c7b2f0808/munch-2.5.0.tar.gz", hash = "sha256:2d735f6f24d4dba3417fa448cae40c6e896ec1fdab6cdb5e6510999758a4dbd2", size = 17015, upload-time = "2019-10-30T09:56:08.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/ab/85d8da5c9a45e072301beb37ad7f833cd344e04c817d97e0cc75681d248f/munch-2.5.0-py2.py3-none-any.whl", hash = "sha256:6f44af89a2ce4ed04ff8de41f70b226b984db10a91dcc7b9ac2efc1c77022fdd", size = 10347, upload-time = "2019-10-30T09:56:06.835Z" }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "netaddr" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/90/188b2a69654f27b221fba92fda7217778208532c962509e959a9cee5229d/netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a", size = 2260504, upload-time = "2024-05-28T21:30:37.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cc/f4fe2c7ce68b92cbf5b2d379ca366e1edae38cccaad00f69f529b460c3ef/netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe", size = 2262023, upload-time = "2024-05-28T21:30:34.191Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/c7/87c64d7ab426156530676000c94784ef55676df2f13b2796f97722464124/numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", size = 21199346, upload-time = "2025-06-21T11:47:47.57Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/0966c2f44beeac12af8d836e5b5f826a407cf34c45cb73ddcdfce9f5960b/numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", size = 14361143, upload-time = "2025-06-21T11:48:10.766Z" }, + { url = "https://files.pythonhosted.org/packages/7d/31/6e35a247acb1bfc19226791dfc7d4c30002cd4e620e11e58b0ddf836fe52/numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", size = 5378989, upload-time = "2025-06-21T11:48:19.998Z" }, + { url = "https://files.pythonhosted.org/packages/b0/25/93b621219bb6f5a2d4e713a824522c69ab1f06a57cd571cda70e2e31af44/numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", size = 6912890, upload-time = "2025-06-21T11:48:31.376Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/6b06ed98d11fb32e27fb59468b42383f3877146d3ee639f733776b6ac596/numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", size = 14569032, upload-time = "2025-06-21T11:48:52.563Z" }, + { url = "https://files.pythonhosted.org/packages/75/c9/9bec03675192077467a9c7c2bdd1f2e922bd01d3a69b15c3a0fdcd8548f6/numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", size = 16930354, upload-time = "2025-06-21T11:49:17.473Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e2/5756a00cabcf50a3f527a0c968b2b4881c62b1379223931853114fa04cda/numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", size = 15879605, upload-time = "2025-06-21T11:49:41.161Z" }, + { url = "https://files.pythonhosted.org/packages/ff/86/a471f65f0a86f1ca62dcc90b9fa46174dd48f50214e5446bc16a775646c5/numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", size = 18666994, upload-time = "2025-06-21T11:50:08.516Z" }, + { url = "https://files.pythonhosted.org/packages/43/a6/482a53e469b32be6500aaf61cfafd1de7a0b0d484babf679209c3298852e/numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", size = 6603672, upload-time = "2025-06-21T11:50:19.584Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fb/bb613f4122c310a13ec67585c70e14b03bfc7ebabd24f4d5138b97371d7c/numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", size = 13024015, upload-time = "2025-06-21T11:50:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/51/58/2d842825af9a0c041aca246dc92eb725e1bc5e1c9ac89712625db0c4e11c/numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", size = 10456989, upload-time = "2025-06-21T11:50:55.616Z" }, + { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664, upload-time = "2025-06-21T12:15:30.845Z" }, + { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078, upload-time = "2025-06-21T12:15:52.23Z" }, + { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554, upload-time = "2025-06-21T12:16:01.434Z" }, + { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560, upload-time = "2025-06-21T12:16:11.895Z" }, + { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638, upload-time = "2025-06-21T12:16:32.611Z" }, + { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729, upload-time = "2025-06-21T12:16:57.439Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330, upload-time = "2025-06-21T12:17:20.638Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734, upload-time = "2025-06-21T12:17:47.938Z" }, + { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411, upload-time = "2025-06-21T12:17:58.475Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973, upload-time = "2025-06-21T12:18:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491, upload-time = "2025-06-21T12:18:33.585Z" }, + { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" }, + { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" }, + { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" }, + { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" }, + { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" }, + { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" }, + { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" }, + { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" }, + { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" }, + { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" }, + { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" }, + { url = "https://files.pythonhosted.org/packages/e8/34/facc13b9b42ddca30498fc51f7f73c3d0f2be179943a4b4da8686e259740/numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", size = 21070637, upload-time = "2025-06-21T12:26:12.518Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/41b705d9dbae04649b529fc9bd3387664c3281c7cd78b404a4efe73dcc45/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", size = 5304087, upload-time = "2025-06-21T12:26:22.294Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/fe3ac1902bff7a4934a22d49e1c9d71a623204d654d4cc43c6e8fe337fcb/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", size = 6817588, upload-time = "2025-06-21T12:26:32.939Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ee/89bedf69c36ace1ac8f59e97811c1f5031e179a37e4821c3a230bf750142/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", size = 14399010, upload-time = "2025-06-21T12:26:54.086Z" }, + { url = "https://files.pythonhosted.org/packages/15/08/e00e7070ede29b2b176165eba18d6f9784d5349be3c0c1218338e79c27fd/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", size = 16752042, upload-time = "2025-06-21T12:27:19.018Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/1c6b515a83d5564b1698a61efa245727c8feecf308f4091f565988519d20/numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", size = 12927246, upload-time = "2025-06-21T12:27:38.618Z" }, +] + +[[package]] +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, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" }, + { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" }, + { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" }, + { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" }, + { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" }, + { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" }, + { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" }, + { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" }, + { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" }, + { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" }, + { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" }, + { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" }, + { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" }, + { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" }, + { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" }, + { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" }, + { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" }, + { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" }, + { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" }, + { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" }, + { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" }, + { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" }, + { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" }, + { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" }, + { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" }, + { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" }, + { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" }, + { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" }, + { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" }, + { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" }, + { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" }, + { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, +] + +[[package]] +name = "py" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/ff/fec109ceb715d2a6b4c4a85a61af3b40c723a961e8828319fbcb15b868dc/py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", size = 207796, upload-time = "2021-11-04T17:17:01.377Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378", size = 98708, upload-time = "2021-11-04T17:17:00.152Z" }, +] + +[[package]] +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, upload-time = "2024-03-30T13:22:22.564Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" }, + { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" }, + { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" }, + { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" }, + { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" }, + { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" }, + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +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/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, +] + +[[package]] +name = "python-statemachine" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/91/4f05f3931d1e9b1df71b17dc08c43feddf2bed7dbf13f95323df2cc8e340/python_statemachine-2.5.0.tar.gz", hash = "sha256:ae88cd22e47930b92b983a2176e61d811e571b69897be2568ec812c2885fb93a", size = 403718, upload-time = "2024-12-03T17:58:49.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/2d/1c95ebe84df60d630f8e855d1df2c66368805444ac167e9b50f29eabe917/python_statemachine-2.5.0-py3-none-any.whl", hash = "sha256:0ed53846802c17037fcb2a92323f4bc0c833290fa9d17a3587c50886c1541e62", size = 50415, upload-time = "2024-12-03T17:58:47.375Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "retry" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "decorator" }, + { name = "py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/72/75d0b85443fbc8d9f38d08d2b1b67cc184ce35280e4a3813cda2f445f3a4/retry-0.9.2.tar.gz", hash = "sha256:f8bfa8b99b69c4506d6f5bd3b0aabf77f98cdb17f3c9fc3f5ca820033336fba4", size = 6448, upload-time = "2016-05-11T13:58:51.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/0d/53aea75710af4528a25ed6837d71d117602b01946b307a3912cb3cfcbcba/retry-0.9.2-py2.py3-none-any.whl", hash = "sha256:ccddf89761fa2c726ab29391837d4327f819ea14d244c232a1d24c67a2f98606", size = 7986, upload-time = "2016-05-11T13:58:39.925Z" }, +] + +[[package]] +name = "scalecodec" +version = "1.2.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "more-itertools" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/7c/703893e7a8751318517a3dd8c0c060b2c30ffa33f4ab5dd6a4ed483f7967/scalecodec-1.2.11.tar.gz", hash = "sha256:99a2cdbfccdcaf22bd86b86da55a730a2855514ad2309faef4a4a93ac6cbeb8d", size = 150260, upload-time = "2024-07-05T11:48:46.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/60/2a903fa9ed3dfc842240da22969a25b16ea213ed3ee25b7ba8ae1cba20c7/scalecodec-1.2.11-py3-none-any.whl", hash = "sha256:d15c94965f617caa25096f83a45f5f73031d05e6ee08d6039969f0a64fc35de1", size = 99164, upload-time = "2024-07-05T11:48:42.777Z" }, +] + +[[package]] +name = "setuptools" +version = "70.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/60/5db2249526c9b453c5bb8b9f6965fcab0ddb7f40ad734420b3b421f7da44/setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0", size = 2265182, upload-time = "2024-05-21T10:28:18.891Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/88/70c5767a0e43eb4451c2200f07d042a4bcd7639276003a9c54a68cfcc1f8/setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4", size = 863432, upload-time = "2024-05-21T10:28:12.781Z" }, +] + +[[package]] +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, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "starlette" +version = "0.37.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/b5/6bceb93ff20bd7ca36e6f7c540581abb18f53130fabb30ba526e26fd819b/starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823", size = 2843736, upload-time = "2024-03-05T16:16:54.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/18/31fa32ed6c68ba66220204ef0be798c349d0a20c1901f9d4a794e08c76d8/starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee", size = 71908, upload-time = "2024-03-05T16:16:50.957Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, +] + +[[package]] +name = "toml" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/19/5cbd78eac8b1783671c40e34bb0fa83133a06d340a38b55c645076d40094/toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", size = 16719, upload-time = "2018-10-04T02:36:43.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/12/ced7105d2de62fa7c8fb5fce92cc4ce66b57c95fb875e9318dba7f8c5db0/toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", size = 25796, upload-time = "2018-10-04T02:36:40.681Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/a7/4cf0334105c1160dd6819f3297f8700fda7fc30ab4f61fbf3e725acbc7cc/uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8", size = 1447410, upload-time = "2024-10-14T23:37:33.612Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7c/1517b0bbc2dbe784b563d6ab54f2ef88c890fdad77232c98ed490aa07132/uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0", size = 805476, upload-time = "2024-10-14T23:37:36.11Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ea/0bfae1aceb82a503f358d8d2fa126ca9dbdb2ba9c7866974faec1cb5875c/uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e", size = 3960855, upload-time = "2024-10-14T23:37:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ca/0864176a649838b838f36d44bf31c451597ab363b60dc9e09c9630619d41/uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb", size = 3973185, upload-time = "2024-10-14T23:37:40.226Z" }, + { url = "https://files.pythonhosted.org/packages/30/bf/08ad29979a936d63787ba47a540de2132169f140d54aa25bc8c3df3e67f4/uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6", size = 3820256, upload-time = "2024-10-14T23:37:42.839Z" }, + { url = "https://files.pythonhosted.org/packages/da/e2/5cf6ef37e3daf2f06e651aae5ea108ad30df3cb269102678b61ebf1fdf42/uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d", size = 3937323, upload-time = "2024-10-14T23:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284, upload-time = "2024-10-14T23:37:47.833Z" }, + { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349, upload-time = "2024-10-14T23:37:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089, upload-time = "2024-10-14T23:37:51.703Z" }, + { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770, upload-time = "2024-10-14T23:37:54.122Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321, upload-time = "2024-10-14T23:37:55.766Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022, upload-time = "2024-10-14T23:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, + { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, + { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, + { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, + { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, +] + +[[package]] +name = "volume-manager-poc" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "bittensor" }, + { name = "compute-horde-sdk" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "pydantic" }, + { name = "pytest" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.optional-dependencies] +dev = [ + { name = "httpx" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, +] + +[package.metadata] +requires-dist = [ + { name = "bittensor", specifier = ">=9.8.2" }, + { name = "compute-horde-sdk" }, + { name = "fastapi", specifier = ">=0.104.0" }, + { name = "httpx", specifier = ">=0.25.0" }, + { name = "httpx", marker = "extra == 'dev'", specifier = ">=0.25.0" }, + { name = "huggingface-hub", specifier = ">=0.19.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, +] +provides-extras = ["dev"] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { 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", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { 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", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "wheel" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241, upload-time = "2024-08-17T09:20:38.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1", size = 31969, upload-time = "2024-08-17T09:18:00.852Z" }, + { url = "https://files.pythonhosted.org/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8", size = 30800, upload-time = "2024-08-17T09:18:01.863Z" }, + { url = "https://files.pythonhosted.org/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166", size = 221566, upload-time = "2024-08-17T09:18:03.461Z" }, + { url = "https://files.pythonhosted.org/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7", size = 201214, upload-time = "2024-08-17T09:18:05.616Z" }, + { url = "https://files.pythonhosted.org/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623", size = 429433, upload-time = "2024-08-17T09:18:06.957Z" }, + { url = "https://files.pythonhosted.org/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a", size = 194822, upload-time = "2024-08-17T09:18:08.331Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88", size = 208538, upload-time = "2024-08-17T09:18:10.332Z" }, + { url = "https://files.pythonhosted.org/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c", size = 216953, upload-time = "2024-08-17T09:18:11.707Z" }, + { url = "https://files.pythonhosted.org/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2", size = 203594, upload-time = "2024-08-17T09:18:13.799Z" }, + { url = "https://files.pythonhosted.org/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084", size = 210971, upload-time = "2024-08-17T09:18:15.824Z" }, + { url = "https://files.pythonhosted.org/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d", size = 415050, upload-time = "2024-08-17T09:18:17.142Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839", size = 192216, upload-time = "2024-08-17T09:18:18.779Z" }, + { url = "https://files.pythonhosted.org/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da", size = 30120, upload-time = "2024-08-17T09:18:20.009Z" }, + { url = "https://files.pythonhosted.org/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58", size = 30003, upload-time = "2024-08-17T09:18:21.052Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3", size = 26777, upload-time = "2024-08-17T09:18:22.809Z" }, + { url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969, upload-time = "2024-08-17T09:18:24.025Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787, upload-time = "2024-08-17T09:18:25.318Z" }, + { url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959, upload-time = "2024-08-17T09:18:26.518Z" }, + { url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006, upload-time = "2024-08-17T09:18:27.905Z" }, + { url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326, upload-time = "2024-08-17T09:18:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380, upload-time = "2024-08-17T09:18:30.706Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934, upload-time = "2024-08-17T09:18:32.133Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301, upload-time = "2024-08-17T09:18:33.474Z" }, + { url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351, upload-time = "2024-08-17T09:18:34.889Z" }, + { url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294, upload-time = "2024-08-17T09:18:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674, upload-time = "2024-08-17T09:18:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022, upload-time = "2024-08-17T09:18:40.138Z" }, + { url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170, upload-time = "2024-08-17T09:18:42.163Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040, upload-time = "2024-08-17T09:18:43.699Z" }, + { url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796, upload-time = "2024-08-17T09:18:45.29Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b8/e4b3ad92d249be5c83fa72916c9091b0965cb0faeff05d9a0a3870ae6bff/xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6", size = 31795, upload-time = "2024-08-17T09:18:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d8/b3627a0aebfbfa4c12a41e22af3742cf08c8ea84f5cc3367b5de2d039cce/xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5", size = 30792, upload-time = "2024-08-17T09:18:47.862Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cc/762312960691da989c7cd0545cb120ba2a4148741c6ba458aa723c00a3f8/xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc", size = 220950, upload-time = "2024-08-17T09:18:49.06Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e9/cc266f1042c3c13750e86a535496b58beb12bf8c50a915c336136f6168dc/xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3", size = 199980, upload-time = "2024-08-17T09:18:50.445Z" }, + { url = "https://files.pythonhosted.org/packages/bf/85/a836cd0dc5cc20376de26b346858d0ac9656f8f730998ca4324921a010b9/xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c", size = 428324, upload-time = "2024-08-17T09:18:51.988Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0e/15c243775342ce840b9ba34aceace06a1148fa1630cd8ca269e3223987f5/xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb", size = 194370, upload-time = "2024-08-17T09:18:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/87/a1/b028bb02636dfdc190da01951d0703b3d904301ed0ef6094d948983bef0e/xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f", size = 207911, upload-time = "2024-08-17T09:18:55.509Z" }, + { url = "https://files.pythonhosted.org/packages/80/d5/73c73b03fc0ac73dacf069fdf6036c9abad82de0a47549e9912c955ab449/xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7", size = 216352, upload-time = "2024-08-17T09:18:57.073Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2a/5043dba5ddbe35b4fe6ea0a111280ad9c3d4ba477dd0f2d1fe1129bda9d0/xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326", size = 203410, upload-time = "2024-08-17T09:18:58.54Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b2/9a8ded888b7b190aed75b484eb5c853ddd48aa2896e7b59bbfbce442f0a1/xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf", size = 210322, upload-time = "2024-08-17T09:18:59.943Z" }, + { url = "https://files.pythonhosted.org/packages/98/62/440083fafbc917bf3e4b67c2ade621920dd905517e85631c10aac955c1d2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7", size = 414725, upload-time = "2024-08-17T09:19:01.332Z" }, + { url = "https://files.pythonhosted.org/packages/75/db/009206f7076ad60a517e016bb0058381d96a007ce3f79fa91d3010f49cc2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c", size = 192070, upload-time = "2024-08-17T09:19:03.007Z" }, + { url = "https://files.pythonhosted.org/packages/1f/6d/c61e0668943a034abc3a569cdc5aeae37d686d9da7e39cf2ed621d533e36/xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637", size = 30172, upload-time = "2024-08-17T09:19:04.355Z" }, + { url = "https://files.pythonhosted.org/packages/96/14/8416dce965f35e3d24722cdf79361ae154fa23e2ab730e5323aa98d7919e/xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43", size = 30041, upload-time = "2024-08-17T09:19:05.435Z" }, + { url = "https://files.pythonhosted.org/packages/27/ee/518b72faa2073f5aa8e3262408d284892cb79cf2754ba0c3a5870645ef73/xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b", size = 26801, upload-time = "2024-08-17T09:19:06.547Z" }, +] + +[[package]] +name = "yarl" +version = "1.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" }, + { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" }, + { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" }, + { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" }, + { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" }, + { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" }, + { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" }, + { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" }, + { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" }, + { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" }, + { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" }, + { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" }, + { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" }, + { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" }, + { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" }, + { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" }, + { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" }, + { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" }, + { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" }, + { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" }, + { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" }, + { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" }, + { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" }, + { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" }, + { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" }, + { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" }, + { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" }, + { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" }, + { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" }, + { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" }, + { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" }, + { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" }, + { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" }, + { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" }, + { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" }, + { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" }, + { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" }, + { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" }, + { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" }, + { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" }, + { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" }, + { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" }, + { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" }, + { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" }, + { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, +] diff --git a/examples/volume_manager/volume_manager/__init__.py b/examples/volume_manager/volume_manager/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/volume_manager/volume_manager/__main__.py b/examples/volume_manager/volume_manager/__main__.py new file mode 100644 index 000000000..edb9908ef --- /dev/null +++ b/examples/volume_manager/volume_manager/__main__.py @@ -0,0 +1,37 @@ +import logging +import os +import sys + +import uvicorn + + +def main(): + """Main entry point for the Volume Manager Example.""" + # Configure basic logging + log_level = os.environ.get("VOLUME_MANAGER_LOG_LEVEL", "INFO").upper() + logging.basicConfig( + level=getattr(logging, log_level), + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + logger = logging.getLogger(__name__) + + # Get configuration from environment + port = int(os.environ.get("VOLUME_MANAGER_PORT", "8001")) + + try: + uvicorn.run( + "volume_manager.api:app", + host="0.0.0.0", + port=port, + reload=False, + log_level="info", + ) + except KeyboardInterrupt: + logger.info("Shutting down Volume Manager Example") + except Exception as e: + logger.error(f"Failed to start Volume Manager Example: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/volume_manager/volume_manager/api.py b/examples/volume_manager/volume_manager/api.py new file mode 100644 index 000000000..347be4c02 --- /dev/null +++ b/examples/volume_manager/volume_manager/api.py @@ -0,0 +1,153 @@ +import logging +from typing import Any, Dict, List +from fastapi import FastAPI, HTTPException + +from .schemas import ( + JobFinishedRequest, + JobFinishedResponse, + PrepareVolumeRequest, + PrepareVolumeResponse, +) +from .utils import create_volume_from_spec +from .storage import VolumeStorage + +logger = logging.getLogger(__name__) + +# Global storage instance +storage = VolumeStorage() + +app = FastAPI( + title="Volume Manager Example", + description="Example implementation of a volume manager for Compute Horde", + version="0.1.0", +) + + +@app.get("/") +async def root() -> Dict[str, List[str]]: + """List available endpoints.""" + return { + "endpoints": [ + "GET / - This directory", + "GET /health - Health check", + "GET /cache/status - Cache information", + "DELETE /cache/clear - Clear cache", + "POST /prepare_volume - Prepare volume for job", + "POST /job_finished - Job completion notification" + ] + } + + +@app.post("/prepare_volume", response_model=PrepareVolumeResponse) +async def prepare_volume(request: PrepareVolumeRequest) -> PrepareVolumeResponse: + """ + Prepare a volume for a job. + + This endpoint receives a volume specification and job metadata, + downloads the volume if not already cached, and returns Docker + mount options for the volume. + """ + + logger.info(f"Prepare volume request received for job {request.job_uuid}") + logger.debug(f"Volume type: {request.volume.volume_type}") + + try: + mount_options = [] + + if request.volume.volume_type == "multi_volume": + # Handle multi-volume by preparing each sub-volume + logger.info(f"Processing multi-volume with {len(request.volume.volumes)} sub-volumes") + for i, sub_volume in enumerate(request.volume.volumes): + logger.info(f"Processing sub-volume {i}: {sub_volume.model_dump()}") + volume = create_volume_from_spec(sub_volume) + volume_path = await storage.prepare_volume(volume) + + # Mount each sub-volume to a different path + mount_path = ("/volume/" + sub_volume.relative_path) if sub_volume.relative_path else "/volume" + mount_options.append(["-v", f"{volume_path}:{mount_path}"]) + + else: + # Handle individual volume + logger.info(f"Processing individual volume") + volume = create_volume_from_spec(request.volume) + volume_path = await storage.prepare_volume(volume) + + # Mount to the default volume path + mount_path = ("/volume/" + request.volume.relative_path) if request.volume.relative_path else "/volume" + mount_options = [["-v", f"{volume_path}:{mount_path}"]] + + logger.info(f"Volume prepared for job {request.job_uuid}") + logger.debug(f"Returning mount options: {mount_options}") + + return PrepareVolumeResponse(mounts=mount_options) + + except ValueError as e: + logger.error(f"Invalid volume specification: {e}") + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.exception(f"Failed to prepare volume for job {request.job_uuid}") + raise HTTPException(status_code=500, detail=str(e)) + + +@app.post("/job_finished", response_model=JobFinishedResponse) +async def job_finished(request: JobFinishedRequest) -> JobFinishedResponse: + """ + Notify that a job has finished. + + This endpoint is called when a job completes. The volume manager + can use this to clean up resources or update usage statistics. + """ + try: + logger.info(f"Job {request.job_uuid} finished") + + # For now, we don't clean up volumes immediately + # In a real implementation, you might want to: + # - Track volume usage + # - Implement LRU cache eviction + # - Clean up unused volumes + + return JobFinishedResponse(success=True) + + except Exception as e: + logger.exception(f"Failed to process job finished notification for {request.job_uuid}") + return JobFinishedResponse(success=False) + + +@app.get("/health") +async def health_check() -> Dict[str, str]: + """Health check endpoint.""" + return {"status": "healthy"} + + +@app.get("/cache/status") +async def cache_status() -> Dict[str, Any]: + """Get cache status information.""" + cached_volumes = storage.list_cached_volumes() + cache_size = storage.get_cache_size() + + return { + "cached_volumes_count": len(cached_volumes), + "cache_size": f"{cache_size // 1024} KB", + "cached_volumes": cached_volumes, + } + + +@app.delete("/cache/clear") +async def clear_cache() -> Dict[str, str]: + """Clear all cached volumes.""" + try: + cached_volumes = storage.list_cached_volumes() + if not cached_volumes: + return {"message": "No volumes in cache to clear"} + + cleared_count = 0 + for volume_hash in cached_volumes: + if storage.cleanup_volume(volume_hash): + cleared_count += 1 + + logger.info(f"Cleared {cleared_count} volumes from cache") + return {"message": f"Cleared {cleared_count} volumes from cache"} + + except Exception as e: + logger.exception("Failed to clear cache") + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/examples/volume_manager/volume_manager/schemas.py b/examples/volume_manager/volume_manager/schemas.py new file mode 100644 index 000000000..44a1b0989 --- /dev/null +++ b/examples/volume_manager/volume_manager/schemas.py @@ -0,0 +1,60 @@ +from typing import List, Optional, Any +from pydantic import BaseModel + + +class VolumeSpec(BaseModel): + """Volume specification from the API.""" + volume_type: str + repo_id: Optional[str] = None + repo_type: Optional[str] = None + revision: Optional[str] = None + relative_path: Optional[str] = None + allow_patterns: Optional[List[str]] = None + token: Optional[str] = None + usage_type: Optional[str] = None + volumes: Optional[List['VolumeSpec']] = None + contents: Optional[str] = None # For inline volumes + url: Optional[str] = None # For URL-based volumes + + +class OutputUpload(BaseModel): + """Output upload configuration.""" + output_upload_type: str + relative_path: str + url: str + + +class JobMetadata(BaseModel): + """Job metadata from the API.""" + message_type: Optional[str] = None + job_uuid: Optional[str] = None + executor_class: Optional[str] = None + docker_image: Optional[str] = None + raw_script: Optional[str] = None + docker_run_options_preset: Optional[str] = None + docker_run_cmd: Optional[List[str]] = None + volume: Optional[Any] = None + output_upload: Optional[OutputUpload] = None + artifacts_dir: Optional[str] = None + + +class PrepareVolumeRequest(BaseModel): + """Request to prepare a volume.""" + job_uuid: str + volume: VolumeSpec + job_metadata: JobMetadata + + +class PrepareVolumeResponse(BaseModel): + """Response from prepare volume endpoint.""" + mounts: List[List[str]] # List of Docker mount options + + +class JobFinishedRequest(BaseModel): + """Request to notify job finished.""" + job_uuid: str + + +class JobFinishedResponse(BaseModel): + """Response from job finished endpoint.""" + success: bool \ No newline at end of file diff --git a/examples/volume_manager/volume_manager/storage.py b/examples/volume_manager/volume_manager/storage.py new file mode 100644 index 000000000..f9d59d125 --- /dev/null +++ b/examples/volume_manager/volume_manager/storage.py @@ -0,0 +1,149 @@ +import logging +import os +import pathlib +import shutil + +from compute_horde_core.volume import ( + Volume, + VolumeDownloader, + VolumeDownloadFailed, + VolumeType, +) + +logger = logging.getLogger(__name__) + + +class VolumeStorage: + """Manages volume storage and caching.""" + + def __init__(self, cache_dir: str = "/tmp/volume_manager/cache"): + self.cache_dir = pathlib.Path(cache_dir) + # Create the cache directory and all parent directories + try: + os.makedirs(self.cache_dir, exist_ok=True) + except Exception as e: + logger.error(f"Failed to create cache directory {self.cache_dir}: {e}") + raise + logger.info(f"Volume storage initialized at {self.cache_dir}") + + def _get_volume_path(self, volume_url: str) -> pathlib.Path: + """Get the path where a volume should be stored.""" + return pathlib.Path(self.cache_dir / volume_url) + + def _is_volume_cached(self, volume_url: str) -> tuple[bool, pathlib.Path]: + """ + Check if a volume is cached and return its path. + + Args: + volume_hash: Hash of the volume to check + + Returns: + Tuple of (is_cached, volume_path) + """ + volume_path = self._get_volume_path(volume_url) + is_cached = volume_path.exists() and volume_path.is_dir() + return is_cached, volume_path + + async def prepare_volume(self, volume: Volume) -> pathlib.Path: + """ + Prepare a volume by downloading it if not already cached. + + Args: + volume: The volume to prepare + + Returns: + Path to the prepared volume + + Raises: + VolumeDownloadFailed: If download fails + """ + # Generate a unique identifier for caching based on volume type + if volume.volume_type == VolumeType.single_file: + volume_url = volume.url.replace('/', '_').replace(':', '_') + elif volume.volume_type == VolumeType.huggingface_volume: + volume_url = f"hf-{volume.repo_id.replace('/', '_')}" + elif volume.volume_type == VolumeType.zip_url: + volume_url = volume.contents.replace('/', '_').replace(':', '_') + elif volume.volume_type == VolumeType.inline: + if volume.relative_path: + volume_url = f"inline-{volume.relative_path}" + else: + volume_url = f"inline-{len(volume.contents)}" + else: + volume_url = f"unknown-{volume.volume_type}" + + is_cached, volume_path = self._is_volume_cached(volume_url) + + # Check if already cached + if is_cached: + logger.info(f"Volume {volume_url} found in cache - skipping download") + return volume_path + + # Download the volume + logger.info(f"Volume {volume_url} not found in cache - downloading") + + try: + downloader = VolumeDownloader.for_volume(volume) + await downloader.download(volume_path) + + logger.info(f"Successfully downloaded volume {volume_url}") + return volume_path + + except VolumeDownloadFailed as e: + logger.error(f"Failed to download volume {volume_url}: {e}") + shutil.rmtree(volume_path, ignore_errors=True) + raise + except Exception as e: + logger.exception(f"Unexpected error during download of {volume_url}") + if volume_path.exists(): + shutil.rmtree(volume_path, ignore_errors=True) + raise + + def cleanup_volume(self, volume_url: str) -> bool: + """ + Clean up a volume from cache. + + Args: + volume_url: URL of the volume to clean up + + Returns: + True if cleanup was successful, False if volume didn't exist + """ + volume_path = self._get_volume_path(volume_url) + + if volume_path.exists(): + try: + shutil.rmtree(volume_path, ignore_errors=True) + logger.info(f"Cleaned up volume {volume_url}") + return True + except Exception as e: + logger.exception(f"Failed to clean up volume {volume_url}") + return False + else: + return False + + def list_cached_volumes(self) -> list[str]: + """List all cached volume URLs.""" + if not self.cache_dir.exists(): + return [] + + volumes = [] + for item in self.cache_dir.iterdir(): + if item.is_dir(): + volumes.append(item.name) + + return volumes + + def get_cache_size(self) -> int: + """Get the total size of the cache in bytes.""" + if not self.cache_dir.exists(): + return 0 + + total_size = 0 + for root, _, files in os.walk(self.cache_dir): + for file in files: + file_path = pathlib.Path(root) / file + if file_path.exists(): + total_size += file_path.stat().st_size + + return total_size \ No newline at end of file diff --git a/examples/volume_manager/volume_manager/utils.py b/examples/volume_manager/volume_manager/utils.py new file mode 100644 index 000000000..7af9afef7 --- /dev/null +++ b/examples/volume_manager/volume_manager/utils.py @@ -0,0 +1,23 @@ +import logging + +from pydantic import TypeAdapter + +from .schemas import VolumeSpec + +from compute_horde_core.volume import Volume + +logger = logging.getLogger(__name__) + + +def create_volume_from_spec(volume_spec: VolumeSpec) -> "Volume": + """Create a Volume object from VolumeSpec.""" + try: + volume_data = volume_spec.model_dump() + logger.info(f"Creating {volume_spec.volume_type} volume") + + volume_adapter = TypeAdapter(Volume) + return volume_adapter.validate_python(volume_data) + + except Exception as e: + logger.exception(f"Failed to create {volume_spec.volume_type} volume") + raise ValueError(f"Failed to create {volume_spec.volume_type} volume: {e}") \ No newline at end of file diff --git a/examples/volume_manager/wallets/coldkey b/examples/volume_manager/wallets/coldkey new file mode 100644 index 000000000..0393c4826 --- /dev/null +++ b/examples/volume_manager/wallets/coldkey @@ -0,0 +1 @@ +{"secretSeed":"0x5a71bd9b9ecd1453847b621e195d9cda469e233cad16f894010c8eac451ec8c4","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS","accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","secretPhrase":"unhappy occur suffer sense replace exist gorilla old warfare another gold act warfare banner question fresh average sorry match topple apart morning mechanic remain","privateKey":"0x55aacc8a8bca925f7f1f3bab48301adce2e6a2984dfad28877ddafd4b92c9d0b85025c223c4d0aca4e9e278a4818a10565f82d32698df9c3e4852ccaff9ed7e2"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/coldkeypub.txt b/examples/volume_manager/wallets/coldkeypub.txt new file mode 100644 index 000000000..e708f3066 --- /dev/null +++ b/examples/volume_manager/wallets/coldkeypub.txt @@ -0,0 +1 @@ +{"accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/hotkeys/default b/examples/volume_manager/wallets/hotkeys/default new file mode 100644 index 000000000..f5f8f2b5e --- /dev/null +++ b/examples/volume_manager/wallets/hotkeys/default @@ -0,0 +1 @@ +{"ss58Address":"5CDYBwxDZwXnYSp7E39Vy92MVbCMtcK2i953oRxDm9Veko7M","secretPhrase":"wool remember perfect current orbit tattoo net near donor club various company orphan tourist october material crane farm race clinic mouse cattle opinion tilt","secretSeed":"0x3434dc3b4ea59fcb0fbda2a6291deea7cb5d2739508a02991e806b0f0e597129","privateKey":"0xd29be7ec1105c7c101d26e5f7c4af14bb19e5c2a36abc17462666f706edaf70ec057f259a54c143beb14bf1e799399b7feccbed1421c0aa97648a75cc01414bd","publicKey":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860","accountId":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/coldkey b/examples/volume_manager/wallets/validator/coldkey new file mode 100644 index 000000000..0393c4826 --- /dev/null +++ b/examples/volume_manager/wallets/validator/coldkey @@ -0,0 +1 @@ +{"secretSeed":"0x5a71bd9b9ecd1453847b621e195d9cda469e233cad16f894010c8eac451ec8c4","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS","accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","secretPhrase":"unhappy occur suffer sense replace exist gorilla old warfare another gold act warfare banner question fresh average sorry match topple apart morning mechanic remain","privateKey":"0x55aacc8a8bca925f7f1f3bab48301adce2e6a2984dfad28877ddafd4b92c9d0b85025c223c4d0aca4e9e278a4818a10565f82d32698df9c3e4852ccaff9ed7e2"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/coldkeypub.txt b/examples/volume_manager/wallets/validator/coldkeypub.txt new file mode 100644 index 000000000..e708f3066 --- /dev/null +++ b/examples/volume_manager/wallets/validator/coldkeypub.txt @@ -0,0 +1 @@ +{"accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/hotkeys/default b/examples/volume_manager/wallets/validator/hotkeys/default new file mode 100644 index 000000000..f5f8f2b5e --- /dev/null +++ b/examples/volume_manager/wallets/validator/hotkeys/default @@ -0,0 +1 @@ +{"ss58Address":"5CDYBwxDZwXnYSp7E39Vy92MVbCMtcK2i953oRxDm9Veko7M","secretPhrase":"wool remember perfect current orbit tattoo net near donor club various company orphan tourist october material crane farm race clinic mouse cattle opinion tilt","secretSeed":"0x3434dc3b4ea59fcb0fbda2a6291deea7cb5d2739508a02991e806b0f0e597129","privateKey":"0xd29be7ec1105c7c101d26e5f7c4af14bb19e5c2a36abc17462666f706edaf70ec057f259a54c143beb14bf1e799399b7feccbed1421c0aa97648a75cc01414bd","publicKey":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860","accountId":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860"} \ No newline at end of file diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index 52fbad5a6..35638d324 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -63,7 +63,7 @@ cd $PROJECT_ROOT/executor update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" # Uncomment the line below only if you have a volume manager service running -# update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" +update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner diff --git a/local_stack/send_hello_world_job.py b/local_stack/send_hello_world_job.py index ee9131f5a..139318437 100644 --- a/local_stack/send_hello_world_job.py +++ b/local_stack/send_hello_world_job.py @@ -7,7 +7,6 @@ import tempfile import time import ssl -import argparse import boto3 import bittensor @@ -17,7 +16,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec -from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HTTPOutputVolume, HuggingfaceInputVolume +from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HTTPOutputVolume # Configure logging logging.basicConfig(level=logging.INFO) @@ -91,51 +90,6 @@ def create_mt_client_with_tempfiles( client = httpx.Client(cert=(cert_file.name, key_file.name), verify=ctx) return client - -async def run_volume_test(): - """Run a test job that downloads a HuggingFace model.""" - logger.info("Running volume download test...") - - # Pull required Docker image - os.system("docker pull python:3.11-slim") - - # Create a job spec that downloads a small HuggingFace model - compute_horde_job_spec = ComputeHordeJobSpec( - executor_class=ExecutorClass.always_on__llm__a6000, - job_namespace="SN123.0", - docker_image="python:3.11-slim", - args=[ - "bash", - "-c", - "echo 'Testing volume download...' && " - "ls -la /volume && " - "echo 'Job completed successfully' > /artifacts/stuff", - ], - artifacts_dir="/artifacts", - input_volumes={ - "/volume/model": HuggingfaceInputVolume( - repo_id="prajjwal1/bert-tiny", - repo_type="model", - ), - }, - download_time_limit_sec=60, - execution_time_limit_sec=30, - upload_time_limit_sec=10, - ) - - # Create and submit the job. - job = await compute_horde_client.create_job(compute_horde_job_spec) - await job.wait(timeout=10 * 60) - - # Validate job completion and output. - expected_artifacts = {"/artifacts/stuff": b"Job completed successfully\n"} - if job.status != "Completed" or job.result.artifacts != expected_artifacts: - raise RuntimeError( - f"Job failed: status={job.status}, artifacts={job.result.artifacts}" - ) - - logger.info("Volume test success!") - async def run_hello_world_job(): compute_horde_streaming_job_spec = ComputeHordeJobSpec( executor_class=ExecutorClass.always_on__llm__a6000, @@ -272,24 +226,12 @@ async def run_hello_world_job(): logger.info("Success!") async def main() -> None: - parser = argparse.ArgumentParser(description="Run ComputeHorde integration tests") - parser.add_argument( - "--test-volume", - action="store_true", - help="Include volume download test" - ) - args = parser.parse_args() - # Pull required Docker images os.system("docker pull alpine") os.system("docker pull backenddevelopersltd/compute-horde-executor:v1-latest") os.system("docker pull backenddevelopersltd/compute-horde-streaming-job-test:v0-latest") - # Run volume test if requested - if args.test_volume: - await run_volume_test() - else: - await run_hello_world_job() + await run_hello_world_job() if __name__ == "__main__": From 0fa92c1ba05a5d403a89b0e674c830d2060a7ec6 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 05:18:11 -0300 Subject: [PATCH 13/25] ci test --- .github/workflows/integration_ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index 402878c3c..c1b6877b5 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -3,9 +3,8 @@ name: Run integration tests # temporary disable integration test on: push: - branches: [master, main] - pull_request: branches: [master, main, ci_test] + pull_request: env: PYTHON_DEFAULT_VERSION: "3.11" From d58961901fbfcb06e5a23964948031b71a2f9203 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 05:21:57 -0300 Subject: [PATCH 14/25] quick test --- .github/workflows/integration_ci.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index c1b6877b5..3276e45fc 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -32,23 +32,23 @@ jobs: - name: Start all services run: local_stack/run_and_await_readiness.sh /tmp/integration_test_logs/ - - name: test - working-directory: ./local_stack - run: uv run ./send_hello_world_job.py - env: - AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} - S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} + # - name: test + # working-directory: ./local_stack + # run: uv run ./send_hello_world_job.py + # env: + # AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} + # AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} + # AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} + # S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} - name: test volume manager integration working-directory: ./examples/volume_manager run: ./run_tests.sh -i -d - - name: Upload integration test logs - uses: actions/upload-artifact@v4 - if: always() - with: - name: integration-test-logs - path: /tmp/integration_test_logs/*.log + # - name: Upload integration test logs + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: integration-test-logs + # path: /tmp/integration_test_logs/*.log From c132b09d25c3ea7d2917de3f1ea611b05e500c15 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 05:34:59 -0300 Subject: [PATCH 15/25] uncomment changes --- .github/workflows/integration_ci.yml | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index 3276e45fc..ecbab7b42 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -3,7 +3,7 @@ name: Run integration tests # temporary disable integration test on: push: - branches: [master, main, ci_test] + branches: [master, main] pull_request: env: @@ -32,23 +32,23 @@ jobs: - name: Start all services run: local_stack/run_and_await_readiness.sh /tmp/integration_test_logs/ - # - name: test - # working-directory: ./local_stack - # run: uv run ./send_hello_world_job.py - # env: - # AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} - # AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} - # AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} - # S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} + - name: test + working-directory: ./local_stack + run: uv run ./send_hello_world_job.py + env: + AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} + S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} - name: test volume manager integration working-directory: ./examples/volume_manager run: ./run_tests.sh -i -d - # - name: Upload integration test logs - # uses: actions/upload-artifact@v4 - # if: always() - # with: - # name: integration-test-logs - # path: /tmp/integration_test_logs/*.log + - name: Upload integration test logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: integration-test-logs + path: /tmp/integration_test_logs/*.log From fcb27e7b13427fcc443944ccedf2e5dd1a47b03d Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 07:37:08 -0300 Subject: [PATCH 16/25] Lint and docs --- compute_horde_sdk/README.md | 4 +- examples/volume_manager/README.md | 17 ++--- examples/volume_manager/test_endpoints.py | 44 ++++++------- examples/volume_manager/test_integration.py | 43 +++++++------ .../volume_manager/volume_manager/__main__.py | 13 ++-- examples/volume_manager/volume_manager/api.py | 63 +++++++++++-------- .../volume_manager/volume_manager/schemas.py | 15 +++-- .../volume_manager/volume_manager/storage.py | 52 +++++++-------- .../volume_manager/volume_manager/utils.py | 4 +- 9 files changed, 134 insertions(+), 121 deletions(-) diff --git a/compute_horde_sdk/README.md b/compute_horde_sdk/README.md index 237d492b9..84689b328 100644 --- a/compute_horde_sdk/README.md +++ b/compute_horde_sdk/README.md @@ -274,7 +274,7 @@ You can also add custom headers for authentication or any other purpose. Any env ```bash # Authentication header -export VOLUME_MANAGER_HEADER_AUTHORIZATION='Bearer dupadupakupa' +export VOLUME_MANAGER_HEADER_AUTHORIZATION='Bearer tokentokentoken' # Custom API key export VOLUME_MANAGER_HEADER_X_API_KEY='your-api-key-here' @@ -320,6 +320,8 @@ Your volume manager must implement these endpoints: } ``` +**Note:** Large volume downloads may take considerable time, so volume managers should not implement self-timeout mechanisms during volume preparation as the system already handles timeouts. For the same reason, the manager service should handle executor disconnections gracefully. + **Response:** ```json { diff --git a/examples/volume_manager/README.md b/examples/volume_manager/README.md index 8df4382b1..2676b4864 100644 --- a/examples/volume_manager/README.md +++ b/examples/volume_manager/README.md @@ -1,6 +1,6 @@ # Volume Manager Example -An example implementation of a volume manager for Compute Horde. This demonstrates how to build an external volume management service that can download, cache, and prepare volumes for compute jobs. +An example implementation of a volume manager for Compute Horde. This demonstrates how to build an external volume management service that can download, cache, and return mount options for volumes used in compute jobs. ## Quick Start @@ -24,16 +24,16 @@ docker-compose down ```bash # Set env variable directly -export VOLUME_MANAGER_PORT=9000 +export VOLUME_MANAGER_PORT=8123 # Or use it with docker-compose -VOLUME_MANAGER_PORT=9000 docker-compose up -d +VOLUME_MANAGER_PORT=8123 docker-compose up -d ``` ## Monitoring ```bash -# Check health (uses VOLUME_MANAGER_PORT or defaults to 8001) +# Check health curl http://localhost:${VOLUME_MANAGER_PORT:-8001}/health # Check cache status @@ -42,7 +42,7 @@ curl http://localhost:${VOLUME_MANAGER_PORT:-8001}/cache/status ## Local Testing -After setting everything up with `local_stack/run_in_screen.sh`, test the +After setting everything up with [`local_stack/run_in_screen.sh`](https://github.com/backend-developers-ltd/ComputeHorde/blob/master/local_stack/run_in_screen.sh), test the volume manager with: ```bash ./run_tests.sh @@ -55,9 +55,4 @@ VOLUME_MANAGER_PORT=9000 ./run_tests.sh # Test the whole ComputeHorde integration instead of endpoints only ./run_tests.sh -i -``` - -**Available flags:** -- `-d, --deploy`: Deploy volume manager example -- `-i, --integration`: Test complete job integration -- `-h, --help`: Show help message \ No newline at end of file +``` \ No newline at end of file diff --git a/examples/volume_manager/test_endpoints.py b/examples/volume_manager/test_endpoints.py index 1f55ce455..a6dd7949e 100644 --- a/examples/volume_manager/test_endpoints.py +++ b/examples/volume_manager/test_endpoints.py @@ -12,13 +12,11 @@ TEST_MODEL = "prajjwal1/bert-tiny" JOB_UUID = str(uuid.uuid4()) + def test_prepare_volume_endpoint(): - """Test the prepare_volume API contract with valid request.""" + """Test the prepare_volume API endpoint.""" logger.info("Testing prepare_volume endpoint...") - - - # Generate a unique job UUID for each test run - + request_data = { "job_uuid": JOB_UUID, "volume": { @@ -26,7 +24,7 @@ def test_prepare_volume_endpoint(): "repo_id": TEST_MODEL, "revision": "main", "relative_path": "models", - "usage_type": "reusable" + "usage_type": "reusable", }, "job_metadata": { "message_type": "V0JobRequest", @@ -40,17 +38,21 @@ def test_prepare_volume_endpoint(): "output_upload": { "output_upload_type": "single_file_put", "relative_path": "results.json", - "url": "https://s3.example.com/results.json" + "url": "https://s3.example.com/results.json", }, - "artifacts_dir": "/artifacts" - } + "artifacts_dir": "/artifacts", + }, } - + response = requests.post(f"{VOLUME_MANAGER_URL}/prepare_volume", json=request_data) response.raise_for_status() - + data = response.json() - assert data == {"mounts":[["-v","/tmp/volume_manager/cache/hf-prajjwal1_bert-tiny:/volume/models"]]}, data + assert data == { + "mounts": [ + ["-v", "/tmp/volume_manager/cache/hf-prajjwal1_bert-tiny:/volume/models"] + ] + }, data logger.info("✓ Prepare volume endpoint working") @@ -58,29 +60,27 @@ def test_prepare_volume_endpoint(): def test_job_finished_endpoint(): """Test the job_finished endpoint.""" logger.info("Testing job_finished endpoint...") - + # Generate a unique job UUID for each test run - - request_data = { - "job_uuid": JOB_UUID - } - + + request_data = {"job_uuid": JOB_UUID} + response = requests.post(f"{VOLUME_MANAGER_URL}/job_finished", json=request_data) response.raise_for_status() - + logger.info("✓ Job finished endpoint working") def main(): """Test the required volume manager endpoints.""" logger.info("Testing required volume manager endpoints...") - + # Test basic API endpoints test_prepare_volume_endpoint() test_job_finished_endpoint() - + logger.info("All required endpoints are working! ✓") if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/volume_manager/test_integration.py b/examples/volume_manager/test_integration.py index 50db4c1ee..c36a1e1e9 100644 --- a/examples/volume_manager/test_integration.py +++ b/examples/volume_manager/test_integration.py @@ -3,18 +3,14 @@ import os import logging import requests - import bittensor -import sys -import pathlib - -# Add the local compute_horde_sdk to the Python path -sdk_path = pathlib.Path(__file__).parent.parent.parent / "compute_horde_sdk" / "src" -sys.path.insert(0, str(sdk_path)) - from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec -from compute_horde_sdk.v1 import ComputeHordeClient, ExecutorClass, HuggingfaceInputVolume +from compute_horde_sdk.v1 import ( + ComputeHordeClient, + ExecutorClass, + HuggingfaceInputVolume, +) # Configure logging logging.basicConfig(level=logging.INFO) @@ -24,7 +20,9 @@ wallet = bittensor.wallet( name="validator", hotkey="default", - path=(pathlib.Path(__file__).parent.parent.parent / "local_stack" / "wallets").as_posix(), + path=( + pathlib.Path(__file__).parent.parent.parent / "local_stack" / "wallets" + ).as_posix(), ) compute_horde_client = ComputeHordeClient( hotkey=wallet.hotkey, @@ -36,11 +34,13 @@ async def run_volume_test(): """Run a test job that downloads a HuggingFace model.""" logger.info("Running volume download test...") - + # Pull required Docker images os.system("docker pull python:3.11-slim") - os.system("docker pull us-central1-docker.pkg.dev/twistlock-secresearch/public/can-ctr-escape-cve-2022-0492:latest") - + os.system( + "docker pull us-central1-docker.pkg.dev/twistlock-secresearch/public/can-ctr-escape-cve-2022-0492:latest" + ) + # Create a job spec that downloads a small HuggingFace model compute_horde_job_spec = ComputeHordeJobSpec( executor_class=ExecutorClass.always_on__llm__a6000, @@ -49,9 +49,7 @@ async def run_volume_test(): args=[ "bash", "-c", - "echo 'Testing volume download...' && " - "ls -la /volume && " - "echo 'Job completed successfully' > /artifacts/stuff", + "echo 'Running volume test...' > /artifacts/stuff", ], artifacts_dir="/artifacts", input_volumes={ @@ -70,7 +68,7 @@ async def run_volume_test(): await job.wait(timeout=60) # Validate job completion and output. - expected_artifacts = {"/artifacts/stuff": b"Job completed successfully\n"} + expected_artifacts = {"/artifacts/stuff": b"Running volume test...\n"} if job.status != "Completed" or job.result.artifacts != expected_artifacts: raise RuntimeError( f"Job failed: status={job.status}, artifacts={job.result.artifacts}" @@ -85,11 +83,12 @@ async def run_volume_test(): if "hf-prajjwal1_bert-tiny" in cache_data.get("cached_volumes", []): logger.info("✓ Volume manager successfully cached the model") else: - logger.error("Volume manager cache is empty") - except Exception as e: - logger.error(f"Could not check volume manager status: {e}") + logger.warning("Volume manager cache is empty") + except Exception: + logger.warning("Could not check volume manager status") + + logger.info("Volume test finished!") - logger.info("Volume test success!") if __name__ == "__main__": - asyncio.run(run_volume_test()) \ No newline at end of file + asyncio.run(run_volume_test()) diff --git a/examples/volume_manager/volume_manager/__main__.py b/examples/volume_manager/volume_manager/__main__.py index edb9908ef..0c503ddb2 100644 --- a/examples/volume_manager/volume_manager/__main__.py +++ b/examples/volume_manager/volume_manager/__main__.py @@ -1,23 +1,20 @@ import logging import os import sys - import uvicorn def main(): - """Main entry point for the Volume Manager Example.""" - # Configure basic logging + log_level = os.environ.get("VOLUME_MANAGER_LOG_LEVEL", "INFO").upper() logging.basicConfig( level=getattr(logging, log_level), - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) - - # Get configuration from environment + port = int(os.environ.get("VOLUME_MANAGER_PORT", "8001")) - + try: uvicorn.run( "volume_manager.api:app", @@ -34,4 +31,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/volume_manager/volume_manager/api.py b/examples/volume_manager/volume_manager/api.py index 347be4c02..80292a51c 100644 --- a/examples/volume_manager/volume_manager/api.py +++ b/examples/volume_manager/volume_manager/api.py @@ -1,4 +1,5 @@ import logging + from typing import Any, Dict, List from fastapi import FastAPI, HTTPException @@ -33,7 +34,7 @@ async def root() -> Dict[str, List[str]]: "GET /cache/status - Cache information", "DELETE /cache/clear - Clear cache", "POST /prepare_volume - Prepare volume for job", - "POST /job_finished - Job completion notification" + "POST /job_finished - Job completion notification", ] } @@ -42,7 +43,7 @@ async def root() -> Dict[str, List[str]]: async def prepare_volume(request: PrepareVolumeRequest) -> PrepareVolumeResponse: """ Prepare a volume for a job. - + This endpoint receives a volume specification and job metadata, downloads the volume if not already cached, and returns Docker mount options for the volume. @@ -50,37 +51,47 @@ async def prepare_volume(request: PrepareVolumeRequest) -> PrepareVolumeResponse logger.info(f"Prepare volume request received for job {request.job_uuid}") logger.debug(f"Volume type: {request.volume.volume_type}") - + try: mount_options = [] - + if request.volume.volume_type == "multi_volume": # Handle multi-volume by preparing each sub-volume - logger.info(f"Processing multi-volume with {len(request.volume.volumes)} sub-volumes") + logger.info( + f"Processing multi-volume with {len(request.volume.volumes)} sub-volumes" + ) for i, sub_volume in enumerate(request.volume.volumes): logger.info(f"Processing sub-volume {i}: {sub_volume.model_dump()}") volume = create_volume_from_spec(sub_volume) volume_path = await storage.prepare_volume(volume) - + # Mount each sub-volume to a different path - mount_path = ("/volume/" + sub_volume.relative_path) if sub_volume.relative_path else "/volume" + mount_path = ( + ("/volume/" + sub_volume.relative_path) + if sub_volume.relative_path + else "/volume" + ) mount_options.append(["-v", f"{volume_path}:{mount_path}"]) - + else: # Handle individual volume - logger.info(f"Processing individual volume") + logger.info("Processing individual volume") volume = create_volume_from_spec(request.volume) volume_path = await storage.prepare_volume(volume) - + # Mount to the default volume path - mount_path = ("/volume/" + request.volume.relative_path) if request.volume.relative_path else "/volume" + mount_path = ( + ("/volume/" + request.volume.relative_path) + if request.volume.relative_path + else "/volume" + ) mount_options = [["-v", f"{volume_path}:{mount_path}"]] - + logger.info(f"Volume prepared for job {request.job_uuid}") logger.debug(f"Returning mount options: {mount_options}") - + return PrepareVolumeResponse(mounts=mount_options) - + except ValueError as e: logger.error(f"Invalid volume specification: {e}") raise HTTPException(status_code=400, detail=str(e)) @@ -93,23 +104,25 @@ async def prepare_volume(request: PrepareVolumeRequest) -> PrepareVolumeResponse async def job_finished(request: JobFinishedRequest) -> JobFinishedResponse: """ Notify that a job has finished. - + This endpoint is called when a job completes. The volume manager can use this to clean up resources or update usage statistics. """ try: logger.info(f"Job {request.job_uuid} finished") - + # For now, we don't clean up volumes immediately # In a real implementation, you might want to: # - Track volume usage # - Implement LRU cache eviction # - Clean up unused volumes - + return JobFinishedResponse(success=True) - - except Exception as e: - logger.exception(f"Failed to process job finished notification for {request.job_uuid}") + + except Exception: + logger.exception( + f"Failed to process job finished notification for {request.job_uuid}" + ) return JobFinishedResponse(success=False) @@ -124,7 +137,7 @@ async def cache_status() -> Dict[str, Any]: """Get cache status information.""" cached_volumes = storage.list_cached_volumes() cache_size = storage.get_cache_size() - + return { "cached_volumes_count": len(cached_volumes), "cache_size": f"{cache_size // 1024} KB", @@ -139,15 +152,15 @@ async def clear_cache() -> Dict[str, str]: cached_volumes = storage.list_cached_volumes() if not cached_volumes: return {"message": "No volumes in cache to clear"} - + cleared_count = 0 for volume_hash in cached_volumes: if storage.cleanup_volume(volume_hash): cleared_count += 1 - + logger.info(f"Cleared {cleared_count} volumes from cache") return {"message": f"Cleared {cleared_count} volumes from cache"} - + except Exception as e: logger.exception("Failed to clear cache") - raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file + raise HTTPException(status_code=500, detail=str(e)) diff --git a/examples/volume_manager/volume_manager/schemas.py b/examples/volume_manager/volume_manager/schemas.py index 44a1b0989..43d7da386 100644 --- a/examples/volume_manager/volume_manager/schemas.py +++ b/examples/volume_manager/volume_manager/schemas.py @@ -4,6 +4,7 @@ class VolumeSpec(BaseModel): """Volume specification from the API.""" + volume_type: str repo_id: Optional[str] = None repo_type: Optional[str] = None @@ -12,13 +13,14 @@ class VolumeSpec(BaseModel): allow_patterns: Optional[List[str]] = None token: Optional[str] = None usage_type: Optional[str] = None - volumes: Optional[List['VolumeSpec']] = None - contents: Optional[str] = None # For inline volumes - url: Optional[str] = None # For URL-based volumes + volumes: Optional[List["VolumeSpec"]] = None + contents: Optional[str] = None # For inline volumes + url: Optional[str] = None # For URL-based volumes class OutputUpload(BaseModel): """Output upload configuration.""" + output_upload_type: str relative_path: str url: str @@ -26,6 +28,7 @@ class OutputUpload(BaseModel): class JobMetadata(BaseModel): """Job metadata from the API.""" + message_type: Optional[str] = None job_uuid: Optional[str] = None executor_class: Optional[str] = None @@ -40,6 +43,7 @@ class JobMetadata(BaseModel): class PrepareVolumeRequest(BaseModel): """Request to prepare a volume.""" + job_uuid: str volume: VolumeSpec job_metadata: JobMetadata @@ -47,14 +51,17 @@ class PrepareVolumeRequest(BaseModel): class PrepareVolumeResponse(BaseModel): """Response from prepare volume endpoint.""" + mounts: List[List[str]] # List of Docker mount options class JobFinishedRequest(BaseModel): """Request to notify job finished.""" + job_uuid: str class JobFinishedResponse(BaseModel): """Response from job finished endpoint.""" - success: bool \ No newline at end of file + + success: bool diff --git a/examples/volume_manager/volume_manager/storage.py b/examples/volume_manager/volume_manager/storage.py index f9d59d125..b5e5c83d8 100644 --- a/examples/volume_manager/volume_manager/storage.py +++ b/examples/volume_manager/volume_manager/storage.py @@ -15,7 +15,7 @@ class VolumeStorage: """Manages volume storage and caching.""" - + def __init__(self, cache_dir: str = "/tmp/volume_manager/cache"): self.cache_dir = pathlib.Path(cache_dir) # Create the cache directory and all parent directories @@ -25,45 +25,45 @@ def __init__(self, cache_dir: str = "/tmp/volume_manager/cache"): logger.error(f"Failed to create cache directory {self.cache_dir}: {e}") raise logger.info(f"Volume storage initialized at {self.cache_dir}") - + def _get_volume_path(self, volume_url: str) -> pathlib.Path: """Get the path where a volume should be stored.""" return pathlib.Path(self.cache_dir / volume_url) - + def _is_volume_cached(self, volume_url: str) -> tuple[bool, pathlib.Path]: """ Check if a volume is cached and return its path. - + Args: volume_hash: Hash of the volume to check - + Returns: Tuple of (is_cached, volume_path) """ volume_path = self._get_volume_path(volume_url) is_cached = volume_path.exists() and volume_path.is_dir() return is_cached, volume_path - + async def prepare_volume(self, volume: Volume) -> pathlib.Path: """ Prepare a volume by downloading it if not already cached. - + Args: volume: The volume to prepare - + Returns: Path to the prepared volume - + Raises: VolumeDownloadFailed: If download fails """ # Generate a unique identifier for caching based on volume type if volume.volume_type == VolumeType.single_file: - volume_url = volume.url.replace('/', '_').replace(':', '_') + volume_url = volume.url.replace("/", "_").replace(":", "_") elif volume.volume_type == VolumeType.huggingface_volume: volume_url = f"hf-{volume.repo_id.replace('/', '_')}" elif volume.volume_type == VolumeType.zip_url: - volume_url = volume.contents.replace('/', '_').replace(':', '_') + volume_url = volume.contents.replace("/", "_").replace(":", "_") elif volume.volume_type == VolumeType.inline: if volume.relative_path: volume_url = f"inline-{volume.relative_path}" @@ -71,7 +71,7 @@ async def prepare_volume(self, volume: Volume) -> pathlib.Path: volume_url = f"inline-{len(volume.contents)}" else: volume_url = f"unknown-{volume.volume_type}" - + is_cached, volume_path = self._is_volume_cached(volume_url) # Check if already cached @@ -81,7 +81,7 @@ async def prepare_volume(self, volume: Volume) -> pathlib.Path: # Download the volume logger.info(f"Volume {volume_url} not found in cache - downloading") - + try: downloader = VolumeDownloader.for_volume(volume) await downloader.download(volume_path) @@ -93,57 +93,57 @@ async def prepare_volume(self, volume: Volume) -> pathlib.Path: logger.error(f"Failed to download volume {volume_url}: {e}") shutil.rmtree(volume_path, ignore_errors=True) raise - except Exception as e: + except Exception: logger.exception(f"Unexpected error during download of {volume_url}") if volume_path.exists(): shutil.rmtree(volume_path, ignore_errors=True) raise - + def cleanup_volume(self, volume_url: str) -> bool: """ Clean up a volume from cache. - + Args: volume_url: URL of the volume to clean up - + Returns: True if cleanup was successful, False if volume didn't exist """ volume_path = self._get_volume_path(volume_url) - + if volume_path.exists(): try: shutil.rmtree(volume_path, ignore_errors=True) logger.info(f"Cleaned up volume {volume_url}") return True - except Exception as e: + except Exception: logger.exception(f"Failed to clean up volume {volume_url}") return False else: return False - + def list_cached_volumes(self) -> list[str]: """List all cached volume URLs.""" if not self.cache_dir.exists(): return [] - + volumes = [] for item in self.cache_dir.iterdir(): if item.is_dir(): volumes.append(item.name) - + return volumes - + def get_cache_size(self) -> int: """Get the total size of the cache in bytes.""" if not self.cache_dir.exists(): return 0 - + total_size = 0 for root, _, files in os.walk(self.cache_dir): for file in files: file_path = pathlib.Path(root) / file if file_path.exists(): total_size += file_path.stat().st_size - - return total_size \ No newline at end of file + + return total_size diff --git a/examples/volume_manager/volume_manager/utils.py b/examples/volume_manager/volume_manager/utils.py index 7af9afef7..13207e1f1 100644 --- a/examples/volume_manager/volume_manager/utils.py +++ b/examples/volume_manager/volume_manager/utils.py @@ -17,7 +17,7 @@ def create_volume_from_spec(volume_spec: VolumeSpec) -> "Volume": volume_adapter = TypeAdapter(Volume) return volume_adapter.validate_python(volume_data) - + except Exception as e: logger.exception(f"Failed to create {volume_spec.volume_type} volume") - raise ValueError(f"Failed to create {volume_spec.volume_type} volume: {e}") \ No newline at end of file + raise ValueError(f"Failed to create {volume_spec.volume_type} volume: {e}") From eb6e13bf2c39adc0cae8c836a8e5a7f0e6f31a19 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 07:42:30 -0300 Subject: [PATCH 17/25] comment volume manager var --- local_stack/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index 35638d324..52fbad5a6 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -63,7 +63,7 @@ cd $PROJECT_ROOT/executor update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" # Uncomment the line below only if you have a volume manager service running -update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" +# update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner From e2959960a5e6c3874bfd8cbf68b40c4fbf2befbb Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 08:16:08 -0300 Subject: [PATCH 18/25] last test --- .github/workflows/integration_ci.yml | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index ecbab7b42..3276e45fc 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -3,7 +3,7 @@ name: Run integration tests # temporary disable integration test on: push: - branches: [master, main] + branches: [master, main, ci_test] pull_request: env: @@ -32,23 +32,23 @@ jobs: - name: Start all services run: local_stack/run_and_await_readiness.sh /tmp/integration_test_logs/ - - name: test - working-directory: ./local_stack - run: uv run ./send_hello_world_job.py - env: - AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} - AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} - S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} + # - name: test + # working-directory: ./local_stack + # run: uv run ./send_hello_world_job.py + # env: + # AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} + # AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} + # AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} + # S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} - name: test volume manager integration working-directory: ./examples/volume_manager run: ./run_tests.sh -i -d - - name: Upload integration test logs - uses: actions/upload-artifact@v4 - if: always() - with: - name: integration-test-logs - path: /tmp/integration_test_logs/*.log + # - name: Upload integration test logs + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: integration-test-logs + # path: /tmp/integration_test_logs/*.log From cf50948ec896d4ca3d30c5acfe1a37dde8f92961 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 08:41:24 -0300 Subject: [PATCH 19/25] quick CI test --- .github/workflows/integration_ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index 3276e45fc..deaafb1e3 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -44,6 +44,9 @@ jobs: - name: test volume manager integration working-directory: ./examples/volume_manager run: ./run_tests.sh -i -d + env: + VOLUME_MANAGER_ADDRESS: ${{ 0.0.0.0:8001 }} + DEBUG_TURN_AUTHENTICATION_OFF: true # - name: Upload integration test logs # uses: actions/upload-artifact@v4 From bf9d7b9103a704a9acb28af98087ba6fc1a87664 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:27:25 -0300 Subject: [PATCH 20/25] Final Ci test for Volume Manager Integration --- .github/workflows/integration_ci.yml | 5 +++-- examples/volume_manager/test_integration.py | 5 +++++ examples/volume_manager/wallets/coldkey | 1 - examples/volume_manager/wallets/coldkeypub.txt | 1 - examples/volume_manager/wallets/hotkeys/default | 1 - examples/volume_manager/wallets/validator/coldkey | 1 - examples/volume_manager/wallets/validator/coldkeypub.txt | 1 - examples/volume_manager/wallets/validator/hotkeys/default | 1 - local_stack/prepare.sh | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 examples/volume_manager/wallets/coldkey delete mode 100644 examples/volume_manager/wallets/coldkeypub.txt delete mode 100644 examples/volume_manager/wallets/hotkeys/default delete mode 100644 examples/volume_manager/wallets/validator/coldkey delete mode 100644 examples/volume_manager/wallets/validator/coldkeypub.txt delete mode 100644 examples/volume_manager/wallets/validator/hotkeys/default diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index deaafb1e3..596673055 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -31,6 +31,8 @@ jobs: enable-cache: true - name: Start all services run: local_stack/run_and_await_readiness.sh /tmp/integration_test_logs/ + env: + VOLUME_MANAGER_ADDRESS: "http://localhost:8001" # - name: test # working-directory: ./local_stack @@ -45,8 +47,7 @@ jobs: working-directory: ./examples/volume_manager run: ./run_tests.sh -i -d env: - VOLUME_MANAGER_ADDRESS: ${{ 0.0.0.0:8001 }} - DEBUG_TURN_AUTHENTICATION_OFF: true + VOLUME_MANAGER_ADDRESS: "http://localhost:8001" # - name: Upload integration test logs # uses: actions/upload-artifact@v4 diff --git a/examples/volume_manager/test_integration.py b/examples/volume_manager/test_integration.py index c36a1e1e9..65470c1a7 100644 --- a/examples/volume_manager/test_integration.py +++ b/examples/volume_manager/test_integration.py @@ -1,10 +1,15 @@ import asyncio import pathlib import os +import sys import logging import requests import bittensor +# Add the local compute_horde_sdk to the Python path +sdk_path = pathlib.Path(__file__).parent.parent.parent / "compute_horde_sdk" / "src" +sys.path.insert(0, str(sdk_path)) + from compute_horde_sdk._internal.sdk import ComputeHordeJobSpec from compute_horde_sdk.v1 import ( ComputeHordeClient, diff --git a/examples/volume_manager/wallets/coldkey b/examples/volume_manager/wallets/coldkey deleted file mode 100644 index 0393c4826..000000000 --- a/examples/volume_manager/wallets/coldkey +++ /dev/null @@ -1 +0,0 @@ -{"secretSeed":"0x5a71bd9b9ecd1453847b621e195d9cda469e233cad16f894010c8eac451ec8c4","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS","accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","secretPhrase":"unhappy occur suffer sense replace exist gorilla old warfare another gold act warfare banner question fresh average sorry match topple apart morning mechanic remain","privateKey":"0x55aacc8a8bca925f7f1f3bab48301adce2e6a2984dfad28877ddafd4b92c9d0b85025c223c4d0aca4e9e278a4818a10565f82d32698df9c3e4852ccaff9ed7e2"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/coldkeypub.txt b/examples/volume_manager/wallets/coldkeypub.txt deleted file mode 100644 index e708f3066..000000000 --- a/examples/volume_manager/wallets/coldkeypub.txt +++ /dev/null @@ -1 +0,0 @@ -{"accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/hotkeys/default b/examples/volume_manager/wallets/hotkeys/default deleted file mode 100644 index f5f8f2b5e..000000000 --- a/examples/volume_manager/wallets/hotkeys/default +++ /dev/null @@ -1 +0,0 @@ -{"ss58Address":"5CDYBwxDZwXnYSp7E39Vy92MVbCMtcK2i953oRxDm9Veko7M","secretPhrase":"wool remember perfect current orbit tattoo net near donor club various company orphan tourist october material crane farm race clinic mouse cattle opinion tilt","secretSeed":"0x3434dc3b4ea59fcb0fbda2a6291deea7cb5d2739508a02991e806b0f0e597129","privateKey":"0xd29be7ec1105c7c101d26e5f7c4af14bb19e5c2a36abc17462666f706edaf70ec057f259a54c143beb14bf1e799399b7feccbed1421c0aa97648a75cc01414bd","publicKey":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860","accountId":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/coldkey b/examples/volume_manager/wallets/validator/coldkey deleted file mode 100644 index 0393c4826..000000000 --- a/examples/volume_manager/wallets/validator/coldkey +++ /dev/null @@ -1 +0,0 @@ -{"secretSeed":"0x5a71bd9b9ecd1453847b621e195d9cda469e233cad16f894010c8eac451ec8c4","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS","accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","secretPhrase":"unhappy occur suffer sense replace exist gorilla old warfare another gold act warfare banner question fresh average sorry match topple apart morning mechanic remain","privateKey":"0x55aacc8a8bca925f7f1f3bab48301adce2e6a2984dfad28877ddafd4b92c9d0b85025c223c4d0aca4e9e278a4818a10565f82d32698df9c3e4852ccaff9ed7e2"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/coldkeypub.txt b/examples/volume_manager/wallets/validator/coldkeypub.txt deleted file mode 100644 index e708f3066..000000000 --- a/examples/volume_manager/wallets/validator/coldkeypub.txt +++ /dev/null @@ -1 +0,0 @@ -{"accountId":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","publicKey":"0x4ed3ceafafe2553d2bb2b7eb0c8c3e6a2c950ef67feb2ee7a26be4ed9dde7852","ss58Address":"5Dr4WexBJE5kNGDYZsei8ar8X2pQPMDjhSBEWxZHhVQwgYnS"} \ No newline at end of file diff --git a/examples/volume_manager/wallets/validator/hotkeys/default b/examples/volume_manager/wallets/validator/hotkeys/default deleted file mode 100644 index f5f8f2b5e..000000000 --- a/examples/volume_manager/wallets/validator/hotkeys/default +++ /dev/null @@ -1 +0,0 @@ -{"ss58Address":"5CDYBwxDZwXnYSp7E39Vy92MVbCMtcK2i953oRxDm9Veko7M","secretPhrase":"wool remember perfect current orbit tattoo net near donor club various company orphan tourist october material crane farm race clinic mouse cattle opinion tilt","secretSeed":"0x3434dc3b4ea59fcb0fbda2a6291deea7cb5d2739508a02991e806b0f0e597129","privateKey":"0xd29be7ec1105c7c101d26e5f7c4af14bb19e5c2a36abc17462666f706edaf70ec057f259a54c143beb14bf1e799399b7feccbed1421c0aa97648a75cc01414bd","publicKey":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860","accountId":"0x06bcaa2432bbb3eede485b108399589fe7dd494a391b88c4e4c7015123df9860"} \ No newline at end of file diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index 52fbad5a6..35638d324 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -63,7 +63,7 @@ cd $PROJECT_ROOT/executor update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" # Uncomment the line below only if you have a volume manager service running -# update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" +update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner From c4c17115d295660fc0a4b7e8bf40cbca013a1e42 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:31:27 -0300 Subject: [PATCH 21/25] Final changes --- local_stack/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local_stack/prepare.sh b/local_stack/prepare.sh index 35638d324..52fbad5a6 100755 --- a/local_stack/prepare.sh +++ b/local_stack/prepare.sh @@ -63,7 +63,7 @@ cd $PROJECT_ROOT/executor update_env_var "DEBUG_NO_GPU_MODE" "true" ".env" # Uncomment the line below only if you have a volume manager service running -update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" +# update_env_var "VOLUME_MANAGER_ADDRESS" "http://localhost:8001" ".env" # setup miner cd $PROJECT_ROOT/miner From cc1e665c435710cb134fc99d0c17aead01419f4e Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:32:27 -0300 Subject: [PATCH 22/25] Final CI changes --- .github/workflows/integration_ci.yml | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index 596673055..f73d40e5d 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -3,7 +3,7 @@ name: Run integration tests # temporary disable integration test on: push: - branches: [master, main, ci_test] + branches: [master, main] pull_request: env: @@ -34,14 +34,14 @@ jobs: env: VOLUME_MANAGER_ADDRESS: "http://localhost:8001" - # - name: test - # working-directory: ./local_stack - # run: uv run ./send_hello_world_job.py - # env: - # AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} - # AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} - # AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} - # S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} + - name: test + working-directory: ./local_stack + run: uv run ./send_hello_world_job.py + env: + AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TESTS_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TESTS_AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} + S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} - name: test volume manager integration working-directory: ./examples/volume_manager @@ -49,10 +49,10 @@ jobs: env: VOLUME_MANAGER_ADDRESS: "http://localhost:8001" - # - name: Upload integration test logs - # uses: actions/upload-artifact@v4 - # if: always() - # with: - # name: integration-test-logs - # path: /tmp/integration_test_logs/*.log + - name: Upload integration test logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: integration-test-logs + path: /tmp/integration_test_logs/*.log From 221513925b74300636b546a65da64d7b249033ef Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:44:31 -0300 Subject: [PATCH 23/25] refactor CI test --- .github/workflows/volume_manager_ci.yml | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/volume_manager_ci.yml diff --git a/.github/workflows/volume_manager_ci.yml b/.github/workflows/volume_manager_ci.yml new file mode 100644 index 000000000..004899f00 --- /dev/null +++ b/.github/workflows/volume_manager_ci.yml @@ -0,0 +1,48 @@ +name: Volume Manager Integration Tests + +on: + push: + branches: [master, main, ci_test] + pull_request: + +env: + PYTHON_DEFAULT_VERSION: "3.11" + +jobs: + volume-manager-test: + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }} + uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_DEFAULT_VERSION }} + cache: "pip" + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + version: "0.6.x" + enable-cache: true + + - name: Set VOLUME_MANAGER_ADDRESS env variable + run: | + sed -i 's/# update_env_var "VOLUME_MANAGER_ADDRESS"/update_env_var "VOLUME_MANAGER_ADDRESS"/' local_stack/prepare.sh + + - name: Start all services + run: local_stack/run_and_await_readiness.sh /tmp/volume_manager_test_logs/ + + - name: Test volume manager integration + working-directory: ./examples/volume_manager + run: ./run_tests.sh -i -d + + - name: Upload volume manager test logs + uses: actions/upload-artifact@v4 + if: always() + with: + name: volume-manager-test-logs + path: /tmp/volume_manager_test_logs/*.log \ No newline at end of file From 6aa8eaec759724fccaa312612506104914587637 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:48:01 -0300 Subject: [PATCH 24/25] Set up CI for Volume Manager integration --- .github/workflows/volume_manager_ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/volume_manager_ci.yml b/.github/workflows/volume_manager_ci.yml index 004899f00..628c1c3b5 100644 --- a/.github/workflows/volume_manager_ci.yml +++ b/.github/workflows/volume_manager_ci.yml @@ -1,8 +1,8 @@ -name: Volume Manager Integration Tests +name: Run Volume Manager Integration Tests on: push: - branches: [master, main, ci_test] + branches: [master, main] pull_request: env: From 4b2f3942479a79bda9ff70752d562a80beaf9619 Mon Sep 17 00:00:00 2001 From: Mateus Cardoso Date: Wed, 6 Aug 2025 11:50:09 -0300 Subject: [PATCH 25/25] CI final changes for Volume Manager Integration --- .github/workflows/integration_ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/integration_ci.yml b/.github/workflows/integration_ci.yml index f73d40e5d..23df83ac5 100644 --- a/.github/workflows/integration_ci.yml +++ b/.github/workflows/integration_ci.yml @@ -31,8 +31,6 @@ jobs: enable-cache: true - name: Start all services run: local_stack/run_and_await_readiness.sh /tmp/integration_test_logs/ - env: - VOLUME_MANAGER_ADDRESS: "http://localhost:8001" - name: test working-directory: ./local_stack @@ -43,12 +41,6 @@ jobs: AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }} S3_BUCKET_NAME: ${{ env.S3_BUCKET_NAME }} - - name: test volume manager integration - working-directory: ./examples/volume_manager - run: ./run_tests.sh -i -d - env: - VOLUME_MANAGER_ADDRESS: "http://localhost:8001" - - name: Upload integration test logs uses: actions/upload-artifact@v4 if: always()