From 08b6b983a20fa0ec69823fba8651d65730e5cebe Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 14:08:17 -0300 Subject: [PATCH 1/9] First pass adding msgspec structs Added nix-shell dev file Switch to dev branch of py-leap for get_table row auto struct unpack Add testing module with helpers like override global load config function Add types for Queue V0 Rename test contract to gpu.scd to match current Add new enqueue test Update lock --- default.nix | 36 +++ pyproject.toml | 2 +- skynet/_testing.py | 9 + skynet/config.py | 12 +- skynet/constants.py | 15 +- skynet/dgpu/compute.py | 57 ++-- skynet/dgpu/daemon.py | 63 ++-- skynet/dgpu/network.py | 12 +- skynet/types.py | 47 +++ tests/conftest.py | 18 +- .../telos.gpu.abi => gpu.scd/gpu.scd.abi} | 0 .../telos.gpu.wasm => gpu.scd/gpu.scd.wasm} | Bin tests/test_chain.py | 66 +++- tests/test_reqs.py | 49 +-- uv.lock | 304 +++++++++--------- 15 files changed, 423 insertions(+), 267 deletions(-) create mode 100644 default.nix create mode 100644 skynet/_testing.py create mode 100644 skynet/types.py rename tests/contracts/{telos.gpu/telos.gpu.abi => gpu.scd/gpu.scd.abi} (100%) rename tests/contracts/{telos.gpu/telos.gpu.wasm => gpu.scd/gpu.scd.wasm} (100%) diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..e07932c --- /dev/null +++ b/default.nix @@ -0,0 +1,36 @@ +with (import {}); +let + glibStorePath = lib.getLib glib; + zstdStorePath = lib.getLib zstd; + libGLStorePath = lib.getLib libGL; +in +stdenv.mkDerivation { + name = "skynet"; + buildInputs = [ + # System requirements. + glib + zstd + libGL + libgcc.lib + # Python requirements. + python313Full + python313Packages.uv + ]; + src = null; + shellHook = '' + set -e + + LIB_GCC_PATH="${libgcc.lib}/lib" + GLIB_PATH="${glibStorePath}/lib" + ZSTD_PATH="${zstdStorePath}/lib" + LIBGL_PATH="${libGLStorePath}/lib" + + + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$LIB_GCC_PATH" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$GLIB_PATH" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$ZSTD_PATH" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$LIBGL_PATH" + + export LD_LIBRARY_PATH + ''; +} diff --git a/pyproject.toml b/pyproject.toml index 05bc088..a99e121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,7 @@ explicit = true torch = { index = "torch" } triton = { index = "torch" } torchvision = { index = "torch" } -py-leap = { git = "https://github.com/guilledk/py-leap.git", rev = "v0.1a34" } +py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" } pytest-dockerctl = { git = "https://github.com/pikers/pytest-dockerctl.git", branch = "g_update" } [build-system] diff --git a/skynet/_testing.py b/skynet/_testing.py new file mode 100644 index 0000000..c6aa435 --- /dev/null +++ b/skynet/_testing.py @@ -0,0 +1,9 @@ +from skynet.config import Config, DgpuConfig, set_config_override + + +def override_dgpu_config(**kwargs) -> DgpuConfig: + config = Config( + dgpu=DgpuConfig(**kwargs) + ) + set_config_override(config) + return config.dgpu diff --git a/skynet/config.py b/skynet/config.py index f319714..fed610d 100755 --- a/skynet/config.py +++ b/skynet/config.py @@ -1,5 +1,4 @@ import os -import toml import msgspec @@ -59,7 +58,18 @@ class Config(msgspec.Struct): user: UserConfig | None = None +__config_override = None +def set_config_override(config: Config): + global __config_override + __config_override = config + + def load_skynet_toml(file_path=DEFAULT_CONFIG_PATH) -> Config: + global __config_override + + if __config_override: + return __config_override + with open(file_path, 'r') as file: return msgspec.toml.decode(file.read(), type=Config) diff --git a/skynet/constants.py b/skynet/constants.py index f0af6ee..4b09f6e 100755 --- a/skynet/constants.py +++ b/skynet/constants.py @@ -1,20 +1,9 @@ -import msgspec +from skynet.types import ModelDesc -from enum import Enum -from typing import Literal - -VERSION = '0.1a12' +VERSION = '0.1a13' DOCKER_RUNTIME_CUDA = 'skynet:runtime-cuda' - -class ModelDesc(msgspec.Struct): - short: str # short unique name - mem: float # recomended mem - attrs: dict # additional mode specific attrs - tags: list[Literal['txt2img', 'img2img', 'inpaint', 'upscale']] - - MODELS: dict[str, ModelDesc] = { 'RealESRGAN_x4plus': ModelDesc( short='realesrgan', diff --git a/skynet/dgpu/compute.py b/skynet/dgpu/compute.py index 3a0645a..96f7635 100755 --- a/skynet/dgpu/compute.py +++ b/skynet/dgpu/compute.py @@ -13,6 +13,7 @@ import trio import torch from skynet.config import load_skynet_toml +from skynet.types import ModelMode, BodyV0, BodyV0Params from skynet.dgpu.tui import maybe_update_tui from skynet.dgpu.errors import ( DGPUComputeError, @@ -23,13 +24,13 @@ from skynet.dgpu.utils import crop_image, convert_from_cv2_to_image, convert_fro def prepare_params_for_diffuse( - params: dict, - mode: str, + params: BodyV0Params, + mode: ModelMode, inputs: list[bytes] ): _params = {} match mode: - case 'inpaint': + case ModelMode.INPAINT: image = crop_image( inputs[0], params['width'], params['height']) @@ -44,28 +45,25 @@ def prepare_params_for_diffuse( else: _params['strength'] = float(params['strength']) - case 'img2img': + case ModelMode.IMG2IMG: image = crop_image( inputs[0], params['width'], params['height']) _params['image'] = image _params['strength'] = float(params['strength']) - case 'txt2img' | 'diffuse': + case ModelMode.TXT2IMG | ModelMode.DIFFUSE: ... case _: raise DGPUComputeError(f'Unknown mode {mode}') - # _params['width'] = int(params['width']) - # _params['height'] = int(params['height']) - return ( - params['prompt'], - float(params['guidance']), - int(params['step']), - torch.manual_seed(int(params['seed'])), - params['upscaler'] if 'upscaler' in params else None, + params.prompt, + params.guidance, + params.step, + torch.manual_seed(int(params.seed)), + params.upscaler, _params ) @@ -74,9 +72,9 @@ _model_mode: str = '' _model = None @cm -def maybe_load_model(name: str, mode: str): - if mode == 'diffuse': - mode = 'txt2img' +def maybe_load_model(name: str, mode: ModelMode): + if mode == ModelMode.DIFFUSE: + mode = ModelMode.TXT2IMG global _model_name, _model_mode, _model config = load_skynet_toml().dgpu @@ -90,7 +88,7 @@ def maybe_load_model(name: str, mode: str): _model_name = _model_mode = '' # load model - if mode == 'upscale': + if mode == ModelMode.UPSCALE: _model = init_upscaler() else: @@ -100,8 +98,9 @@ def maybe_load_model(name: str, mode: str): _model_name = name _model_mode = mode - logging.debug('memory summary:') - logging.debug('\n' + torch.cuda.memory_summary()) + if torch.cuda.is_available(): + logging.debug('memory summary:') + logging.debug('\n' + torch.cuda.memory_summary()) yield _model @@ -109,12 +108,12 @@ def maybe_load_model(name: str, mode: str): def compute_one( model, request_id: int, - method: str, - params: dict, + method: ModelMode, + params: BodyV0Params, inputs: list[bytes] = [], should_cancel = None ): - total_steps = params['step'] if 'step' in params else 1 + total_steps = params.step def inference_step_wakeup(*args, **kwargs): '''This is a callback function that gets invoked every inference step, we need to raise an exception here if we need to cancel work @@ -140,17 +139,19 @@ def compute_one( inference_step_wakeup(0) - output_type = 'png' - if 'output_type' in params: - output_type = params['output_type'] - + output_type = params.output_type output = None output_hash = None try: - name = params['model'] + name = params.model match method: - case 'diffuse' | 'txt2img' | 'img2img' | 'inpaint': + case ( + ModelMode.DIFFUSE | + ModelMode.TXT2IMG | + ModelMode.IMG2IMG | + ModelMode.INPAINT + ): arguments = prepare_params_for_diffuse( params, method, inputs) prompt, guidance, step, seed, upscaler, extra_params = arguments diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index b23c652..dfac447 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -1,21 +1,18 @@ -import json import logging import random -import time -from datetime import datetime from functools import partial from hashlib import sha256 import trio +import msgspec from skynet.config import DgpuConfig as Config -from skynet.constants import ( - MODELS, - VERSION, -) -from skynet.dgpu.errors import ( - DGPUComputeError, +from skynet.types import ( + RequestV0, + BodyV0 ) +from skynet.constants import MODELS +from skynet.dgpu.errors import DGPUComputeError from skynet.dgpu.tui import maybe_update_tui, maybe_update_tui_async from skynet.dgpu.compute import maybe_load_model, compute_one from skynet.dgpu.network import NetConnector @@ -41,14 +38,13 @@ async def maybe_update_tui_balance(conn: NetConnector): async def maybe_serve_one( config: Config, conn: NetConnector, - req: dict, + req: RequestV0, ): - rid = req['id'] - logging.info(f'maybe serve request #{rid}') + logging.info(f'maybe serve request #{req.id}') # parse request - body = json.loads(req['body']) - model = body['params']['model'] + body = msgspec.json.decode(req.body, type=BodyV0) + model = body.params.model # if model not known, ignore. if model not in MODELS: @@ -76,11 +72,11 @@ async def maybe_serve_one( results = [res['request_id'] for res in conn._tables['results']] # if worker already produced a result for this request - if rid in results: - logging.info(f'worker already submitted a result for request #{rid}, skip...') + if req.id in results: + logging.info(f'worker already submitted a result for request #{req.id}, skip...') return - statuses = conn._tables['requests'][rid] + statuses = conn._tables['requests'][req.id] # skip if workers in non_compete already on it competitors = set((status['worker'] for status in statuses)) @@ -90,12 +86,12 @@ async def maybe_serve_one( # resolve the ipfs hashes into the actual data behind them inputs = [] - raw_inputs = req['binary_data'].split(',') + raw_inputs = req.binary_data.split(',') if raw_inputs: logging.info(f'fetching IPFS inputs: {raw_inputs}') retry = 3 - for _input in req['binary_data'].split(','): + for _input in raw_inputs: if _input: for r in range(retry): try: @@ -114,24 +110,22 @@ async def maybe_serve_one( # compute unique request hash used on submit hash_str = ( - str(req['nonce']) + str(req.nonce) + - req['body'] + req.body + - req['binary_data'] + req.binary_data ) logging.debug(f'hashing: {hash_str}') request_hash = sha256(hash_str.encode('utf-8')).hexdigest() logging.info(f'calculated request hash: {request_hash}') - params = body['params'] - total_step = params['step'] if 'step' in params else 1 - model = body['params']['model'] - mode = body['method'] + total_step = body.params.step + mode = body.method # TODO: validate request - resp = await conn.begin_work(rid) + resp = await conn.begin_work(req.id) if not resp or 'code' in resp: logging.info('begin_work error, probably being worked on already... skip.') return @@ -140,10 +134,7 @@ async def maybe_serve_one( try: maybe_update_tui(lambda tui: tui.set_progress(0, done=total_step)) - output_type = 'png' - if 'output_type' in body['params']: - output_type = body['params']['output_type'] - + output_type = body.params.output_type output = None output_hash = None match config.backend: @@ -152,8 +143,8 @@ async def maybe_serve_one( partial( compute_one, model, - rid, - mode, params, + req.id, + mode, body.params, inputs=inputs, should_cancel=conn.should_cancel_work, ) @@ -168,7 +159,7 @@ async def maybe_serve_one( ipfs_hash = await conn.publish_on_ipfs(output, typ=output_type) - await conn.submit_work(rid, request_hash, output_hash, ipfs_hash) + await conn.submit_work(req.id, request_hash, output_hash, ipfs_hash) await maybe_update_tui_balance(conn) @@ -177,8 +168,8 @@ async def maybe_serve_one( if 'network cancel' not in str(err): logging.exception('Failed to serve model request !?\n') - if rid in conn._tables['requests']: - await conn.cancel_work(rid, 'reason not provided') + if req.id in conn._tables['requests']: + await conn.cancel_work(req.id, 'reason not provided') async def dgpu_serve_forever(config: Config, conn: NetConnector): diff --git a/skynet/dgpu/network.py b/skynet/dgpu/network.py index e6585d1..72b4812 100755 --- a/skynet/dgpu/network.py +++ b/skynet/dgpu/network.py @@ -3,7 +3,6 @@ import json import time import logging from pathlib import Path -from typing import AsyncGenerator from functools import partial import trio @@ -16,10 +15,8 @@ from leap.cleos import CLEOS from leap.protocol import Asset from skynet.dgpu.tui import maybe_update_tui from skynet.config import DgpuConfig as Config -from skynet.constants import ( - DEFAULT_IPFS_DOMAIN, - GPU_CONTRACT_ABI, -) +from skynet.types import RequestV0 +from skynet.constants import GPU_CONTRACT_ABI from skynet.ipfs import ( AsyncIPFSHTTP, @@ -81,7 +78,7 @@ class NetConnector: # blockchain helpers - async def get_work_requests_last_hour(self): + async def get_work_requests_last_hour(self) -> list[RequestV0]: logging.info('get_work_requests_last_hour') rows = await failable( partial( @@ -89,7 +86,8 @@ class NetConnector: 'gpu.scd', 'gpu.scd', 'queue', index_position=2, key_type='i64', - lower_bound=int(time.time()) - 3600 + lower_bound=int(time.time()) - 3600, + resp_cls=RequestV0 ), ret_fail=[]) logging.info(f'found {len(rows)} requests on queue') diff --git a/skynet/types.py b/skynet/types.py new file mode 100644 index 0000000..ef28e67 --- /dev/null +++ b/skynet/types.py @@ -0,0 +1,47 @@ +from enum import StrEnum + +from msgspec import Struct + + +class ModelMode(StrEnum): + DIFFUSE = 'diffuse' + TXT2IMG = 'txt2img' + IMG2IMG = 'img2img' + UPSCALE = 'upscale' + INPAINT = 'inpaint' + + +class ModelDesc(Struct): + short: str # short unique name + mem: float # recomended mem + attrs: dict # additional mode specific attrs + tags: list[ModelMode] + + +class BodyV0Params(Struct): + prompt: str + model: str + seed: int + step: int = 1 + guidance: float | None = None + width: int | None = None + height: int | None = None + strength: float | None = None + output_type: str | None = 'png' + upscaler: str | None = None + + +class BodyV0(Struct): + method: ModelMode + params: BodyV0Params + + +class RequestV0(Struct): + id: int + user: str + reward: str + min_verification: int + nonce: int + body: str + binary_data: str + timestamp: str diff --git a/tests/conftest.py b/tests/conftest.py index 4c6a406..1bf3c72 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,5 @@ import pytest -from skynet.config import * from skynet.ipfs import AsyncIPFSHTTP @@ -21,20 +20,20 @@ def skynet_cleos(cleos_bs): cleos = cleos_bs priv, pub = cleos.create_key_pair() - cleos.import_key('telos.gpu', priv) - cleos.new_account('telos.gpu', ram=4200000, key=pub) + cleos.import_key('gpu.scd', priv) + cleos.new_account('gpu.scd', ram=4200000, key=pub) cleos.deploy_contract_from_path( - 'telos.gpu', - 'tests/contracts/telos.gpu', + 'gpu.scd', + 'tests/contracts/gpu.scd', create_account=False ) cleos.push_action( - 'telos.gpu', + 'gpu.scd', 'config', - ['eosio.token', '4,GPU'], - 'telos.gpu' + ['eosio.token', '4,TLOS'], + 'gpu.scd' ) yield cleos @@ -42,7 +41,8 @@ def skynet_cleos(cleos_bs): @pytest.fixture def inject_mockers(): - from skynet.constants import MODELS, ModelDesc + from skynet.constants import MODELS + from skynet.types import ModelDesc MODELS['skygpu/txt2img-mocker'] = ModelDesc( short='tester', diff --git a/tests/contracts/telos.gpu/telos.gpu.abi b/tests/contracts/gpu.scd/gpu.scd.abi similarity index 100% rename from tests/contracts/telos.gpu/telos.gpu.abi rename to tests/contracts/gpu.scd/gpu.scd.abi diff --git a/tests/contracts/telos.gpu/telos.gpu.wasm b/tests/contracts/gpu.scd/gpu.scd.wasm similarity index 100% rename from tests/contracts/telos.gpu/telos.gpu.wasm rename to tests/contracts/gpu.scd/gpu.scd.wasm diff --git a/tests/test_chain.py b/tests/test_chain.py index 243115a..68c3cc1 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -1,3 +1,65 @@ -def test_dev(skynet_cleos): +from msgspec import json + +from skynet.types import BodyV0, BodyV0Params +from skynet.dgpu.network import NetConnector + +from skynet._testing import override_dgpu_config + + +async def test_enqueue(skynet_cleos): cleos = skynet_cleos - ... + + # create account and deposit tokens into gpu + account = cleos.new_account() + quantity = '1000.0000 TLOS' + cleos.transfer_token('eosio', account, quantity) + cleos.transfer_token(account, 'gpu.scd', quantity) + + og_body = BodyV0( + method='txt2img', + params=BodyV0Params( + prompt='cyberpunk hacker travis bickle dystopic alley graffiti', + model='skygpu/txt2img-mocker', + step=4, + seed=0, + guidance=10.0 + ) + ) + + body = json.encode(og_body).decode('utf-8') + binary_data = '' + reward = '1.0000 TLOS' + min_verification = 1 + + # send enqueue req + cleos.push_action( + 'gpu.scd', + 'enqueue', + [ + account, + body, + binary_data, + reward, + min_verification + ], + account, + key=cleos.private_keys[account] + ) + + config = override_dgpu_config( + account='testworker1', + permission='active', + key='', + node_url=cleos.endpoint, + ipfs_url='http://127.0.0.1:5001', + hf_token='' + ) + net = NetConnector(config) + queue = await net.get_work_requests_last_hour() + + assert len(queue) == 1 + + req = queue[0] + body = json.decode(req.body, type=BodyV0) + + assert og_body == body diff --git a/tests/test_reqs.py b/tests/test_reqs.py index 03a1cc6..274c691 100644 --- a/tests/test_reqs.py +++ b/tests/test_reqs.py @@ -1,22 +1,33 @@ import pytest +from skynet.types import ModelMode, BodyV0Params from skynet.dgpu.compute import maybe_load_model, compute_one +from skynet._testing import override_dgpu_config + @pytest.mark.parametrize("mode", [ - ('diffuse'), ('txt2img') + (ModelMode.DIFFUSE), (ModelMode.TXT2IMG) ]) async def test_pipeline_mocker(inject_mockers, mode): + override_dgpu_config( + account='testworker1', + permission='active', + key='', + node_url='', + ipfs_url='http://127.0.0.1:5001', + hf_token='' + ) model = 'skygpu/txt2img-mocker' - params = { - "prompt": "Kronos God Realistic 4k", - "model": model, - "step": 21, - "width": 1024, - "height": 1024, - "seed": 168402949, - "guidance": "7.5" - } + params = BodyV0Params( + prompt="Kronos God Realistic 4k", + model=model, + step=4, + width=1024, + height=1024, + seed=168402949, + guidance="7.5" + ) with maybe_load_model(model, mode) as model: compute_one(model, 0, mode, params) @@ -25,15 +36,15 @@ async def test_pipeline_mocker(inject_mockers, mode): async def test_pipeline(): model = 'stabilityai/stable-diffusion-xl-base-1.0' mode = 'txt2img' - params = { - "prompt": "Kronos God Realistic 4k", - "model": model, - "step": 21, - "width": 1024, - "height": 1024, - "seed": 168402949, - "guidance": "7.5" - } + params = BodyV0Params( + prompt="Kronos God Realistic 4k", + model=model, + step=21, + width=1024, + height=1024, + seed=168402949, + guidance="7.5" + ) with maybe_load_model(model, mode) as model: compute_one(model, 0, mode, params) diff --git a/uv.lock b/uv.lock index 29475b7..2f32770 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,3 @@ -# vim: ft=toml version = 1 requires-python = ">=3.10, <3.13" resolution-markers = [ @@ -51,16 +50,16 @@ wheels = [ [[package]] name = "aiohappyeyeballs" -version = "2.4.4" +version = "2.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/55/e4373e888fdacb15563ef6fa9fa8c8252476ea071e96fb46defac9f18bf2/aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", size = 21977 } +sdist = { url = "https://files.pythonhosted.org/packages/08/07/508f9ebba367fc3370162e53a3cfd12f5652ad79f0e0bfdf9f9847c6f159/aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0", size = 21726 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/74/fbb6559de3607b3300b9be3cc64e97548d55678e44623db17820dbd20002/aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8", size = 14756 }, + { url = "https://files.pythonhosted.org/packages/44/4c/03fb05f56551828ec67ceb3665e5dc51638042d204983a03b0a1541475b6/aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1", size = 14543 }, ] [[package]] name = "aiohttp" -version = "3.11.11" +version = "3.11.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -72,53 +71,56 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/ed/f26db39d29cd3cb2f5a3374304c713fe5ab5a0e4c8ee25a0c45cc6adf844/aiohttp-3.11.11.tar.gz", hash = "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e", size = 7669618 } +sdist = { url = "https://files.pythonhosted.org/packages/37/4b/952d49c73084fb790cb5c6ead50848c8e96b4980ad806cf4d2ad341eaa03/aiohttp-3.11.12.tar.gz", hash = "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0", size = 7673175 } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/7d/ff2e314b8f9e0b1df833e2d4778eaf23eae6b8cc8f922495d110ddcbf9e1/aiohttp-3.11.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8", size = 708550 }, - { url = "https://files.pythonhosted.org/packages/09/b8/aeb4975d5bba233d6f246941f5957a5ad4e3def8b0855a72742e391925f2/aiohttp-3.11.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5", size = 468430 }, - { url = "https://files.pythonhosted.org/packages/9c/5b/5b620279b3df46e597008b09fa1e10027a39467387c2332657288e25811a/aiohttp-3.11.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2", size = 455593 }, - { url = "https://files.pythonhosted.org/packages/d8/75/0cdf014b816867d86c0bc26f3d3e3f194198dbf33037890beed629cd4f8f/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43", size = 1584635 }, - { url = "https://files.pythonhosted.org/packages/df/2f/95b8f4e4dfeb57c1d9ad9fa911ede35a0249d75aa339edd2c2270dc539da/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f", size = 1632363 }, - { url = "https://files.pythonhosted.org/packages/39/cb/70cf69ea7c50f5b0021a84f4c59c3622b2b3b81695f48a2f0e42ef7eba6e/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d", size = 1668315 }, - { url = "https://files.pythonhosted.org/packages/2f/cc/3a3fc7a290eabc59839a7e15289cd48f33dd9337d06e301064e1e7fb26c5/aiohttp-3.11.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef", size = 1589546 }, - { url = "https://files.pythonhosted.org/packages/15/b4/0f7b0ed41ac6000e283e7332f0f608d734b675a8509763ca78e93714cfb0/aiohttp-3.11.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438", size = 1544581 }, - { url = "https://files.pythonhosted.org/packages/58/b9/4d06470fd85c687b6b0e31935ef73dde6e31767c9576d617309a2206556f/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3", size = 1529256 }, - { url = "https://files.pythonhosted.org/packages/61/a2/6958b1b880fc017fd35f5dfb2c26a9a50c755b75fd9ae001dc2236a4fb79/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55", size = 1536592 }, - { url = "https://files.pythonhosted.org/packages/0f/dd/b974012a9551fd654f5bb95a6dd3f03d6e6472a17e1a8216dd42e9638d6c/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e", size = 1607446 }, - { url = "https://files.pythonhosted.org/packages/e0/d3/6c98fd87e638e51f074a3f2061e81fcb92123bcaf1439ac1b4a896446e40/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33", size = 1628809 }, - { url = "https://files.pythonhosted.org/packages/a8/2e/86e6f85cbca02be042c268c3d93e7f35977a0e127de56e319bdd1569eaa8/aiohttp-3.11.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c", size = 1564291 }, - { url = "https://files.pythonhosted.org/packages/0b/8d/1f4ef3503b767717f65e1f5178b0173ab03cba1a19997ebf7b052161189f/aiohttp-3.11.11-cp310-cp310-win32.whl", hash = "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745", size = 416601 }, - { url = "https://files.pythonhosted.org/packages/ad/86/81cb83691b5ace3d9aa148dc42bacc3450d749fc88c5ec1973573c1c1779/aiohttp-3.11.11-cp310-cp310-win_amd64.whl", hash = "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9", size = 442007 }, - { url = "https://files.pythonhosted.org/packages/34/ae/e8806a9f054e15f1d18b04db75c23ec38ec954a10c0a68d3bd275d7e8be3/aiohttp-3.11.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76", size = 708624 }, - { url = "https://files.pythonhosted.org/packages/c7/e0/313ef1a333fb4d58d0c55a6acb3cd772f5d7756604b455181049e222c020/aiohttp-3.11.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538", size = 468507 }, - { url = "https://files.pythonhosted.org/packages/a9/60/03455476bf1f467e5b4a32a465c450548b2ce724eec39d69f737191f936a/aiohttp-3.11.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204", size = 455571 }, - { url = "https://files.pythonhosted.org/packages/be/f9/469588603bd75bf02c8ffb8c8a0d4b217eed446b49d4a767684685aa33fd/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9", size = 1685694 }, - { url = "https://files.pythonhosted.org/packages/88/b9/1b7fa43faf6c8616fa94c568dc1309ffee2b6b68b04ac268e5d64b738688/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03", size = 1743660 }, - { url = "https://files.pythonhosted.org/packages/2a/8b/0248d19dbb16b67222e75f6aecedd014656225733157e5afaf6a6a07e2e8/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287", size = 1785421 }, - { url = "https://files.pythonhosted.org/packages/c4/11/f478e071815a46ca0a5ae974651ff0c7a35898c55063305a896e58aa1247/aiohttp-3.11.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e", size = 1675145 }, - { url = "https://files.pythonhosted.org/packages/26/5d/284d182fecbb5075ae10153ff7374f57314c93a8681666600e3a9e09c505/aiohttp-3.11.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665", size = 1619804 }, - { url = "https://files.pythonhosted.org/packages/1b/78/980064c2ad685c64ce0e8aeeb7ef1e53f43c5b005edcd7d32e60809c4992/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b", size = 1654007 }, - { url = "https://files.pythonhosted.org/packages/21/8d/9e658d63b1438ad42b96f94da227f2e2c1d5c6001c9e8ffcc0bfb22e9105/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34", size = 1650022 }, - { url = "https://files.pythonhosted.org/packages/85/fd/a032bf7f2755c2df4f87f9effa34ccc1ef5cea465377dbaeef93bb56bbd6/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d", size = 1732899 }, - { url = "https://files.pythonhosted.org/packages/c5/0c/c2b85fde167dd440c7ba50af2aac20b5a5666392b174df54c00f888c5a75/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2", size = 1755142 }, - { url = "https://files.pythonhosted.org/packages/bc/78/91ae1a3b3b3bed8b893c5d69c07023e151b1c95d79544ad04cf68f596c2f/aiohttp-3.11.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773", size = 1692736 }, - { url = "https://files.pythonhosted.org/packages/77/89/a7ef9c4b4cdb546fcc650ca7f7395aaffbd267f0e1f648a436bec33c9b95/aiohttp-3.11.11-cp311-cp311-win32.whl", hash = "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62", size = 416418 }, - { url = "https://files.pythonhosted.org/packages/fc/db/2192489a8a51b52e06627506f8ac8df69ee221de88ab9bdea77aa793aa6a/aiohttp-3.11.11-cp311-cp311-win_amd64.whl", hash = "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac", size = 442509 }, - { url = "https://files.pythonhosted.org/packages/69/cf/4bda538c502f9738d6b95ada11603c05ec260807246e15e869fc3ec5de97/aiohttp-3.11.11-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886", size = 704666 }, - { url = "https://files.pythonhosted.org/packages/46/7b/87fcef2cad2fad420ca77bef981e815df6904047d0a1bd6aeded1b0d1d66/aiohttp-3.11.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2", size = 464057 }, - { url = "https://files.pythonhosted.org/packages/5a/a6/789e1f17a1b6f4a38939fbc39d29e1d960d5f89f73d0629a939410171bc0/aiohttp-3.11.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c", size = 455996 }, - { url = "https://files.pythonhosted.org/packages/b7/dd/485061fbfef33165ce7320db36e530cd7116ee1098e9c3774d15a732b3fd/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a", size = 1682367 }, - { url = "https://files.pythonhosted.org/packages/e9/d7/9ec5b3ea9ae215c311d88b2093e8da17e67b8856673e4166c994e117ee3e/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231", size = 1736989 }, - { url = "https://files.pythonhosted.org/packages/d6/fb/ea94927f7bfe1d86178c9d3e0a8c54f651a0a655214cce930b3c679b8f64/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e", size = 1793265 }, - { url = "https://files.pythonhosted.org/packages/40/7f/6de218084f9b653026bd7063cd8045123a7ba90c25176465f266976d8c82/aiohttp-3.11.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8", size = 1691841 }, - { url = "https://files.pythonhosted.org/packages/77/e2/992f43d87831cbddb6b09c57ab55499332f60ad6fdbf438ff4419c2925fc/aiohttp-3.11.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8", size = 1619317 }, - { url = "https://files.pythonhosted.org/packages/96/74/879b23cdd816db4133325a201287c95bef4ce669acde37f8f1b8669e1755/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c", size = 1641416 }, - { url = "https://files.pythonhosted.org/packages/30/98/b123f6b15d87c54e58fd7ae3558ff594f898d7f30a90899718f3215ad328/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab", size = 1646514 }, - { url = "https://files.pythonhosted.org/packages/d7/38/257fda3dc99d6978ab943141d5165ec74fd4b4164baa15e9c66fa21da86b/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da", size = 1702095 }, - { url = "https://files.pythonhosted.org/packages/0c/f4/ddab089053f9fb96654df5505c0a69bde093214b3c3454f6bfdb1845f558/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853", size = 1734611 }, - { url = "https://files.pythonhosted.org/packages/c3/d6/f30b2bc520c38c8aa4657ed953186e535ae84abe55c08d0f70acd72ff577/aiohttp-3.11.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e", size = 1694576 }, - { url = "https://files.pythonhosted.org/packages/bc/97/b0a88c3f4c6d0020b34045ee6d954058abc870814f6e310c4c9b74254116/aiohttp-3.11.11-cp312-cp312-win32.whl", hash = "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600", size = 411363 }, - { url = "https://files.pythonhosted.org/packages/7f/23/cc36d9c398980acaeeb443100f0216f50a7cfe20c67a9fd0a2f1a5a846de/aiohttp-3.11.11-cp312-cp312-win_amd64.whl", hash = "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d", size = 437666 }, + { url = "https://files.pythonhosted.org/packages/65/42/3880e133590820aa7bc6d068eb7d8e0ad9fdce9b4663f92b821d3f6b5601/aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f", size = 708721 }, + { url = "https://files.pythonhosted.org/packages/d8/8c/04869803bed108b25afad75f94c651b287851843caacbec6677d8f2d572b/aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854", size = 468596 }, + { url = "https://files.pythonhosted.org/packages/4f/f4/9074011f0d1335b161c953fb32545b6667cf24465e1932b9767874995c7e/aiohttp-3.11.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957", size = 455758 }, + { url = "https://files.pythonhosted.org/packages/fd/68/06298c57ef8f534065930b805e6dbd83613f0534447922782fb9920fce28/aiohttp-3.11.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42", size = 1584797 }, + { url = "https://files.pythonhosted.org/packages/bd/1e/cee6b51fcb3b1c4185a7dc62b3113bc136fae07f39386c88c90b7f79f199/aiohttp-3.11.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55", size = 1632535 }, + { url = "https://files.pythonhosted.org/packages/71/1f/42424462b7a09da362e1711090db9f8d68a37a33f0aab51307335517c599/aiohttp-3.11.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb", size = 1668484 }, + { url = "https://files.pythonhosted.org/packages/f6/79/0e25542bbe3c2bfd7a12c7a49c7bce73b09a836f65079e4b77bc2bafc89e/aiohttp-3.11.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae", size = 1589708 }, + { url = "https://files.pythonhosted.org/packages/d1/13/93ae26b75e23f7d3a613872e472fae836ca100dc5bde5936ebc93ada8890/aiohttp-3.11.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7", size = 1544752 }, + { url = "https://files.pythonhosted.org/packages/cf/5e/48847fad1b014ef92ef18ea1339a3b58eb81d3bc717b94c3627f5d2a42c5/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788", size = 1529417 }, + { url = "https://files.pythonhosted.org/packages/ae/56/fbd4ea019303f4877f0e0b8c9de92e9db24338e7545570d3f275f3c74c53/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e", size = 1557808 }, + { url = "https://files.pythonhosted.org/packages/f1/43/112189cf6b3c482ecdd6819b420eaa0c2033426f28d741bb7f19db5dd2bb/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5", size = 1536765 }, + { url = "https://files.pythonhosted.org/packages/30/12/59986547de8306e06c7b30e547ccda02d29636e152366caba2dd8627bfe1/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb", size = 1607621 }, + { url = "https://files.pythonhosted.org/packages/aa/9b/af3b323b20df3318ed20d701d8242e523d59c842ca93f23134b05c9d5054/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf", size = 1628977 }, + { url = "https://files.pythonhosted.org/packages/36/62/adf5a331a7bda475cc326dde393fa2bc5849060b1b37ac3d1bee1953f2cd/aiohttp-3.11.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff", size = 1564455 }, + { url = "https://files.pythonhosted.org/packages/90/c4/4a24291f22f111a854dfdb54dc94d4e0a5229ccbb7bc7f0bed972aa50410/aiohttp-3.11.12-cp310-cp310-win32.whl", hash = "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d", size = 416768 }, + { url = "https://files.pythonhosted.org/packages/51/69/5221c8006acb7bb10d9e8e2238fb216571bddc2e00a8d95bcfbe2f579c57/aiohttp-3.11.12-cp310-cp310-win_amd64.whl", hash = "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5", size = 442170 }, + { url = "https://files.pythonhosted.org/packages/9c/38/35311e70196b6a63cfa033a7f741f800aa8a93f57442991cbe51da2394e7/aiohttp-3.11.12-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb", size = 708797 }, + { url = "https://files.pythonhosted.org/packages/44/3e/46c656e68cbfc4f3fc7cb5d2ba4da6e91607fe83428208028156688f6201/aiohttp-3.11.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9", size = 468669 }, + { url = "https://files.pythonhosted.org/packages/a0/d6/2088fb4fd1e3ac2bfb24bc172223babaa7cdbb2784d33c75ec09e66f62f8/aiohttp-3.11.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933", size = 455739 }, + { url = "https://files.pythonhosted.org/packages/e7/dc/c443a6954a56f4a58b5efbfdf23cc6f3f0235e3424faf5a0c56264d5c7bb/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1", size = 1685858 }, + { url = "https://files.pythonhosted.org/packages/25/67/2d5b3aaade1d5d01c3b109aa76e3aa9630531252cda10aa02fb99b0b11a1/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94", size = 1743829 }, + { url = "https://files.pythonhosted.org/packages/90/9b/9728fe9a3e1b8521198455d027b0b4035522be18f504b24c5d38d59e7278/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6", size = 1785587 }, + { url = "https://files.pythonhosted.org/packages/ce/cf/28fbb43d4ebc1b4458374a3c7b6db3b556a90e358e9bbcfe6d9339c1e2b6/aiohttp-3.11.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5", size = 1675319 }, + { url = "https://files.pythonhosted.org/packages/e5/d2/006c459c11218cabaa7bca401f965c9cc828efbdea7e1615d4644eaf23f7/aiohttp-3.11.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204", size = 1619982 }, + { url = "https://files.pythonhosted.org/packages/9d/83/ca425891ebd37bee5d837110f7fddc4d808a7c6c126a7d1b5c3ad72fc6ba/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58", size = 1654176 }, + { url = "https://files.pythonhosted.org/packages/25/df/047b1ce88514a1b4915d252513640184b63624e7914e41d846668b8edbda/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef", size = 1660198 }, + { url = "https://files.pythonhosted.org/packages/d3/cc/6ecb8e343f0902528620b9dbd567028a936d5489bebd7dbb0dd0914f4fdb/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420", size = 1650186 }, + { url = "https://files.pythonhosted.org/packages/f8/f8/453df6dd69256ca8c06c53fc8803c9056e2b0b16509b070f9a3b4bdefd6c/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df", size = 1733063 }, + { url = "https://files.pythonhosted.org/packages/55/f8/540160787ff3000391de0e5d0d1d33be4c7972f933c21991e2ea105b2d5e/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804", size = 1755306 }, + { url = "https://files.pythonhosted.org/packages/30/7d/49f3bfdfefd741576157f8f91caa9ff61a6f3d620ca6339268327518221b/aiohttp-3.11.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b", size = 1692909 }, + { url = "https://files.pythonhosted.org/packages/40/9c/8ce00afd6f6112ce9a2309dc490fea376ae824708b94b7b5ea9cba979d1d/aiohttp-3.11.12-cp311-cp311-win32.whl", hash = "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16", size = 416584 }, + { url = "https://files.pythonhosted.org/packages/35/97/4d3c5f562f15830de472eb10a7a222655d750839943e0e6d915ef7e26114/aiohttp-3.11.12-cp311-cp311-win_amd64.whl", hash = "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6", size = 442674 }, + { url = "https://files.pythonhosted.org/packages/4d/d0/94346961acb476569fca9a644cc6f9a02f97ef75961a6b8d2b35279b8d1f/aiohttp-3.11.12-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250", size = 704837 }, + { url = "https://files.pythonhosted.org/packages/a9/af/05c503f1cc8f97621f199ef4b8db65fb88b8bc74a26ab2adb74789507ad3/aiohttp-3.11.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1", size = 464218 }, + { url = "https://files.pythonhosted.org/packages/f2/48/b9949eb645b9bd699153a2ec48751b985e352ab3fed9d98c8115de305508/aiohttp-3.11.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c", size = 456166 }, + { url = "https://files.pythonhosted.org/packages/14/fb/980981807baecb6f54bdd38beb1bd271d9a3a786e19a978871584d026dcf/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df", size = 1682528 }, + { url = "https://files.pythonhosted.org/packages/90/cb/77b1445e0a716914e6197b0698b7a3640590da6c692437920c586764d05b/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259", size = 1737154 }, + { url = "https://files.pythonhosted.org/packages/ff/24/d6fb1f4cede9ccbe98e4def6f3ed1e1efcb658871bbf29f4863ec646bf38/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d", size = 1793435 }, + { url = "https://files.pythonhosted.org/packages/17/e2/9f744cee0861af673dc271a3351f59ebd5415928e20080ab85be25641471/aiohttp-3.11.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e", size = 1692010 }, + { url = "https://files.pythonhosted.org/packages/90/c4/4a1235c1df544223eb57ba553ce03bc706bdd065e53918767f7fa1ff99e0/aiohttp-3.11.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0", size = 1619481 }, + { url = "https://files.pythonhosted.org/packages/60/70/cf12d402a94a33abda86dd136eb749b14c8eb9fec1e16adc310e25b20033/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0", size = 1641578 }, + { url = "https://files.pythonhosted.org/packages/1b/25/7211973fda1f5e833fcfd98ccb7f9ce4fbfc0074e3e70c0157a751d00db8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9", size = 1684463 }, + { url = "https://files.pythonhosted.org/packages/93/60/b5905b4d0693f6018b26afa9f2221fefc0dcbd3773fe2dff1a20fb5727f1/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f", size = 1646691 }, + { url = "https://files.pythonhosted.org/packages/b4/fc/ba1b14d6fdcd38df0b7c04640794b3683e949ea10937c8a58c14d697e93f/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9", size = 1702269 }, + { url = "https://files.pythonhosted.org/packages/5e/39/18c13c6f658b2ba9cc1e0c6fb2d02f98fd653ad2addcdf938193d51a9c53/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef", size = 1734782 }, + { url = "https://files.pythonhosted.org/packages/9f/d2/ccc190023020e342419b265861877cd8ffb75bec37b7ddd8521dd2c6deb8/aiohttp-3.11.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9", size = 1694740 }, + { url = "https://files.pythonhosted.org/packages/3f/54/186805bcada64ea90ea909311ffedcd74369bfc6e880d39d2473314daa36/aiohttp-3.11.12-cp312-cp312-win32.whl", hash = "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a", size = 411530 }, + { url = "https://files.pythonhosted.org/packages/3d/63/5eca549d34d141bcd9de50d4e59b913f3641559460c739d5e215693cb54a/aiohttp-3.11.12-cp312-cp312-win_amd64.whl", hash = "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802", size = 437860 }, ] [[package]] @@ -203,11 +205,11 @@ wheels = [ [[package]] name = "attrs" -version = "24.3.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, ] [[package]] @@ -244,25 +246,24 @@ sdist = { url = "https://files.pythonhosted.org/packages/86/41/00a6b000f222f0fa4 [[package]] name = "bitsandbytes" -version = "0.45.0" +version = "0.45.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "torch" }, - { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/99/9a/f41d252bf8b0bc5969b4dce1274cd04b7ddc541de1060dd27eca680bc1b2/bitsandbytes-0.45.0-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:0f0323de1ff1fdf8383e79bdad1283516a4c05a6fd2b44a363bf4e059422305b", size = 69084267 }, - { url = "https://files.pythonhosted.org/packages/1e/90/a2bbb9b5f997b9c9aa9c15ee4adf553ee71053bb942f89fd48d920a1aa9d/bitsandbytes-0.45.0-py3-none-win_amd64.whl", hash = "sha256:ebbf96e0ecb466716a65ecdeaef3fa1983575447b9ab66b74e5211892507c6ff", size = 68520043 }, + { url = "https://files.pythonhosted.org/packages/db/9d/9382259196d7ad7f3550702390081224e673a705e75b5660ee377b592fc0/bitsandbytes-0.45.2-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:ba3a720187f518b172ebce4081049c682ae3fd8284947e22499b256ff99a2bc3", size = 69680042 }, + { url = "https://files.pythonhosted.org/packages/cb/33/550bcfe84f08ee20f3bcc1b129dcadaf7f2d1a065ce9812476fc7be2874a/bitsandbytes-0.45.2-py3-none-win_amd64.whl", hash = "sha256:e1893170455422924156760bae9e210ae26e8bd2ce7b21065d24b47decbe6963", size = 69124143 }, ] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] @@ -270,7 +271,7 @@ name = "cffi" version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "pycparser" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } wheels = [ @@ -531,35 +532,35 @@ sdist = { url = "https://files.pythonhosted.org/packages/f6/1d/ac8914360460fafa1 [[package]] name = "fonttools" -version = "4.55.4" +version = "4.56.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/8d/8912cdde6a2b4c19ced69ea5790cd17d1c095a3c0104c1c936a1de804a64/fonttools-4.55.4.tar.gz", hash = "sha256:9598af0af85073659facbe9612fcc56b071ef2f26e3819ebf9bd8c5d35f958c5", size = 3498560 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/8c/9ffa2a555af0e5e5d0e2ed7fdd8c9bef474ed676995bb4c57c9cd0014248/fonttools-4.56.0.tar.gz", hash = "sha256:a114d1567e1a1586b7e9e7fc2ff686ca542a82769a296cef131e4c4af51e58f4", size = 3462892 } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/84/da14576ce30bbed3c882bfc4de84d2e4348c65b1382688812357cb21416a/fonttools-4.55.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b332ea7b7f5f3d99f9bc5a28a23c3824ae72711abf7c4e1d62fa21699fdebe7", size = 2774346 }, - { url = "https://files.pythonhosted.org/packages/50/1d/3da7148a5552871c5dbe368de755602a0df0672e339edc133ed3e9704f2a/fonttools-4.55.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8f925909256e62152e7c3e192655dbca3ab8c3cdef7d7b436732727e80feb6", size = 2302782 }, - { url = "https://files.pythonhosted.org/packages/1b/9d/6b5be027fbfc8eab302d89608fc158b37531f3116506062e0d7183546465/fonttools-4.55.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a58af9b98e39bcd773aa352b4512be79b472830b799cb1d3cafb2b4796b71cd", size = 4584269 }, - { url = "https://files.pythonhosted.org/packages/53/6f/c5ccd4c8f90fd7f6964a1b8981e58f5cc6361acedb0a473a8dae4e1ac3c6/fonttools-4.55.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:736d750d2ab4523067d8058e5294b40b01f2eee521e0fd401bec0d5e21e80b12", size = 4626917 }, - { url = "https://files.pythonhosted.org/packages/c7/ea/53c4c75212b30d257e0865d6905eb6747ec7450b414caff742ff031eb758/fonttools-4.55.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1a9a2e7e8a9d3bfa9589db3e6c4e4c127fec252493924b2f87a67a25f9430057", size = 4581220 }, - { url = "https://files.pythonhosted.org/packages/04/4f/05d9bf9595d75ece4d65e52bd994431cff575e11f00a9444ac8b2781091e/fonttools-4.55.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87824368e994af34a95cb4279a8c711e51974b3c28d052d39d768531cc9e8e59", size = 4750636 }, - { url = "https://files.pythonhosted.org/packages/43/21/d91c8d4583e0f6ee8e08868d5ab3de44f78af8da37d47e265f5b433bd0e2/fonttools-4.55.4-cp310-cp310-win32.whl", hash = "sha256:6c36dcbfe64bce38c4d4f1d436cdc6445e969eee96eb98d98be603b5abf8c3f2", size = 2177793 }, - { url = "https://files.pythonhosted.org/packages/b8/33/c26363a57f5e766f38c84fb4e34d26d32a26398804f72e12a00c007166a1/fonttools-4.55.4-cp310-cp310-win_amd64.whl", hash = "sha256:3c53a467e5cf629acdbefc98b0f554859539fb6447bbeae4117b9ab51464ccc5", size = 2222313 }, - { url = "https://files.pythonhosted.org/packages/5c/22/cf0707f681486bf91f998c3a6a6492d806d1cf09445ce01b26a724917439/fonttools-4.55.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1605b28165c785bf26c2cbd205dc0822463e3f9f56f187049eb214dc5f4a59cb", size = 2775483 }, - { url = "https://files.pythonhosted.org/packages/09/79/11a07753a7b9ef46eaaa5e85b72558095713060aeca1393057a081fb21e3/fonttools-4.55.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d851d8b2fdb676507365d1430c3285d62c4039d0d7760d8cf2f2e5ea3aa19d73", size = 2303701 }, - { url = "https://files.pythonhosted.org/packages/93/67/173994471ddb0ff8cd45b0a2ff9fa03416152ca90bd14d1cbe1ff75fb66c/fonttools-4.55.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fb3cf1cddf08cec0338f238f950cb76fabab23a324a579e3e1f9b2ef2578329", size = 4891469 }, - { url = "https://files.pythonhosted.org/packages/16/b9/22e8be0fceaed86187ba35a1035b309e47575c68ee6ace3b66f146300f43/fonttools-4.55.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddd3208b06186ca00fbd329c0d0fed5ba209c99017cc46e2c4ea42233c2fbd00", size = 4920672 }, - { url = "https://files.pythonhosted.org/packages/cc/15/ed0f0a9d303419e7c885b3a71bfe70bb71c8f964e5b1d515056e38551c69/fonttools-4.55.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9bd98819cb585a894dda9dcb337afeb2601abf17da17de7bfbfc1bc2e4a062c7", size = 4899903 }, - { url = "https://files.pythonhosted.org/packages/b5/02/bd0da57dac3f44f37898b058659cf3beedbfd89b7d0f4b10761c9602dc1b/fonttools-4.55.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4877376c10541e8dccf14876c8476d5082338fa5d21103894894382cc245144b", size = 5067979 }, - { url = "https://files.pythonhosted.org/packages/a0/b9/c232b07c0ecaba9e522695780ca8d711b099bf87889a19a6b35a4ebfde90/fonttools-4.55.4-cp311-cp311-win32.whl", hash = "sha256:3a5e466894ec6d8a009b0eb8e02a6eb26959a318d5b7a906280c26bdadce6423", size = 2176681 }, - { url = "https://files.pythonhosted.org/packages/e3/50/2aa1cf2492e6aded4320122aed690268e97076aba1f418c0b4c68fb11a50/fonttools-4.55.4-cp311-cp311-win_amd64.whl", hash = "sha256:f595129e6f9c6402965d6295fe8c18c1945d27af0f90bdb52ff426226e647afc", size = 2223239 }, - { url = "https://files.pythonhosted.org/packages/7a/ee/c7f06da45f60c076677291470599eb9f8aae6605cbfbebbcb8ee12428e26/fonttools-4.55.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b3db72ad2d26a0e9ec694cbfb4485a8da9c095d29f66561cf935dbd19f3efcea", size = 2769913 }, - { url = "https://files.pythonhosted.org/packages/d9/a9/19aa6a9685d0bb285678850bfa22365a8376c590a7aaacc9f03d3a43beaa/fonttools-4.55.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87717808fd5953588c3ffaf512e8cab0e43c09c1da04e42ba87fa4c07d8170c7", size = 2301168 }, - { url = "https://files.pythonhosted.org/packages/00/63/88740f4333008336844aadbc9f7ef85d50e2eed779a5c33e13907a2439eb/fonttools-4.55.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f49dac626ad5bc1a0147b88e6157e3211fd440d00007f0da6c9e5f91dd5cb88e", size = 4806195 }, - { url = "https://files.pythonhosted.org/packages/7b/fa/1d103fe6e9bf174afd1c04772ca4f88e8f577f44d37b7cc8644fe5ff2620/fonttools-4.55.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d0ac8656ada8b604ae5da15d9aa075232f2181b95b51a3a2a55195222df7e7", size = 4877282 }, - { url = "https://files.pythonhosted.org/packages/b8/53/1cdd447f30598950e4bf8a2de8cd1f6573e6cb34b726cf23713a3cd8fb1e/fonttools-4.55.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:013c8b45873fa77a4ff6d25e43fecf1046cb7e8c6b32f1843117f98f3f8eac60", size = 4784688 }, - { url = "https://files.pythonhosted.org/packages/71/21/edfdcd85c1cce918d410909759a8db667f95bf3faed88141b1abfa2cefe1/fonttools-4.55.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94caad375d254a0332926512f06791f5e66c24a913ebecd6178b14f61d27c62f", size = 5012253 }, - { url = "https://files.pythonhosted.org/packages/7d/e7/7c16717b75e40f735e01d899ee152a0573e90be0e6b8fc2d47c16ba8239c/fonttools-4.55.4-cp312-cp312-win32.whl", hash = "sha256:cb3eb4bf3a0c4e431e1ccab7a33ef4f1bb32657133fff4a61dc4fcbd54b94d29", size = 2165283 }, - { url = "https://files.pythonhosted.org/packages/50/ff/85d1c1d396a3ceaabcf7cb543da56d2223d9b76429bafd6c87f4a4e880df/fonttools-4.55.4-cp312-cp312-win_amd64.whl", hash = "sha256:6914269f6ff6b20c6b5a9b19d0b752880bd8ee218d9a7d6afe9960bbf1922d98", size = 2212080 }, - { url = "https://files.pythonhosted.org/packages/f3/5d/29b126e12df844432e188d19e74f47c2578fa5a72a122b4f41819e1e0923/fonttools-4.55.4-py3-none-any.whl", hash = "sha256:d07ad8f31038c6394a0945752458313367a0ef8125d284ee59f99e68393a3c2d", size = 1111964 }, + { url = "https://files.pythonhosted.org/packages/1e/5e/6ac30c2cc6a29454260f13c9c6422fc509b7982c13cd4597041260d8f482/fonttools-4.56.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:331954d002dbf5e704c7f3756028e21db07097c19722569983ba4d74df014000", size = 2752190 }, + { url = "https://files.pythonhosted.org/packages/92/3a/ac382a8396d1b420ee45eeb0f65b614a9ca7abbb23a1b17524054f0f2200/fonttools-4.56.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d1613abd5af2f93c05867b3a3759a56e8bf97eb79b1da76b2bc10892f96ff16", size = 2280624 }, + { url = "https://files.pythonhosted.org/packages/8a/ae/00b58bfe20e9ff7fbc3dda38f5d127913942b5e252288ea9583099a31bf5/fonttools-4.56.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:705837eae384fe21cee5e5746fd4f4b2f06f87544fa60f60740007e0aa600311", size = 4562074 }, + { url = "https://files.pythonhosted.org/packages/46/d0/0004ca8f6a200252e5bd6982ed99b5fe58c4c59efaf5f516621c4cd8f703/fonttools-4.56.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc871904a53a9d4d908673c6faa15689874af1c7c5ac403a8e12d967ebd0c0dc", size = 4604747 }, + { url = "https://files.pythonhosted.org/packages/45/ea/c8862bd3e09d143ef8ed8268ec8a7d477828f960954889e65288ac050b08/fonttools-4.56.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:38b947de71748bab150259ee05a775e8a0635891568e9fdb3cdd7d0e0004e62f", size = 4559025 }, + { url = "https://files.pythonhosted.org/packages/8f/75/bb88a9552ec1de31a414066257bfd9f40f4ada00074f7a3799ea39b5741f/fonttools-4.56.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:86b2a1013ef7a64d2e94606632683f07712045ed86d937c11ef4dde97319c086", size = 4728482 }, + { url = "https://files.pythonhosted.org/packages/2a/5f/80a2b640df1e1bb7d459d62c8b3f37fe83fd413897e549106d4ebe6371f5/fonttools-4.56.0-cp310-cp310-win32.whl", hash = "sha256:133bedb9a5c6376ad43e6518b7e2cd2f866a05b1998f14842631d5feb36b5786", size = 2155557 }, + { url = "https://files.pythonhosted.org/packages/8f/85/0904f9dbe51ac70d878d3242a8583b9453a09105c3ed19c6301247fd0d3a/fonttools-4.56.0-cp310-cp310-win_amd64.whl", hash = "sha256:17f39313b649037f6c800209984a11fc256a6137cbe5487091c6c7187cae4685", size = 2200017 }, + { url = "https://files.pythonhosted.org/packages/35/56/a2f3e777d48fcae7ecd29de4d96352d84e5ea9871e5f3fc88241521572cf/fonttools-4.56.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ef04bc7827adb7532be3d14462390dd71287644516af3f1e67f1e6ff9c6d6df", size = 2753325 }, + { url = "https://files.pythonhosted.org/packages/71/85/d483e9c4e5ed586b183bf037a353e8d766366b54fd15519b30e6178a6a6e/fonttools-4.56.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ffda9b8cd9cb8b301cae2602ec62375b59e2e2108a117746f12215145e3f786c", size = 2281554 }, + { url = "https://files.pythonhosted.org/packages/09/67/060473b832b2fade03c127019794df6dc02d9bc66fa4210b8e0d8a99d1e5/fonttools-4.56.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e993e8db36306cc3f1734edc8ea67906c55f98683d6fd34c3fc5593fdbba4c", size = 4869260 }, + { url = "https://files.pythonhosted.org/packages/28/e9/47c02d5a7027e8ed841ab6a10ca00c93dadd5f16742f1af1fa3f9978adf4/fonttools-4.56.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003548eadd674175510773f73fb2060bb46adb77c94854af3e0cc5bc70260049", size = 4898508 }, + { url = "https://files.pythonhosted.org/packages/bf/8a/221d456d1afb8ca043cfd078f59f187ee5d0a580f4b49351b9ce95121f57/fonttools-4.56.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd9825822e7bb243f285013e653f6741954d8147427aaa0324a862cdbf4cbf62", size = 4877700 }, + { url = "https://files.pythonhosted.org/packages/a4/8c/e503863adf7a6aeff7b960e2f66fa44dd0c29a7a8b79765b2821950d7b05/fonttools-4.56.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b23d30a2c0b992fb1c4f8ac9bfde44b5586d23457759b6cf9a787f1a35179ee0", size = 5045817 }, + { url = "https://files.pythonhosted.org/packages/2b/50/79ba3b7e42f4eaa70b82b9e79155f0f6797858dc8a97862428b6852c6aee/fonttools-4.56.0-cp311-cp311-win32.whl", hash = "sha256:47b5e4680002ae1756d3ae3b6114e20aaee6cc5c69d1e5911f5ffffd3ee46c6b", size = 2154426 }, + { url = "https://files.pythonhosted.org/packages/3b/90/4926e653041c4116ecd43e50e3c79f5daae6dcafc58ceb64bc4f71dd4924/fonttools-4.56.0-cp311-cp311-win_amd64.whl", hash = "sha256:14a3e3e6b211660db54ca1ef7006401e4a694e53ffd4553ab9bc87ead01d0f05", size = 2200937 }, + { url = "https://files.pythonhosted.org/packages/39/32/71cfd6877999576a11824a7fe7bc0bb57c5c72b1f4536fa56a3e39552643/fonttools-4.56.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6f195c14c01bd057bc9b4f70756b510e009c83c5ea67b25ced3e2c38e6ee6e9", size = 2747757 }, + { url = "https://files.pythonhosted.org/packages/15/52/d9f716b072c5061a0b915dd4c387f74bef44c68c069e2195c753905bd9b7/fonttools-4.56.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa760e5fe8b50cbc2d71884a1eff2ed2b95a005f02dda2fa431560db0ddd927f", size = 2279007 }, + { url = "https://files.pythonhosted.org/packages/d1/97/f1b3a8afa9a0d814a092a25cd42f59ccb98a0bb7a295e6e02fc9ba744214/fonttools-4.56.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54a45d30251f1d729e69e5b675f9a08b7da413391a1227781e2a297fa37f6d2", size = 4783991 }, + { url = "https://files.pythonhosted.org/packages/95/70/2a781bedc1c45a0c61d29c56425609b22ed7f971da5d7e5df2679488741b/fonttools-4.56.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661a8995d11e6e4914a44ca7d52d1286e2d9b154f685a4d1f69add8418961563", size = 4855109 }, + { url = "https://files.pythonhosted.org/packages/0c/02/a2597858e61a5e3fb6a14d5f6be9e6eb4eaf090da56ad70cedcbdd201685/fonttools-4.56.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d94449ad0a5f2a8bf5d2f8d71d65088aee48adbe45f3c5f8e00e3ad861ed81a", size = 4762496 }, + { url = "https://files.pythonhosted.org/packages/f2/00/aaf00100d6078fdc73f7352b44589804af9dc12b182a2540b16002152ba4/fonttools-4.56.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f59746f7953f69cc3290ce2f971ab01056e55ddd0fb8b792c31a8acd7fee2d28", size = 4990094 }, + { url = "https://files.pythonhosted.org/packages/bf/dc/3ff1db522460db60cf3adaf1b64e0c72b43406717d139786d3fa1eb20709/fonttools-4.56.0-cp312-cp312-win32.whl", hash = "sha256:bce60f9a977c9d3d51de475af3f3581d9b36952e1f8fc19a1f2254f1dda7ce9c", size = 2142888 }, + { url = "https://files.pythonhosted.org/packages/6f/e3/5a181a85777f7809076e51f7422e0dc77eb04676c40ec8bf6a49d390d1ff/fonttools-4.56.0-cp312-cp312-win_amd64.whl", hash = "sha256:300c310bb725b2bdb4f5fc7e148e190bd69f01925c7ab437b9c0ca3e1c7cd9ba", size = 2189734 }, + { url = "https://files.pythonhosted.org/packages/bf/ff/44934a031ce5a39125415eb405b9efb76fe7f9586b75291d66ae5cbfc4e6/fonttools-4.56.0-py3-none-any.whl", hash = "sha256:1088182f68c303b50ca4dc0c82d42083d176cba37af1937e1a976a31149d4d14", size = 1089800 }, ] [[package]] @@ -618,11 +619,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2024.12.0" +version = "2025.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/11/de70dee31455c546fbc88301971ec03c328f3d1138cfba14263f651e9551/fsspec-2024.12.0.tar.gz", hash = "sha256:670700c977ed2fb51e0d9f9253177ed20cbde4a3e5c0283cc5385b5870c8533f", size = 291600 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/79/68612ed99700e6413de42895aa725463e821a6b3be75c87fcce1b4af4c70/fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd", size = 292283 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/86/5486b0188d08aa643e127774a99bac51ffa6cf343e3deb0583956dca5b22/fsspec-2024.12.0-py3-none-any.whl", hash = "sha256:b520aed47ad9804237ff878b504267a3b0b441e97508bd6d2d8774e3db85cee2", size = 183862 }, + { url = "https://files.pythonhosted.org/packages/e2/94/758680531a00d06e471ef649e4ec2ed6bf185356a7f9fbfbb7368a40bd49/fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b", size = 184484 }, ] [[package]] @@ -694,37 +695,37 @@ wheels = [ [[package]] name = "grpcio" -version = "1.69.0" +version = "1.70.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/87/06a145284cbe86c91ca517fe6b57be5efbb733c0d6374b407f0992054d18/grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5", size = 12738244 } +sdist = { url = "https://files.pythonhosted.org/packages/69/e1/4b21b5017c33f3600dcc32b802bb48fe44a4d36d6c066f52650c7c2690fa/grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56", size = 12788932 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/6e/2f8ee5fb65aef962d0bd7e46b815e7b52820687e29c138eaee207a688abc/grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97", size = 5190753 }, - { url = "https://files.pythonhosted.org/packages/89/07/028dcda44d40f9488f0a0de79c5ffc80e2c1bc5ed89da9483932e3ea67cf/grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278", size = 11096752 }, - { url = "https://files.pythonhosted.org/packages/99/a0/c727041b1410605ba38b585b6b52c1a289d7fcd70a41bccbc2c58fc643b2/grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11", size = 5705442 }, - { url = "https://files.pythonhosted.org/packages/7a/2f/1c53f5d127ff882443b19c757d087da1908f41c58c4b098e8eaf6b2bb70a/grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e", size = 6333796 }, - { url = "https://files.pythonhosted.org/packages/cc/f6/2017da2a1b64e896af710253e5bfbb4188605cdc18bce3930dae5cdbf502/grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec", size = 5954245 }, - { url = "https://files.pythonhosted.org/packages/c1/65/1395bec928e99ba600464fb01b541e7e4cdd462e6db25259d755ef9f8d02/grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e", size = 6664854 }, - { url = "https://files.pythonhosted.org/packages/40/57/8b3389cfeb92056c8b44288c9c4ed1d331bcad0215c4eea9ae4629e156d9/grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51", size = 6226854 }, - { url = "https://files.pythonhosted.org/packages/cc/61/1f2bbeb7c15544dffc98b3f65c093e746019995e6f1e21dc3655eec3dc23/grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc", size = 3662734 }, - { url = "https://files.pythonhosted.org/packages/ef/ba/bf1a6d9f5c17d2da849793d72039776c56c98c889c9527f6721b6ee57e6e/grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5", size = 4410306 }, - { url = "https://files.pythonhosted.org/packages/8d/cd/ca256aeef64047881586331347cd5a68a4574ba1a236e293cd8eba34e355/grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561", size = 5198734 }, - { url = "https://files.pythonhosted.org/packages/37/3f/10c1e5e0150bf59aa08ea6aebf38f87622f95f7f33f98954b43d1b2a3200/grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6", size = 11135285 }, - { url = "https://files.pythonhosted.org/packages/08/61/61cd116a572203a740684fcba3fef37a3524f1cf032b6568e1e639e59db0/grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442", size = 5699468 }, - { url = "https://files.pythonhosted.org/packages/01/f1/a841662e8e2465ba171c973b77d18fa7438ced535519b3c53617b7e6e25c/grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c", size = 6332337 }, - { url = "https://files.pythonhosted.org/packages/62/b1/c30e932e02c2e0bfdb8df46fe3b0c47f518fb04158ebdc0eb96cc97d642f/grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6", size = 5949844 }, - { url = "https://files.pythonhosted.org/packages/5e/cb/55327d43b6286100ffae7d1791be6178d13c917382f3e9f43f82e8b393cf/grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d", size = 6661828 }, - { url = "https://files.pythonhosted.org/packages/6f/e4/120d72ae982d51cb9cabcd9672f8a1c6d62011b493a4d049d2abdf564db0/grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2", size = 6226026 }, - { url = "https://files.pythonhosted.org/packages/96/e8/2cc15f11db506d7b1778f0587fa7bdd781602b05b3c4d75b7ca13de33d62/grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258", size = 3662653 }, - { url = "https://files.pythonhosted.org/packages/42/78/3c5216829a48237fcb71a077f891328a435e980d9757a9ebc49114d88768/grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7", size = 4412824 }, - { url = "https://files.pythonhosted.org/packages/61/1d/8f28f147d7f3f5d6b6082f14e1e0f40d58e50bc2bd30d2377c730c57a286/grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b", size = 5161414 }, - { url = "https://files.pythonhosted.org/packages/35/4b/9ab8ea65e515e1844feced1ef9e7a5d8359c48d986c93f3d2a2006fbdb63/grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4", size = 11108909 }, - { url = "https://files.pythonhosted.org/packages/99/68/1856fde2b3c3162bdfb9845978608deef3606e6907fdc2c87443fce6ecd0/grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e", size = 5658302 }, - { url = "https://files.pythonhosted.org/packages/3e/21/3fa78d38dc5080d0d677103fad3a8cd55091635cc2069a7c06c7a54e6c4d/grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084", size = 6306201 }, - { url = "https://files.pythonhosted.org/packages/f3/cb/5c47b82fd1baf43dba973ae399095d51aaf0085ab0439838b4cbb1e87e3c/grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9", size = 5919649 }, - { url = "https://files.pythonhosted.org/packages/c6/67/59d1a56a0f9508a29ea03e1ce800bdfacc1f32b4f6b15274b2e057bf8758/grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d", size = 6648974 }, - { url = "https://files.pythonhosted.org/packages/f8/fe/ca70c14d98c6400095f19a0f4df8273d09c2106189751b564b26019f1dbe/grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55", size = 6215144 }, - { url = "https://files.pythonhosted.org/packages/b3/94/b2b0a9fd487fc8262e20e6dd0ec90d9fa462c82a43b4855285620f6e9d01/grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1", size = 3644552 }, - { url = "https://files.pythonhosted.org/packages/93/99/81aec9f85412e3255a591ae2ccb799238e074be774e5f741abae08a23418/grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01", size = 4399532 }, + { url = "https://files.pythonhosted.org/packages/10/e9/f72408bac1f7b05b25e4df569b02d6b200c8e7857193aa9f1df7a3744add/grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851", size = 5229736 }, + { url = "https://files.pythonhosted.org/packages/b3/17/e65139ea76dac7bcd8a3f17cbd37e3d1a070c44db3098d0be5e14c5bd6a1/grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf", size = 11432751 }, + { url = "https://files.pythonhosted.org/packages/a0/12/42de6082b4ab14a59d30b2fc7786882fdaa75813a4a4f3d4a8c4acd6ed59/grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5", size = 5711439 }, + { url = "https://files.pythonhosted.org/packages/34/f8/b5a19524d273cbd119274a387bb72d6fbb74578e13927a473bc34369f079/grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f", size = 6330777 }, + { url = "https://files.pythonhosted.org/packages/1a/67/3d6c0ad786238aac7fa93b79246fc452978fbfe9e5f86f70da8e8a2797d0/grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295", size = 5944639 }, + { url = "https://files.pythonhosted.org/packages/76/0d/d9f7cbc41c2743cf18236a29b6a582f41bd65572a7144d92b80bc1e68479/grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f", size = 6643543 }, + { url = "https://files.pythonhosted.org/packages/fc/24/bdd7e606b3400c14330e33a4698fa3a49e38a28c9e0a831441adbd3380d2/grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3", size = 6199897 }, + { url = "https://files.pythonhosted.org/packages/d1/33/8132eb370087960c82d01b89faeb28f3e58f5619ffe19889f57c58a19c18/grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199", size = 3617513 }, + { url = "https://files.pythonhosted.org/packages/99/bc/0fce5cfc0ca969df66f5dca6cf8d2258abb88146bf9ab89d8cf48e970137/grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1", size = 4303342 }, + { url = "https://files.pythonhosted.org/packages/65/c4/1f67d23d6bcadd2fd61fb460e5969c52b3390b4a4e254b5e04a6d1009e5e/grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a", size = 5229017 }, + { url = "https://files.pythonhosted.org/packages/e4/bd/cc36811c582d663a740fb45edf9f99ddbd99a10b6ba38267dc925e1e193a/grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386", size = 11472027 }, + { url = "https://files.pythonhosted.org/packages/7e/32/8538bb2ace5cd72da7126d1c9804bf80b4fe3be70e53e2d55675c24961a8/grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b", size = 5707785 }, + { url = "https://files.pythonhosted.org/packages/ce/5c/a45f85f2a0dfe4a6429dee98717e0e8bd7bd3f604315493c39d9679ca065/grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77", size = 6331599 }, + { url = "https://files.pythonhosted.org/packages/9f/e5/5316b239380b8b2ad30373eb5bb25d9fd36c0375e94a98a0a60ea357d254/grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea", size = 5940834 }, + { url = "https://files.pythonhosted.org/packages/05/33/dbf035bc6d167068b4a9f2929dfe0b03fb763f0f861ecb3bb1709a14cb65/grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839", size = 6641191 }, + { url = "https://files.pythonhosted.org/packages/4c/c4/684d877517e5bfd6232d79107e5a1151b835e9f99051faef51fed3359ec4/grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd", size = 6198744 }, + { url = "https://files.pythonhosted.org/packages/e9/43/92fe5eeaf340650a7020cfb037402c7b9209e7a0f3011ea1626402219034/grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113", size = 3617111 }, + { url = "https://files.pythonhosted.org/packages/55/15/b6cf2c9515c028aff9da6984761a3ab484a472b0dc6435fcd07ced42127d/grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca", size = 4304604 }, + { url = "https://files.pythonhosted.org/packages/4c/a4/ddbda79dd176211b518f0f3795af78b38727a31ad32bc149d6a7b910a731/grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff", size = 5198135 }, + { url = "https://files.pythonhosted.org/packages/30/5c/60eb8a063ea4cb8d7670af8fac3f2033230fc4b75f62669d67c66ac4e4b0/grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40", size = 11447529 }, + { url = "https://files.pythonhosted.org/packages/fb/b9/1bf8ab66729f13b44e8f42c9de56417d3ee6ab2929591cfee78dce749b57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e", size = 5664484 }, + { url = "https://files.pythonhosted.org/packages/d1/06/2f377d6906289bee066d96e9bdb91e5e96d605d173df9bb9856095cccb57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898", size = 6303739 }, + { url = "https://files.pythonhosted.org/packages/ae/50/64c94cfc4db8d9ed07da71427a936b5a2bd2b27c66269b42fbda82c7c7a4/grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597", size = 5910417 }, + { url = "https://files.pythonhosted.org/packages/53/89/8795dfc3db4389c15554eb1765e14cba8b4c88cc80ff828d02f5572965af/grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c", size = 6626797 }, + { url = "https://files.pythonhosted.org/packages/9c/b2/6a97ac91042a2c59d18244c479ee3894e7fb6f8c3a90619bb5a7757fa30c/grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f", size = 6190055 }, + { url = "https://files.pythonhosted.org/packages/86/2b/28db55c8c4d156053a8c6f4683e559cd0a6636f55a860f87afba1ac49a51/grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528", size = 3600214 }, + { url = "https://files.pythonhosted.org/packages/17/c3/a7a225645a965029ed432e5b5e9ed959a574e62100afab553eef58be0e37/grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655", size = 4292538 }, ] [[package]] @@ -1270,7 +1271,7 @@ name = "nvidia-cudnn-cu12" version = "9.1.0.70" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, @@ -1297,9 +1298,9 @@ name = "nvidia-cusolver-cu12" version = "11.4.5.107" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cusparse-cu12" }, + { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd", size = 124161928 }, @@ -1310,7 +1311,7 @@ name = "nvidia-cusparse-cu12" version = "12.1.0.106" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c", size = 195958278 }, @@ -1326,10 +1327,10 @@ wheels = [ [[package]] name = "nvidia-nvjitlink-cu12" -version = "12.6.85" +version = "12.8.61" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/d7/c5383e47c7e9bf1c99d5bd2a8c935af2b6d705ad831a7ec5c97db4d82f4f/nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a", size = 19744971 }, + { url = "https://files.pythonhosted.org/packages/03/f8/9d85593582bd99b8d7c65634d2304780aefade049b2b94d96e44084be90b/nvidia_nvjitlink_cu12-12.8.61-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:45fd79f2ae20bd67e8bc411055939049873bfd8fac70ff13bd4865e0b9bdab17", size = 39243473 }, ] [[package]] @@ -1596,12 +1597,13 @@ wheels = [ [[package]] name = "py-leap" -version = "0.1a34" -source = { git = "https://github.com/guilledk/py-leap.git?rev=v0.1a34#6055ca7063c1eb32644e855c6726c29a1d7ac7e9" } +version = "0.1a35" +source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#9c60e0b63f4583bd958c4fa06c14757e758f4a2c" } dependencies = [ { name = "base58" }, { name = "cryptos" }, { name = "httpx" }, + { name = "msgspec" }, { name = "requests" }, { name = "ripemd-hash" }, ] @@ -1948,7 +1950,7 @@ wheels = [ [[package]] name = "scikit-image" -version = "0.25.0" +version = "0.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "imageio" }, @@ -1960,23 +1962,23 @@ dependencies = [ { name = "scipy" }, { name = "tifffile" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e6/8d/383e5438c807804b66d68ed2c09202d185ea781b6022aa8b9fac3851137f/scikit_image-0.25.0.tar.gz", hash = "sha256:58d94fea11b6b3306b3770417dc1cbca7fa9bcbd6a13945d7910399c88c2018c", size = 22696477 } +sdist = { url = "https://files.pythonhosted.org/packages/83/e5/496a74ccfc1206666b9c7164a16657febdfeb6df0e458cb61286b20102c9/scikit_image-0.25.1.tar.gz", hash = "sha256:d4ab30540d114d37c35fe5c837f89b94aaba2a7643afae8354aa353319e9bbbb", size = 22697578 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/13/c3ae240871592139d80b77a531b39fc644d480f219520cedde5a701deb05/scikit_image-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2e1ab19beedb2adaaf5173b0c508687a4c7d392ffb1c21513887ba2331b517e3", size = 13984587 }, - { url = "https://files.pythonhosted.org/packages/b6/01/eb0a7f29db6d215a95af4a6d56086fb3fb98385efcd840271e3e6b9c7459/scikit_image-0.25.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6a749e8d7ba1216e3bd0da7156ddf6e1d9a4d03aa8bc86880b29aadd954b0b11", size = 13187433 }, - { url = "https://files.pythonhosted.org/packages/9d/49/866c3acc5dce86fffbc0852c1090b4df9b36407680691b1e04a4315f4851/scikit_image-0.25.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a8e6c3d58ab8cad95cd695bd0fe1be8b8708acdf02ebfcb6c0225e267250021", size = 14152916 }, - { url = "https://files.pythonhosted.org/packages/c9/6b/bd86ed3813d5da0c118ea971577532abbaacaed154cc1e10cf7aa83d041b/scikit_image-0.25.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd419e765f33a43eebb3509cdab382938085c9e269c01d8da80dbe519e89ec3f", size = 14767782 }, - { url = "https://files.pythonhosted.org/packages/a0/9e/38a8e351227cedf246ddaa62d0c550396c9a436b992d2bdca019f16a2b23/scikit_image-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea2577b6f68cba3a06ac6f362ab39a62701fefce2138a6bf3e978ecbab71a024", size = 12808323 }, - { url = "https://files.pythonhosted.org/packages/3e/5c/8182c9e7560a46a7c138c855b8b1804ddf5dc4c0a926fbc0f3c4092d2112/scikit_image-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e235726d9b404527445679030209965c5365767b8728584fadd8dbfa29e29de", size = 13998703 }, - { url = "https://files.pythonhosted.org/packages/ed/26/0188429b5a81cb58255b73a9c22bd22853438ab3c066f287db045efb5938/scikit_image-0.25.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:854b88b7d8b862ccd8f22e660c16fd54f9430c87e079c8dfe46c7f0cebbb1de3", size = 13175073 }, - { url = "https://files.pythonhosted.org/packages/24/12/46688700f5c3b54976a56500f8f4294147fbbd252dde357e228671024436/scikit_image-0.25.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70e2d90b5bfffffe0880d25d40ddab9ca5c145912461d6c8f6bd3449f4e527bb", size = 14144390 }, - { url = "https://files.pythonhosted.org/packages/35/e8/67e4bd1c5f6c4cd0f53505ebb9eb15f143d6fed1fb4938b542013fa3ec25/scikit_image-0.25.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4287052dcd8fe63934daa6cbe28b2abe817d75e9b952290fdb4de42254740fc", size = 14783976 }, - { url = "https://files.pythonhosted.org/packages/26/72/0653e3274310972bd053fc9271aa29df2de0d51ad2db2d47b199bf6070d5/scikit_image-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:d1e25ff6a3cdd8be938a5a06b441020aac304fa9f457a808bd359f5260468739", size = 12787254 }, - { url = "https://files.pythonhosted.org/packages/21/6a/a8df6953a85042a8a219c97e1758486b997c9dd319e1c474362229406e57/scikit_image-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7e63f18b10f9b74590d2ca62cbc4147e696a5e72cfcbcd4af52395fd94fcdc6e", size = 13981411 }, - { url = "https://files.pythonhosted.org/packages/dd/4c/e40a77c57a6b90dda710bc64ed761c93e7b3dd1cef3815675a2bc6807755/scikit_image-0.25.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bad4af5edf58775607c153af5bc3f193c2b67261ea9817b62362c746e439d094", size = 13230600 }, - { url = "https://files.pythonhosted.org/packages/63/3f/fac8e1eefbe4a885fa1c9a384db8e11e47c19ab5558b25f370ade3901868/scikit_image-0.25.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44f7681ff99eed2c33d993bc4bfc17b62e6cadbca1081c7fdbb3607ce89b15e6", size = 14173033 }, - { url = "https://files.pythonhosted.org/packages/47/fe/f09efbf54782996a7f1d3db0e33cb9097f3cc6033392fb53459d7254fa7c/scikit_image-0.25.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:758f55d858aa796114a4275051ca4bb41d8b40c53eb78cb60f0b1ed235d4144b", size = 15002211 }, - { url = "https://files.pythonhosted.org/packages/89/30/4f95a7462411def5563c01d56674bd122bd6db55ae1e8c31ad68586e2d27/scikit_image-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f7178c6fb6163710571522847326ad936a603646255b22d3d76b6ba58153890", size = 12894520 }, + { url = "https://files.pythonhosted.org/packages/4c/16/f662cd3bdbe4ca8a20e2ffd47fdb758f164ac01ea48c4e69d2a09d8fae97/scikit_image-0.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40763a3a089617e6f00f92d46b3475368b9783588a165c2aa854da95b66bb4ff", size = 13985311 }, + { url = "https://files.pythonhosted.org/packages/76/ca/2912515df1e08a60d378d3572edf61248012747eeb593869289ecc47174d/scikit_image-0.25.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c6b69f33e5512ee7fc53361b064430f146583f08dc75317667e81d5f8fcd0c6", size = 13188177 }, + { url = "https://files.pythonhosted.org/packages/d0/90/42d55f46fd3d9c7d4495025367bcb10033904f65d512143fa39179fa2de2/scikit_image-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9187347d115776ff0ddba3e5d2a04638d291b1a62e3c315d17b71eea351cde8", size = 14153693 }, + { url = "https://files.pythonhosted.org/packages/04/53/2822fe04ae5fc69ea1eba65b8e30a691b7257f93c6ca5621d3d94747d83e/scikit_image-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdfca713979ad1873a4b55d94bb1eb4bc713f0c10165b261bf6f7e606f44a00c", size = 14768517 }, + { url = "https://files.pythonhosted.org/packages/86/9c/cf681f591bc17c0eed560d674223ef11c1d63561fd54b8c33ab0822e17fa/scikit_image-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:167fb146de80bb2a1493d1a760a9ac81644a8a5de254c3dd12a95d1b662d819c", size = 12809084 }, + { url = "https://files.pythonhosted.org/packages/1c/8a/698138616b782d368d24061339226089f29c42878a9b18046c6a2d9d6422/scikit_image-0.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1bde2d5f1dfb23b3c72ef9fcdb2dd5f42fa353e8bd606aea63590eba5e79565", size = 13999468 }, + { url = "https://files.pythonhosted.org/packages/64/dd/ff4d4123547a59bc156a192c8cd52ea9cfcf178b70d1f48afec9d26ab6f4/scikit_image-0.25.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5112d95cccaa45c434e57efc20c1f721ab439e516e2ed49709ddc2afb7c15c70", size = 13175810 }, + { url = "https://files.pythonhosted.org/packages/1e/28/4d76f333cd0c86ccf34ab74517877117914413d307f936eb8df74ca365aa/scikit_image-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f5e313b028f5d7a9f3888ad825ddf4fb78913d7762891abb267b99244b4dd31", size = 14145156 }, + { url = "https://files.pythonhosted.org/packages/27/05/265b62ace7626de13edb7e97f0429a4faae2a95bbc2adb15a28fd5680aba/scikit_image-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39ad76aeff754048dabaff83db752aa0655dee425f006678d14485471bdb459d", size = 14784715 }, + { url = "https://files.pythonhosted.org/packages/35/80/faf325a7aef1d07067dab5ff7a890da229b42a641d2e85c98f3675cd36a2/scikit_image-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:8dc8b06176c1a2316fa8bc539fd7e96155721628ae5cf51bc1a2c62cb9786581", size = 12788033 }, + { url = "https://files.pythonhosted.org/packages/c5/a8/7d56f4401c05a186a5e82aab53977029a3f88cc0f1bd6c1fb4f4dd524262/scikit_image-0.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ebf83699d60134909647395a0bf07db3859646de7192b088e656deda6bc15e95", size = 13982151 }, + { url = "https://files.pythonhosted.org/packages/80/0e/d78876faaf552cf575205160aa82849fc493977a5b0cdf093f6bbb1586fe/scikit_image-0.25.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:408086520eed036340e634ab7e4f648e00238f711bac61ab933efeb11464a238", size = 13231342 }, + { url = "https://files.pythonhosted.org/packages/e0/ae/78a8dba652cdaed8a5f5dd56cf8f11ed64e44151a4813e3312916a7dff46/scikit_image-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bd709faa87795869ccd21f32490c37989ca5846571495822f4b9430fb42c34c", size = 14173769 }, + { url = "https://files.pythonhosted.org/packages/d7/77/6d1da74cb0b7ba07750d6ef7e48f87807b53df1cf4a090775115dd9cc5ea/scikit_image-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b15c0265c072a46ff4720784d756d8f8e5d63567639aa8451f6673994d6846", size = 15002945 }, + { url = "https://files.pythonhosted.org/packages/df/ad/cddec5c0bcde8936c15f07593419f6d94ed33b058737948a0d59fb1142a0/scikit_image-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:a689a0d091e0bd97d7767309abdeb27c43be210d075abb34e71657add920c22b", size = 12895262 }, ] [[package]] @@ -2124,7 +2126,7 @@ requires-dist = [ { name = "outcome", specifier = ">=1.3.0.post0" }, { name = "pillow", specifier = ">=10.0.1,<11" }, { name = "protobuf", specifier = ">=5.29.3,<6" }, - { name = "py-leap", git = "https://github.com/guilledk/py-leap.git?rev=v0.1a34" }, + { name = "py-leap", git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap" }, { name = "pytz", specifier = "~=2023.3.post1" }, { name = "toml", specifier = ">=0.10.2,<0.11" }, { name = "trio", specifier = ">=0.22.2,<0.23" }, @@ -2193,7 +2195,7 @@ wheels = [ [[package]] name = "tb-nightly" -version = "2.19.0a20250121" +version = "2.19.0a20250211" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, @@ -2208,7 +2210,7 @@ dependencies = [ { name = "werkzeug" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/02/45/ddcb96d850d1acf5e0ae48693e6a8b138ee2aef95673afc755d938439ec5/tb_nightly-2.19.0a20250121-py3-none-any.whl", hash = "sha256:85d9dea445235d27a0314575a0600bf1e0c55c1c87f96ab19ae0c665791726d4", size = 5503304 }, + { url = "https://files.pythonhosted.org/packages/5a/90/f0c8effd911d95e4eba1c10836fd446a9cac1f68e62b858b2177aaae963d/tb_nightly-2.19.0a20250211-py3-none-any.whl", hash = "sha256:d2ba2da592308980a4380da77777c0634e618ef2541155717a903986dc0d7adf", size = 5503304 }, ] [[package]] @@ -2434,7 +2436,7 @@ name = "triton" version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "filelock" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/98/29/69aa56dc0b2eb2602b553881e34243475ea2afd9699be042316842788ff5/triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8", size = 209460013 }, From 9eb46862aec7ef2e6896e80d767203c1ab7180fe Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 16:20:26 -0300 Subject: [PATCH 2/9] Add CI, finish structification, break smart contract table sync logic to its own class with context manager, add better test helpers --- .github/workflows/ci.yml | 31 +++++ skynet/_testing.py | 25 +++- skynet/dgpu/__init__.py | 22 ++-- skynet/dgpu/compute.py | 2 +- skynet/dgpu/daemon.py | 60 ++++----- skynet/dgpu/network.py | 256 +++++++++++++++++++++++++++------------ skynet/dgpu/tui.py | 9 +- skynet/dgpu/utils.py | 1 - skynet/types.py | 183 ++++++++++++++++++++++++++++ tests/conftest.py | 23 +++- tests/test_chain.py | 27 ++--- 11 files changed, 482 insertions(+), 157 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7b031f5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: [push] + +jobs: + auto-tests: + name: Pytest Tests + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v5 + + - uses: actions/cache@v3 + name: Cache venv + with: + path: ./.venv + key: venv-${{ hashFiles('uv.lock') }} + + - name: Install with dev + run: uv sync + + - name: Run tests + run: | + uv run \ + pytest \ + tests/test_chain.py diff --git a/skynet/_testing.py b/skynet/_testing.py index c6aa435..44eefba 100644 --- a/skynet/_testing.py +++ b/skynet/_testing.py @@ -1,5 +1,7 @@ -from skynet.config import Config, DgpuConfig, set_config_override +from contextlib import asynccontextmanager as acm +from skynet.config import Config, DgpuConfig, set_config_override +from skynet.dgpu import open_worker def override_dgpu_config(**kwargs) -> DgpuConfig: config = Config( @@ -7,3 +9,24 @@ def override_dgpu_config(**kwargs) -> DgpuConfig: ) set_config_override(config) return config.dgpu + + +@acm +async def open_test_worker( + cleos, ipfs_node, + account: str = 'testworker', + permission: str = 'active', + hf_token: str = '', + **kwargs +): + config = override_dgpu_config( + account=account, + permission=permission, + key=cleos.private_keys[account], + node_url=cleos.endpoint, + ipfs_url=ipfs_node[1].endpoint, + hf_token=hf_token, + **kwargs + ) + async with open_worker(config) as worker: + yield worker diff --git a/skynet/dgpu/__init__.py b/skynet/dgpu/__init__.py index 2d834f8..a22655d 100755 --- a/skynet/dgpu/__init__.py +++ b/skynet/dgpu/__init__.py @@ -7,7 +7,7 @@ import urwid from skynet.config import Config from skynet.dgpu.tui import init_tui from skynet.dgpu.daemon import dgpu_serve_forever -from skynet.dgpu.network import NetConnector +from skynet.dgpu.network import NetConnector, maybe_open_contract_state_mngr @acm @@ -20,21 +20,23 @@ async def open_worker(config: Config): tui = init_tui(config) conn = NetConnector(config) - try: - n: trio.Nursery - async with trio.open_nursery() as n: - if tui: - n.start_soon(tui.run) + async with maybe_open_contract_state_mngr(conn) as state_mngr: + n: trio.Nursery + async with trio.open_nursery() as n: + if tui: + n.start_soon(tui.run) - n.start_soon(conn.iter_poll_update, config.poll_time) + n.start_soon(dgpu_serve_forever, config, conn, state_mngr) - yield conn + yield conn, state_mngr + + n.cancel_scope.cancel() except *urwid.ExitMainLoop: ... async def _dgpu_main(config: Config): - async with open_worker(config) as conn: - await dgpu_serve_forever(config, conn) + async with open_worker(config): + await trio.sleep_forever() diff --git a/skynet/dgpu/compute.py b/skynet/dgpu/compute.py index 96f7635..2856a60 100755 --- a/skynet/dgpu/compute.py +++ b/skynet/dgpu/compute.py @@ -13,7 +13,7 @@ import trio import torch from skynet.config import load_skynet_toml -from skynet.types import ModelMode, BodyV0, BodyV0Params +from skynet.types import ModelMode, BodyV0Params from skynet.dgpu.tui import maybe_update_tui from skynet.dgpu.errors import ( DGPUComputeError, diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index dfac447..4b32b8e 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -1,5 +1,4 @@ import logging -import random from functools import partial from hashlib import sha256 @@ -8,22 +7,16 @@ import msgspec from skynet.config import DgpuConfig as Config from skynet.types import ( - RequestV0, BodyV0 ) from skynet.constants import MODELS from skynet.dgpu.errors import DGPUComputeError from skynet.dgpu.tui import maybe_update_tui, maybe_update_tui_async from skynet.dgpu.compute import maybe_load_model, compute_one -from skynet.dgpu.network import NetConnector - - -def convert_reward_to_int(reward_str): - int_part, decimal_part = ( - reward_str.split('.')[0], - reward_str.split('.')[1].split(' ')[0] - ) - return int(int_part + decimal_part) +from skynet.dgpu.network import ( + NetConnector, + ContractState, +) async def maybe_update_tui_balance(conn: NetConnector): @@ -38,8 +31,14 @@ async def maybe_update_tui_balance(conn: NetConnector): async def maybe_serve_one( config: Config, conn: NetConnector, - req: RequestV0, + state_mngr: ContractState, ): + req = state_mngr.first + + # no requests in queue + if not req: + return + logging.info(f'maybe serve request #{req.id}') # parse request @@ -69,18 +68,13 @@ async def maybe_serve_one( logging.warning('model not blacklisted!, skip...') return - results = [res['request_id'] for res in conn._tables['results']] - # if worker already produced a result for this request - if req.id in results: + if state_mngr.is_request_filled(req.id): logging.info(f'worker already submitted a result for request #{req.id}, skip...') return - statuses = conn._tables['requests'][req.id] - # skip if workers in non_compete already on it - competitors = set((status['worker'] for status in statuses)) - if bool(config.non_compete & competitors): + if state_mngr.should_compete_for_id(req.id): logging.info('worker in configured non_compete list already working on request, skip...') return @@ -146,7 +140,7 @@ async def maybe_serve_one( req.id, mode, body.params, inputs=inputs, - should_cancel=conn.should_cancel_work, + should_cancel=state_mngr.should_cancel_work, ) ) @@ -168,34 +162,28 @@ async def maybe_serve_one( if 'network cancel' not in str(err): logging.exception('Failed to serve model request !?\n') - if req.id in conn._tables['requests']: + if state_mngr.is_request_in_progress(req.id): await conn.cancel_work(req.id, 'reason not provided') -async def dgpu_serve_forever(config: Config, conn: NetConnector): +async def dgpu_serve_forever( + config: Config, + conn: NetConnector, + state_mngr: ContractState +): await maybe_update_tui_balance(conn) last_poll_idx = -1 try: while True: - await conn.wait_data_update() - if conn.poll_index == last_poll_idx: + await state_mngr.wait_data_update() + if state_mngr.poll_index == last_poll_idx: await trio.sleep(config.poll_time) continue - last_poll_idx = conn.poll_index + last_poll_idx = state_mngr.poll_index - queue = conn._tables['queue'] - - random.shuffle(queue) - queue = sorted( - queue, - key=lambda req: convert_reward_to_int(req['reward']), - reverse=True - ) - - if len(queue) > 0: - await maybe_serve_one(config, conn, queue[0]) + await maybe_serve_one(config, conn, state_mngr) except KeyboardInterrupt: ... diff --git a/skynet/dgpu/network.py b/skynet/dgpu/network.py index 72b4812..91c06d8 100755 --- a/skynet/dgpu/network.py +++ b/skynet/dgpu/network.py @@ -1,8 +1,10 @@ import io import json import time +import random import logging from pathlib import Path +from contextlib import asynccontextmanager as acm from functools import partial import trio @@ -10,12 +12,19 @@ import leap import anyio import httpx import outcome +import msgspec from PIL import Image from leap.cleos import CLEOS from leap.protocol import Asset from skynet.dgpu.tui import maybe_update_tui -from skynet.config import DgpuConfig as Config -from skynet.types import RequestV0 +from skynet.config import DgpuConfig as Config, load_skynet_toml +from skynet.types import ( + ConfigV0, + BodyV0, + RequestV0, + WorkerStatusV0, + ResultV0 +) from skynet.constants import GPU_CONTRACT_ABI from skynet.ipfs import ( @@ -64,15 +73,6 @@ class NetConnector: self.ipfs_client = AsyncIPFSHTTP(config.ipfs_url) - # poll_index is used to detect stale data - self.poll_index = 0 - self._tables = { - 'queue': [], - 'requests': {}, - 'results': [] - } - self._data_event = trio.Event() - maybe_update_tui(lambda tui: tui.set_header_text(new_worker_name=self.config.account)) @@ -93,22 +93,23 @@ class NetConnector: logging.info(f'found {len(rows)} requests on queue') return rows - async def get_status_by_request_id(self, request_id: int): + async def get_status_by_request_id(self, request_id: int) -> list[WorkerStatusV0]: logging.info('get_status_by_request_id') rows = await failable( partial( self.cleos.aget_table, - 'gpu.scd', request_id, 'status'), ret_fail=[]) + 'gpu.scd', request_id, 'status', resp_cls=WorkerStatusV0), ret_fail=[]) - logging.info(f'found status for workers: {[r["worker"] for r in rows]}') + logging.info(f'found status for workers: {[r.worker for r in rows]}') return rows - async def get_global_config(self): + async def get_global_config(self) -> ConfigV0: logging.info('get_global_config') rows = await failable( partial( self.cleos.aget_table, - 'gpu.scd', 'gpu.scd', 'config')) + 'gpu.scd', 'gpu.scd', 'config', + resp_cls=ConfigV0)) if rows: cfg = rows[0] @@ -118,7 +119,7 @@ class NetConnector: logging.error('global config not found, is the contract initialized?') return None - async def get_worker_balance(self): + async def get_worker_balance(self) -> str: logging.info('get_worker_balance') rows = await failable( partial( @@ -131,73 +132,13 @@ class NetConnector: )) if rows: - b = rows[0]['balance'] + b = rows[0].balance logging.info(f'balance: {b}') return b else: logging.info('no balance info found') return None - async def get_full_queue_snapshot(self): - ''' - Get a "snapshot" of current contract table state - - ''' - snap = { - 'requests': {}, - 'results': [] - } - - snap['queue'] = await self.get_work_requests_last_hour() - - async def _run_and_save(d, key: str, fn, *args, **kwargs): - d[key] = await fn(*args, **kwargs) - - async with trio.open_nursery() as n: - n.start_soon(_run_and_save, snap, 'results', self.find_results) - for req in snap['queue']: - n.start_soon( - _run_and_save, snap['requests'], req['id'], self.get_status_by_request_id, req['id']) - - - maybe_update_tui(lambda tui: tui.network_update(snap)) - - return snap - - async def wait_data_update(self): - await self._data_event.wait() - - async def iter_poll_update(self, poll_time: float): - ''' - Long running task, polls gpu contract tables latest table rows, - awakes any self._data_event waiters - - ''' - while True: - start_time = time.time() - self._tables = await self.get_full_queue_snapshot() - elapsed = time.time() - start_time - self._data_event.set() - await trio.sleep(max(poll_time - elapsed, 0.1)) - self._data_event = trio.Event() - self.poll_index += 1 - - async def should_cancel_work(self, request_id: int) -> bool: - logging.info('should cancel work?') - if request_id not in self._tables['requests']: - logging.info(f'request #{request_id} no longer in queue, likely its been filled by another worker, cancelling work...') - return True - - competitors = set([ - status['worker'] - for status in self._tables['requests'][request_id] - if status['worker'] != self.config.account - ]) - logging.info(f'competitors: {competitors}') - should_cancel = bool(self.config.non_compete & competitors) - logging.info(f'cancel: {should_cancel}') - return should_cancel - async def begin_work(self, request_id: int): ''' Publish to the bc that the worker is beginning a model-computation @@ -259,7 +200,7 @@ class NetConnector: ) ) - async def find_results(self): + async def find_results(self) -> list[ResultV0]: logging.info('find_results') rows = await failable( partial( @@ -268,7 +209,8 @@ class NetConnector: index_position=4, key_type='name', lower_bound=self.config.account, - upper_bound=self.config.account + upper_bound=self.config.account, + resp_cls=ResultV0 ) ) return rows @@ -342,3 +284,157 @@ class NetConnector: logging.info('decoded as image successfully') return input_data + + + +def convert_reward_to_int(reward_str): + int_part, decimal_part = ( + reward_str.split('.')[0], + reward_str.split('.')[1].split(' ')[0] + ) + return int(int_part + decimal_part) + + +class ContractState: + + def __init__(self, conn: NetConnector): + self._conn = conn + + self._poll_index = 0 + + self._queue: list[RequestV0] = [] + self._status_by_rid: dict[int, list[WorkerStatusV0]] = {} + self._results: list[ResultV0] = [] + + self._new_data = trio.Event() + + @property + def poll_index(self) -> int: + return self._poll_index + + async def _fetch_results(self): + self._results = await self._conn.find_results() + + async def _fetch_statuses_for_id(self, rid: int): + self._status_by_rid[rid] = await self._conn.get_status_by_request_id(rid) + + async def update_state(self): + ''' + Get a "snapshot" of current contract table state + + ''' + # raw queue from chain + _queue = await self._conn.get_work_requests_last_hour() + + # filter out invalids + self._queue = [] + for req in _queue: + try: + msgspec.json.decode(req.body, type=BodyV0) + self._queue.append(req) + + except msgspec.ValidationError: + ... + + random.shuffle(self._queue) + self._queue = sorted( + self._queue, + key=lambda req: convert_reward_to_int(req.reward), + reverse=True + ) + + async with trio.open_nursery() as n: + n.start_soon(self._fetch_results) + for req in self._queue: + n.start_soon( + self._fetch_statuses_for_id, req.id) + + + maybe_update_tui(lambda tui: tui.network_update(self)) + + async def wait_data_update(self): + await self._new_data.wait() + + async def _state_update_task(self, poll_time: float): + ''' + Long running task, polls gpu contract tables latest table rows, + awakes any self._data_event waiters + + ''' + while True: + start_time = time.time() + await self.update_state() + elapsed = time.time() - start_time + self._new_data.set() + await trio.sleep(max(poll_time - elapsed, 0.1)) + self._new_data = trio.Event() + self._poll_index += 1 + + # views into data + + @property + def queue_len(self) -> int: + return len(self._queue) + + @property + def first(self) -> RequestV0 | None: + if len(self._queue) > 0: + return self._queue[0] + + else: + return None + + def competitors_for_id(self, request_id: int) -> set[str]: + return set(( + status.worker + for status in self._status_by_rid[request_id] + if status.worker != self._conn.config.account + )) + + # predicates + + def is_request_filled(self, request_id: int) -> bool: + return request_id in [ + result.request_id for result in self._results + ] + + def is_request_in_progress(self, request_id: int) -> bool: + return request_id in self._status_by_rid + + def should_compete_for_id(self, request_id: int) -> bool: + return bool( + self._conn.config.non_compete & + self.competitors_for_id(request_id) + ) + + async def should_cancel_work(self, request_id: int) -> bool: + logging.info('should cancel work?') + if request_id not in self._status_by_rid: + logging.info(f'request #{request_id} no longer in queue, likely its been filled by another worker, cancelling work...') + return True + + should_cancel = self.should_compete_for_id(request_id) + logging.info(f'cancel: {should_cancel}') + return should_cancel + + + +__state_mngr = None + +@acm +async def maybe_open_contract_state_mngr(conn: NetConnector): + global __state_mngr + + if __state_mngr: + yield __state_mngr + return + + config = load_skynet_toml().dgpu + + mngr = ContractState(conn) + async with trio.open_nursery() as n: + await mngr.update_state() + n.start_soon(mngr._state_update_task, config.poll_time) + __state_mngr = mngr + yield mngr + n.cancel_scope.cancel() diff --git a/skynet/dgpu/tui.py b/skynet/dgpu/tui.py index 5403025..7a796b2 100644 --- a/skynet/dgpu/tui.py +++ b/skynet/dgpu/tui.py @@ -2,7 +2,6 @@ import json import logging import warnings -import trio import urwid from skynet.config import DgpuConfig as Config @@ -157,14 +156,14 @@ class WorkerMonitor: if new_balance is not None: self.balance_widget.set_text(new_balance) - def network_update(self, snapshot: dict): + def network_update(self, state_mngr): queue = [ { **r, - **(json.loads(r['body'])['params']), - 'workers': [s['worker'] for s in snapshot['requests'][r['id']]] + **(json.loads(r.body)['params']), + 'workers': [s.worker for s in state_mngr._status_by_rid[r.id]] } - for r in snapshot['queue'] + for r in state_mngr.queue ] self.update_requests(queue) diff --git a/skynet/dgpu/utils.py b/skynet/dgpu/utils.py index a355be9..6533d26 100755 --- a/skynet/dgpu/utils.py +++ b/skynet/dgpu/utils.py @@ -1,7 +1,6 @@ import io import os import sys -import time import random import logging import importlib diff --git a/skynet/types.py b/skynet/types.py index ef28e67..519f290 100644 --- a/skynet/types.py +++ b/skynet/types.py @@ -17,6 +17,62 @@ class ModelDesc(Struct): attrs: dict # additional mode specific attrs tags: list[ModelMode] +# smart contract types +# https://github.com/guilledk/telos.contracts/blob/gpu_contracts/contracts/telos.gpu/include/telos.gpu/telos.gpu.hpp + +''' +ConfigV0 + +singleton containing global info about system, definition: + +```c++ +struct [[eosio::table]] global_configuration_struct { + name token_contract; + symbol token_symbol; +} global_config_row; + +typedef eosio::singleton<"config"_n, global_configuration_struct> global_config; +``` +''' + +class ConfigV0: + token_contract: str + token_symbol: str + +''' +RequestV0 + +a request placed on the queue, definition: + +scope: get_self() + +```c++ +struct [[eosio::table]] work_request_struct { + uint64_t id; + name user; + asset reward; + uint32_t min_verification; + + uint64_t nonce; + + string body; + string binary_data; + + time_point_sec timestamp; + + uint64_t primary_key() const { return id; } + uint64_t by_time() const { return (uint64_t)timestamp.sec_since_epoch(); } +}; + +typedef eosio::multi_index< + "queue"_n, + work_request_struct, + indexed_by< + "bytime"_n, const_mem_fun + > +> work_queue; +``` +''' class BodyV0Params(Struct): prompt: str @@ -45,3 +101,130 @@ class RequestV0(Struct): body: str binary_data: str timestamp: str + + +''' +AccountV0 + +a user account, users must deposit tokens in order to enqueue requests, definition: + +scope: get_self() + +```c++ +struct [[eosio::table]] account { + name user; + asset balance; + uint64_t nonce; + + uint64_t primary_key()const { return user.value; } +}; +typedef eosio::multi_index<"users"_n, account> users; +``` +''' + +class AccountV0(Struct): + user: str + balance: str + nonce: int + + +''' +WorkerV0 + +a registered worker info, definition: + +scope: get_self() + +```c++ +struct [[eosio::table]] worker { + name account; + time_point_sec joined; + time_point_sec left; + string url; + + uint64_t primary_key()const { return account.value; } +}; +typedef eosio::multi_index<"workers"_n, worker> workers; +``` +''' + +class WorkerV0(Struct): + account: str + joined: str + left: str + url: str + +''' +WorkerStatusV0 + +a worker's status related to a currently in progress fill, definition: + +scope: request id + +```c++ +struct [[eosio::table]] worker_status_struct { + name worker; + string status; + time_point_sec started; + + uint64_t primary_key() const { return worker.value; } +}; +``` +''' + +class WorkerStatusV0(Struct): + worker: str + status: str + started: str + +''' +ResultV0 + +a submited result related to a request, definition: + +scope: get_self() + +```c++ +struct [[eosio::table]] work_result_struct { + uint64_t id; + uint64_t request_id; + name user; + name worker; + checksum256 result_hash; + string ipfs_hash; + time_point_sec submited; + + uint64_t primary_key() const { return id; } + uint64_t by_request_id() const { return request_id; } + checksum256 by_result_hash() const { return result_hash; } + uint64_t by_worker() const { return worker.value; } + uint64_t by_time() const { return (uint64_t)submited.sec_since_epoch(); } +}; + +typedef eosio::multi_index< + "results"_n, + work_result_struct, + indexed_by< + "byreqid"_n, const_mem_fun + >, + indexed_by< + "byresult"_n, const_mem_fun + >, + indexed_by< + "byworker"_n, const_mem_fun + >, + indexed_by< + "bytime"_n, const_mem_fun + > +> work_results; +``` +''' + +class ResultV0(Struct): + id: int + request_id: int + user: str + worker: str + result_hash: str + ipfs_hash: str + submited: str diff --git a/tests/conftest.py b/tests/conftest.py index 1bf3c72..616dab3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,9 +19,9 @@ def postgres_db(): def skynet_cleos(cleos_bs): cleos = cleos_bs - priv, pub = cleos.create_key_pair() - cleos.import_key('gpu.scd', priv) - cleos.new_account('gpu.scd', ram=4200000, key=pub) + # priv, pub = cleos.create_key_pair() + # cleos.import_key('gpu.scd', priv) + cleos.new_account('gpu.scd', ram=4200000) cleos.deploy_contract_from_path( 'gpu.scd', @@ -36,6 +36,8 @@ def skynet_cleos(cleos_bs): 'gpu.scd' ) + cleos.new_account('testworker') + yield cleos @@ -52,3 +54,18 @@ def inject_mockers(): ) yield + + +@pytest.fixture(scope='session') +def ipfs_node(dockerctl): + rpc_port = 15001 + with dockerctl.run( + 'ipfs/go-ipfs:latest', + name='skynet-ipfs', + ports={ + '8080/tcp': 18080, + '4001/tcp': 14001, + '5001/tcp': ('127.0.0.1', rpc_port) + } + ) as cntr: + yield cntr, AsyncIPFSHTTP(f'http://127.0.0.1:{rpc_port}') diff --git a/tests/test_chain.py b/tests/test_chain.py index 68c3cc1..124ee8f 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -1,12 +1,12 @@ +import trio from msgspec import json from skynet.types import BodyV0, BodyV0Params -from skynet.dgpu.network import NetConnector -from skynet._testing import override_dgpu_config +from skynet._testing import open_test_worker -async def test_enqueue(skynet_cleos): +async def test_full_flow(inject_mockers, skynet_cleos, ipfs_node): cleos = skynet_cleos # create account and deposit tokens into gpu @@ -46,20 +46,7 @@ async def test_enqueue(skynet_cleos): key=cleos.private_keys[account] ) - config = override_dgpu_config( - account='testworker1', - permission='active', - key='', - node_url=cleos.endpoint, - ipfs_url='http://127.0.0.1:5001', - hf_token='' - ) - net = NetConnector(config) - queue = await net.get_work_requests_last_hour() - - assert len(queue) == 1 - - req = queue[0] - body = json.decode(req.body, type=BodyV0) - - assert og_body == body + # open worker and fill request + async with open_test_worker(cleos, ipfs_node) as (_conn, state_mngr): + while state_mngr.queue_len > 0: + await trio.sleep(1) From f4804b61d926ee5701ecaf196ec1f8da15fbbdec Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 16:25:40 -0300 Subject: [PATCH 3/9] Increase ipfs startup timeout & update lock (new dockerctl v) --- tests/conftest.py | 3 ++- uv.lock | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 616dab3..1540f9d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,6 +66,7 @@ def ipfs_node(dockerctl): '8080/tcp': 18080, '4001/tcp': 14001, '5001/tcp': ('127.0.0.1', rpc_port) - } + }, + timeout=60 ) as cntr: yield cntr, AsyncIPFSHTTP(f'http://127.0.0.1:{rpc_port}') diff --git a/uv.lock b/uv.lock index 2f32770..57355d4 100644 --- a/uv.lock +++ b/uv.lock @@ -1598,7 +1598,7 @@ wheels = [ [[package]] name = "py-leap" version = "0.1a35" -source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#9c60e0b63f4583bd958c4fa06c14757e758f4a2c" } +source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#18b3c73e724922a060db5f8ea2b9d9727b6152cc" } dependencies = [ { name = "base58" }, { name = "cryptos" }, @@ -1703,7 +1703,7 @@ wheels = [ [[package]] name = "pytest-dockerctl" version = "0.2a0" -source = { git = "https://github.com/pikers/pytest-dockerctl.git?branch=g_update#d58e9317b55954f05f139730a62d55e1acb5f5d1" } +source = { git = "https://github.com/pikers/pytest-dockerctl.git?branch=g_update#9b0f3296cd5e4a5e8a7ef280b1aafafc6cc22880" } dependencies = [ { name = "docker" }, { name = "pytest" }, From f60e582ad50e0da7db924e30c39a022ee29e4f9a Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 17:07:55 -0300 Subject: [PATCH 4/9] Enable and fix all tests, make upscaler just use pipeline_for function, add upscaler mocker --- .github/workflows/ci.yml | 4 +- skynet/dgpu/compute.py | 30 +++++--------- skynet/dgpu/pipes/__init__.py | 0 skynet/dgpu/pipes/tester.py | 2 +- skynet/dgpu/pipes/tester_upscale.py | 34 ++++++++++++++++ skynet/dgpu/utils.py | 3 ++ tests/conftest.py | 29 ++++++++++++-- tests/test_chain.py | 2 +- tests/test_ipfs_client.py | 14 ++++--- tests/test_reqs.py | 61 ++++++++++++++--------------- 10 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 skynet/dgpu/pipes/__init__.py create mode 100644 skynet/dgpu/pipes/tester_upscale.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b031f5..607a122 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,4 @@ jobs: - name: Run tests run: | - uv run \ - pytest \ - tests/test_chain.py + uv run pytest diff --git a/skynet/dgpu/compute.py b/skynet/dgpu/compute.py index 2856a60..f5892f3 100755 --- a/skynet/dgpu/compute.py +++ b/skynet/dgpu/compute.py @@ -32,25 +32,25 @@ def prepare_params_for_diffuse( match mode: case ModelMode.INPAINT: image = crop_image( - inputs[0], params['width'], params['height']) + inputs[0], params.width, params.height) mask = crop_image( - inputs[1], params['width'], params['height']) + inputs[1], params.width, params.height) _params['image'] = image _params['mask_image'] = mask - if 'flux' in params['model'].lower(): + if 'flux' in params.model.lower(): _params['max_sequence_length'] = 512 else: - _params['strength'] = float(params['strength']) + _params['strength'] = params.strength case ModelMode.IMG2IMG: image = crop_image( - inputs[0], params['width'], params['height']) + inputs[0], params.width, params.height) _params['image'] = image - _params['strength'] = float(params['strength']) + _params['strength'] = params.strength case ModelMode.TXT2IMG | ModelMode.DIFFUSE: ... @@ -63,7 +63,6 @@ def prepare_params_for_diffuse( params.guidance, params.step, torch.manual_seed(int(params.seed)), - params.upscaler, _params ) @@ -88,12 +87,8 @@ def maybe_load_model(name: str, mode: ModelMode): _model_name = _model_mode = '' # load model - if mode == ModelMode.UPSCALE: - _model = init_upscaler() - - else: - _model = pipeline_for( - name, mode, cache_dir=config.hf_home) + _model = pipeline_for( + name, mode, cache_dir=config.hf_home) _model_name = name _model_mode = mode @@ -154,7 +149,7 @@ def compute_one( ): arguments = prepare_params_for_diffuse( params, method, inputs) - prompt, guidance, step, seed, upscaler, extra_params = arguments + prompt, guidance, step, seed, extra_params = arguments if 'flux' in name.lower(): extra_params['callback_on_step_end'] = inference_step_wakeup @@ -174,13 +169,6 @@ def compute_one( output_binary = b'' match output_type: case 'png': - if upscaler == 'x4': - input_img = output.convert('RGB') - up_img, _ = init_upscaler().enhance( - convert_from_image_to_cv2(input_img), outscale=4) - - output = convert_from_cv2_to_image(up_img) - output_binary = convert_from_img_to_bytes(output) case _: diff --git a/skynet/dgpu/pipes/__init__.py b/skynet/dgpu/pipes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/skynet/dgpu/pipes/tester.py b/skynet/dgpu/pipes/tester.py index 7978f22..58c6181 100644 --- a/skynet/dgpu/pipes/tester.py +++ b/skynet/dgpu/pipes/tester.py @@ -6,7 +6,7 @@ import msgspec __model = { - 'name': 'skygpu/txt2img-mocker' + 'name': 'skygpu/mocker' } class MockPipelineResult(msgspec.Struct): diff --git a/skynet/dgpu/pipes/tester_upscale.py b/skynet/dgpu/pipes/tester_upscale.py new file mode 100644 index 0000000..934802a --- /dev/null +++ b/skynet/dgpu/pipes/tester_upscale.py @@ -0,0 +1,34 @@ +from PIL import Image + +import msgspec + +from skynet.dgpu.utils import convert_from_image_to_cv2 + + +__model = { + 'name': 'skygpu/mocker-upscale' +} + +class MockPipelineResult(msgspec.Struct): + images: list[Image] + +class MockUpscalePipeline: + + def enhance( + self, + img, + outscale: int + ): + img = Image.new('RGB', (outscale, outscale), color='green') + output = convert_from_image_to_cv2(img) + + return (output, None) + + +def pipeline_for( + model: str, + mode: str, + mem_fraction: float = 1.0, + cache_dir: str | None = None +): + return MockUpscalePipeline() diff --git a/skynet/dgpu/utils.py b/skynet/dgpu/utils.py index 6533d26..6a17c93 100755 --- a/skynet/dgpu/utils.py +++ b/skynet/dgpu/utils.py @@ -124,6 +124,9 @@ def pipeline_for( except ImportError: logging.info(f'didn\'t find a custom pipeline file for {shortname}') + # for now, upscaler special case... + if mode == 'upscale': + return init_upscaler() req_mem = model_info.mem diff --git a/tests/conftest.py b/tests/conftest.py index 1540f9d..7cb2fc0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import pytest from skynet.ipfs import AsyncIPFSHTTP +from skynet._testing import override_dgpu_config @pytest.fixture(scope='session') @@ -44,13 +45,35 @@ def skynet_cleos(cleos_bs): @pytest.fixture def inject_mockers(): from skynet.constants import MODELS - from skynet.types import ModelDesc + from skynet.types import ModelDesc, ModelMode - MODELS['skygpu/txt2img-mocker'] = ModelDesc( + MODELS['skygpu/mocker'] = ModelDesc( short='tester', mem=0.01, attrs={}, - tags=['txt2img'] + tags=[ + ModelMode.TXT2IMG, + ModelMode.IMG2IMG, + ModelMode.INPAINT + ] + ) + + MODELS['skygpu/mocker-upscale'] = ModelDesc( + short='tester-upscale', + mem=0.01, + attrs={}, + tags=[ + ModelMode.UPSCALE + ] + ) + + override_dgpu_config( + account='testworker1', + permission='active', + key='', + node_url='', + ipfs_url='http://127.0.0.1:5001', + hf_token='' ) yield diff --git a/tests/test_chain.py b/tests/test_chain.py index 124ee8f..97adfe6 100644 --- a/tests/test_chain.py +++ b/tests/test_chain.py @@ -19,7 +19,7 @@ async def test_full_flow(inject_mockers, skynet_cleos, ipfs_node): method='txt2img', params=BodyV0Params( prompt='cyberpunk hacker travis bickle dystopic alley graffiti', - model='skygpu/txt2img-mocker', + model='skygpu/mocker', step=4, seed=0, guidance=10.0 diff --git a/tests/test_ipfs_client.py b/tests/test_ipfs_client.py index a400cdf..c857392 100644 --- a/tests/test_ipfs_client.py +++ b/tests/test_ipfs_client.py @@ -1,14 +1,16 @@ from pathlib import Path -async def test_connection(ipfs_client): - await ipfs_client.connect( - '/ip4/169.197.140.154/tcp/4001/p2p/12D3KooWKWogLFNEcNNMKnzU7Snrnuj84RZdMBg3sLiQSQc51oEv') - peers = await ipfs_client.peers() - assert '12D3KooWKWogLFNEcNNMKnzU7Snrnuj84RZdMBg3sLiQSQc51oEv' in [p['Peer'] for p in peers] +# async def test_connection(ipfs_node): +# _, ipfs_client = ipfs_node +# await ipfs_client.connect( +# '/ip4/169.197.140.154/tcp/4001/p2p/12D3KooWKWogLFNEcNNMKnzU7Snrnuj84RZdMBg3sLiQSQc51oEv') +# peers = await ipfs_client.peers() +# assert '12D3KooWKWogLFNEcNNMKnzU7Snrnuj84RZdMBg3sLiQSQc51oEv' in [p['Peer'] for p in peers] -async def test_add_and_pin_file(ipfs_client): +async def test_add_and_pin_file(ipfs_node): + _, ipfs_client = ipfs_node test_file = Path('hello_world.txt') with open(test_file, 'w+') as file: file.write('Hello Skynet!') diff --git a/tests/test_reqs.py b/tests/test_reqs.py index 274c691..a5b9e30 100644 --- a/tests/test_reqs.py +++ b/tests/test_reqs.py @@ -1,24 +1,23 @@ import pytest +from PIL import Image from skynet.types import ModelMode, BodyV0Params from skynet.dgpu.compute import maybe_load_model, compute_one -from skynet._testing import override_dgpu_config - -@pytest.mark.parametrize("mode", [ - (ModelMode.DIFFUSE), (ModelMode.TXT2IMG) +@pytest.mark.parametrize('mode,model', [ + (ModelMode.DIFFUSE, 'skygpu/mocker'), + (ModelMode.TXT2IMG, 'skygpu/mocker'), + (ModelMode.IMG2IMG, 'skygpu/mocker'), + (ModelMode.INPAINT, 'skygpu/mocker'), + (ModelMode.UPSCALE, 'skygpu/mocker-upscale'), ]) -async def test_pipeline_mocker(inject_mockers, mode): - override_dgpu_config( - account='testworker1', - permission='active', - key='', - node_url='', - ipfs_url='http://127.0.0.1:5001', - hf_token='' - ) - model = 'skygpu/txt2img-mocker' +async def test_pipeline_mocker(inject_mockers, mode, model): + # always insert at least two inputs to make all modes pass + inputs = [ + Image.new('RGB', (1, 1), color='green') + for i in range(2) + ] params = BodyV0Params( prompt="Kronos God Realistic 4k", model=model, @@ -30,21 +29,21 @@ async def test_pipeline_mocker(inject_mockers, mode): ) with maybe_load_model(model, mode) as model: - compute_one(model, 0, mode, params) + compute_one(model, 0, mode, params, inputs) - -async def test_pipeline(): - model = 'stabilityai/stable-diffusion-xl-base-1.0' - mode = 'txt2img' - params = BodyV0Params( - prompt="Kronos God Realistic 4k", - model=model, - step=21, - width=1024, - height=1024, - seed=168402949, - guidance="7.5" - ) - - with maybe_load_model(model, mode) as model: - compute_one(model, 0, mode, params) +# disable for now (cuda) +# async def test_pipeline(): +# model = 'stabilityai/stable-diffusion-xl-base-1.0' +# mode = 'txt2img' +# params = BodyV0Params( +# prompt="Kronos God Realistic 4k", +# model=model, +# step=21, +# width=1024, +# height=1024, +# seed=168402949, +# guidance="7.5" +# ) +# +# with maybe_load_model(model, mode) as model: +# compute_one(model, 0, mode, params) From 63c849a41e4dde8e9d4690e3c348c60f29827318 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 17:35:38 -0300 Subject: [PATCH 5/9] Fixes to tui and compatibility with frontend formated requests found while running worker --- skynet/dgpu/compute.py | 6 +++--- skynet/dgpu/daemon.py | 3 +-- skynet/dgpu/network.py | 5 ++++- skynet/dgpu/tui.py | 11 +++++++++-- skynet/types.py | 4 ++-- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/skynet/dgpu/compute.py b/skynet/dgpu/compute.py index f5892f3..c309d4b 100755 --- a/skynet/dgpu/compute.py +++ b/skynet/dgpu/compute.py @@ -43,14 +43,14 @@ def prepare_params_for_diffuse( if 'flux' in params.model.lower(): _params['max_sequence_length'] = 512 else: - _params['strength'] = params.strength + _params['strength'] = float(params.strength) case ModelMode.IMG2IMG: image = crop_image( inputs[0], params.width, params.height) _params['image'] = image - _params['strength'] = params.strength + _params['strength'] = float(params.strength) case ModelMode.TXT2IMG | ModelMode.DIFFUSE: ... @@ -60,7 +60,7 @@ def prepare_params_for_diffuse( return ( params.prompt, - params.guidance, + float(params.guidance), params.step, torch.manual_seed(int(params.seed)), _params diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index 4b32b8e..dd7022b 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -33,14 +33,13 @@ async def maybe_serve_one( conn: NetConnector, state_mngr: ContractState, ): + logging.info(f'maybe serve request pi: {state_mngr.poll_index}') req = state_mngr.first # no requests in queue if not req: return - logging.info(f'maybe serve request #{req.id}') - # parse request body = msgspec.json.decode(req.body, type=BodyV0) model = body.params.model diff --git a/skynet/dgpu/network.py b/skynet/dgpu/network.py index 91c06d8..e5f1070 100755 --- a/skynet/dgpu/network.py +++ b/skynet/dgpu/network.py @@ -20,6 +20,7 @@ from skynet.dgpu.tui import maybe_update_tui from skynet.config import DgpuConfig as Config, load_skynet_toml from skynet.types import ( ConfigV0, + AccountV0, BodyV0, RequestV0, WorkerStatusV0, @@ -128,7 +129,8 @@ class NetConnector: index_position=1, key_type='name', lower_bound=self.config.account, - upper_bound=self.config.account + upper_bound=self.config.account, + resp_cls=AccountV0 )) if rows: @@ -334,6 +336,7 @@ class ContractState: self._queue.append(req) except msgspec.ValidationError: + logging.exception(f'dropping req {req.id} due to:') ... random.shuffle(self._queue) diff --git a/skynet/dgpu/tui.py b/skynet/dgpu/tui.py index 7a796b2..e1c572a 100644 --- a/skynet/dgpu/tui.py +++ b/skynet/dgpu/tui.py @@ -78,6 +78,11 @@ class WorkerMonitor: """ row_widgets = [] + requests = sorted( + requests, + key=lambda r: r['id'] + ) + for req in requests: # Build a columns widget for the request row prompt = req['prompt'] if 'prompt' in req else 'UPSCALE' @@ -159,11 +164,13 @@ class WorkerMonitor: def network_update(self, state_mngr): queue = [ { - **r, + 'id': r.id, + 'user': r.user, + 'reward': r.reward, **(json.loads(r.body)['params']), 'workers': [s.worker for s in state_mngr._status_by_rid[r.id]] } - for r in state_mngr.queue + for r in state_mngr._queue ] self.update_requests(queue) diff --git a/skynet/types.py b/skynet/types.py index 519f290..45450aa 100644 --- a/skynet/types.py +++ b/skynet/types.py @@ -79,10 +79,10 @@ class BodyV0Params(Struct): model: str seed: int step: int = 1 - guidance: float | None = None + guidance: str | float | None = None width: int | None = None height: int | None = None - strength: float | None = None + strength: str | float | None = None output_type: str | None = 'png' upscaler: str | None = None From 1dd2a8ed89bc6c5bfc15b240800e08d8002b50aa Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 11 Feb 2025 18:24:55 -0300 Subject: [PATCH 6/9] Add strength param for reqs that require it --- tests/test_reqs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_reqs.py b/tests/test_reqs.py index a5b9e30..6db19a2 100644 --- a/tests/test_reqs.py +++ b/tests/test_reqs.py @@ -25,7 +25,8 @@ async def test_pipeline_mocker(inject_mockers, mode, model): width=1024, height=1024, seed=168402949, - guidance="7.5" + guidance="7.5", + strength="0.65" ) with maybe_load_model(model, mode) as model: From 6a991561de88f04dadc7f9585975accc51ed48d5 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 18 Feb 2025 15:47:05 -0300 Subject: [PATCH 7/9] Mostly minor typing and comment changes remaining from fomos re-review, only big change is removed BaseException catch inside compute_one --- skynet/cli.py | 14 ++--- skynet/dgpu/__init__.py | 4 +- skynet/dgpu/compute.py | 117 ++++++++++++++++++++++------------------ skynet/dgpu/daemon.py | 5 ++ skynet/dgpu/tui.py | 4 -- skynet/dgpu/utils.py | 5 +- skynet/types.py | 2 +- 7 files changed, 80 insertions(+), 71 deletions(-) diff --git a/skynet/cli.py b/skynet/cli.py index 4a5850e..58297ca 100755 --- a/skynet/cli.py +++ b/skynet/cli.py @@ -1,16 +1,8 @@ import json import logging -import random - -from functools import partial import click -from leap.protocol import ( - Name, - Asset, -) - from .config import ( load_skynet_toml, set_hf_vars, @@ -49,7 +41,7 @@ def txt2img(*args, **kwargs): config = load_skynet_toml() set_hf_vars(config.dgpu.hf_token, config.dgpu.hf_home) - utils.txt2img(hf_token, **kwargs) + utils.txt2img(config.dgpu.hf_token, **kwargs) @click.command() @@ -74,7 +66,7 @@ def img2img(model, prompt, input, output, strength, guidance, steps, seed): config = load_skynet_toml() set_hf_vars(config.dgpu.hf_token, config.dgpu.hf_home) utils.img2img( - hf_token, + config.dgpu.hf_token, model=model, prompt=prompt, img_path=input, @@ -102,7 +94,7 @@ def inpaint(model, prompt, input, mask, output, strength, guidance, steps, seed) config = load_skynet_toml() set_hf_vars(config.dgpu.hf_token, config.dgpu.hf_home) utils.inpaint( - hf_token, + config.dgpu.hf_token, model=model, prompt=prompt, img_path=input, diff --git a/skynet/dgpu/__init__.py b/skynet/dgpu/__init__.py index a22655d..cd460ae 100755 --- a/skynet/dgpu/__init__.py +++ b/skynet/dgpu/__init__.py @@ -5,7 +5,7 @@ import trio import urwid from skynet.config import Config -from skynet.dgpu.tui import init_tui +from skynet.dgpu.tui import init_tui, WorkerMonitor from skynet.dgpu.daemon import dgpu_serve_forever from skynet.dgpu.network import NetConnector, maybe_open_contract_state_mngr @@ -15,7 +15,7 @@ async def open_worker(config: Config): # suppress logs from httpx (logs url + status after every query) logging.getLogger("httpx").setLevel(logging.WARNING) - tui = None + tui: WorkerMonitor | None = None if config.tui: tui = init_tui(config) diff --git a/skynet/dgpu/compute.py b/skynet/dgpu/compute.py index c309d4b..82e68e2 100755 --- a/skynet/dgpu/compute.py +++ b/skynet/dgpu/compute.py @@ -7,6 +7,7 @@ import gc import logging from hashlib import sha256 +from typing import Callable, Generator from contextlib import contextmanager as cm import trio @@ -20,7 +21,14 @@ from skynet.dgpu.errors import ( DGPUInferenceCancelled, ) -from skynet.dgpu.utils import crop_image, convert_from_cv2_to_image, convert_from_image_to_cv2, convert_from_img_to_bytes, init_upscaler, pipeline_for +from skynet.dgpu.utils import ( + Pipeline, + crop_image, + convert_from_cv2_to_image, + convert_from_image_to_cv2, + convert_from_img_to_bytes, + pipeline_for +) def prepare_params_for_diffuse( @@ -68,17 +76,21 @@ def prepare_params_for_diffuse( _model_name: str = '' _model_mode: str = '' -_model = None +_model: Pipeline | None = None @cm -def maybe_load_model(name: str, mode: ModelMode): +def maybe_load_model(name: str, mode: ModelMode) -> Generator[Pipeline, None, None]: if mode == ModelMode.DIFFUSE: mode = ModelMode.TXT2IMG global _model_name, _model_mode, _model config = load_skynet_toml().dgpu - if _model_name != name or _model_mode != mode: + if ( + _model_name != name + or + _model_mode != mode + ): # unload model _model = None gc.collect() @@ -94,24 +106,26 @@ def maybe_load_model(name: str, mode: ModelMode): _model_mode = mode if torch.cuda.is_available(): - logging.debug('memory summary:') - logging.debug('\n' + torch.cuda.memory_summary()) + logging.debug( + 'memory summary:\n' + f'{torch.cuda.memory_summary()}' + ) yield _model def compute_one( - model, + model: Pipeline, request_id: int, method: ModelMode, params: BodyV0Params, inputs: list[bytes] = [], - should_cancel = None + should_cancel: Callable[[int, ...], dict] = None ): total_steps = params.step def inference_step_wakeup(*args, **kwargs): '''This is a callback function that gets invoked every inference step, - we need to raise an exception here if we need to cancel work + we must raise DGPUInferenceCancelled here if we need to cancel work ''' step = args[0] # compat with callback_on_step_end @@ -122,6 +136,9 @@ def compute_one( should_raise = False if should_cancel: + '''Pump main thread event loop, evaluate if we should keep working + on this request, based on latest network info like competitors... + ''' should_raise = trio.from_thread.run(should_cancel, request_id) if should_raise: @@ -137,60 +154,56 @@ def compute_one( output_type = params.output_type output = None output_hash = None - try: - name = params.model + name = params.model - match method: - case ( - ModelMode.DIFFUSE | - ModelMode.TXT2IMG | - ModelMode.IMG2IMG | - ModelMode.INPAINT - ): - arguments = prepare_params_for_diffuse( - params, method, inputs) - prompt, guidance, step, seed, extra_params = arguments + match method: + case ( + ModelMode.DIFFUSE | + ModelMode.TXT2IMG | + ModelMode.IMG2IMG | + ModelMode.INPAINT + ): + arguments = prepare_params_for_diffuse( + params, method, inputs) + prompt, guidance, step, seed, extra_params = arguments - if 'flux' in name.lower(): - extra_params['callback_on_step_end'] = inference_step_wakeup + if 'flux' in name.lower(): + extra_params['callback_on_step_end'] = inference_step_wakeup - else: - extra_params['callback'] = inference_step_wakeup - extra_params['callback_steps'] = 1 + else: + extra_params['callback'] = inference_step_wakeup + extra_params['callback_steps'] = 1 - output = model( - prompt, - guidance_scale=guidance, - num_inference_steps=step, - generator=seed, - **extra_params - ).images[0] + output = model( + prompt, + guidance_scale=guidance, + num_inference_steps=step, + generator=seed, + **extra_params + ).images[0] - output_binary = b'' - match output_type: - case 'png': - output_binary = convert_from_img_to_bytes(output) + output_binary = b'' + match output_type: + case 'png': + output_binary = convert_from_img_to_bytes(output) - case _: - raise DGPUComputeError(f'Unsupported output type: {output_type}') + case _: + raise DGPUComputeError(f'Unsupported output type: {output_type}') - output_hash = sha256(output_binary).hexdigest() + output_hash = sha256(output_binary).hexdigest() - case 'upscale': - input_img = inputs[0].convert('RGB') - up_img, _ = model.enhance( - convert_from_image_to_cv2(input_img), outscale=4) + case 'upscale': + input_img = inputs[0].convert('RGB') + up_img, _ = model.enhance( + convert_from_image_to_cv2(input_img), outscale=4) - output = convert_from_cv2_to_image(up_img) + output = convert_from_cv2_to_image(up_img) - output_binary = convert_from_img_to_bytes(output) - output_hash = sha256(output_binary).hexdigest() + output_binary = convert_from_img_to_bytes(output) + output_hash = sha256(output_binary).hexdigest() - case _: - raise DGPUComputeError('Unsupported compute method') - - except BaseException as err: - raise DGPUComputeError(str(err)) from err + case _: + raise DGPUComputeError('Unsupported compute method') maybe_update_tui(lambda tui: tui.set_status('')) diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index dd7022b..8016e64 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -132,6 +132,11 @@ async def maybe_serve_one( output_hash = None match config.backend: case 'sync-on-thread': + '''Block this task until inference completes, pass + state_mngr.should_cancel_work predicate as the inference_step_wakeup cb + used by torch each step of the inference, it will use a + trio.from_thread to unblock the main thread and pump the event loop + ''' output_hash, output = await trio.to_thread.run_sync( partial( compute_one, diff --git a/skynet/dgpu/tui.py b/skynet/dgpu/tui.py index e1c572a..ce8db5c 100644 --- a/skynet/dgpu/tui.py +++ b/skynet/dgpu/tui.py @@ -141,10 +141,6 @@ class WorkerMonitor: self.progress_bar.current = current - pct = 0 - if self.progress_bar.done != 0: - pct = int((self.progress_bar.current / self.progress_bar.done) * 100) - def update_requests(self, new_requests): """ Replace the data in the existing ListBox with new request widgets. diff --git a/skynet/dgpu/utils.py b/skynet/dgpu/utils.py index 6a17c93..daf955f 100755 --- a/skynet/dgpu/utils.py +++ b/skynet/dgpu/utils.py @@ -75,6 +75,9 @@ class DummyPB: def update(self): ... + +type Pipeline = DiffusionPipeline | RealESRGANer + @torch.compiler.disable @contextmanager def dummy_progress_bar(*args, **kwargs): @@ -90,7 +93,7 @@ def pipeline_for( mode: str, mem_fraction: float = 1.0, cache_dir: str | None = None -) -> DiffusionPipeline: +) -> Pipeline: diffusers.utils.logging.disable_progress_bar() logging.info(f'pipeline_for {model} {mode}') diff --git a/skynet/types.py b/skynet/types.py index 45450aa..44e1bce 100644 --- a/skynet/types.py +++ b/skynet/types.py @@ -13,7 +13,7 @@ class ModelMode(StrEnum): class ModelDesc(Struct): short: str # short unique name - mem: float # recomended mem + mem: float # recomended mem in gb attrs: dict # additional mode specific attrs tags: list[ModelMode] From ecf21be7c86079cf11301e0b2d9e1417db4ae628 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Thu, 20 Feb 2025 14:58:44 -0300 Subject: [PATCH 8/9] Update and pin py-leap --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a99e121..8f266e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,7 +77,7 @@ explicit = true torch = { index = "torch" } triton = { index = "torch" } torchvision = { index = "torch" } -py-leap = { git = "https://github.com/guilledk/py-leap.git", branch = "struct_unwrap" } +py-leap = { git = "https://github.com/guilledk/py-leap.git", rev = "v0.1a35" } pytest-dockerctl = { git = "https://github.com/pikers/pytest-dockerctl.git", branch = "g_update" } [build-system] From 5b935e5b4d07a6bc9442bd11e3d74f68cfa6946f Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Fri, 21 Feb 2025 10:35:35 -0300 Subject: [PATCH 9/9] Add Zolty review comments --- skynet/dgpu/daemon.py | 6 ++++-- skynet/dgpu/network.py | 4 ++-- uv.lock | 21 ++++++++++----------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/skynet/dgpu/daemon.py b/skynet/dgpu/daemon.py index 8016e64..2b47069 100755 --- a/skynet/dgpu/daemon.py +++ b/skynet/dgpu/daemon.py @@ -64,7 +64,7 @@ async def maybe_serve_one( and model in config.model_blacklist ): - logging.warning('model not blacklisted!, skip...') + logging.warning('model is blacklisted!, skip...') return # if worker already produced a result for this request @@ -73,7 +73,7 @@ async def maybe_serve_one( return # skip if workers in non_compete already on it - if state_mngr.should_compete_for_id(req.id): + if not state_mngr.should_compete_for_id(req.id): logging.info('worker in configured non_compete list already working on request, skip...') return @@ -161,6 +161,8 @@ async def maybe_serve_one( await maybe_update_tui_balance(conn) + await state_mngr.update_state() + except BaseException as err: if 'network cancel' not in str(err): diff --git a/skynet/dgpu/network.py b/skynet/dgpu/network.py index e5f1070..6a8350b 100755 --- a/skynet/dgpu/network.py +++ b/skynet/dgpu/network.py @@ -405,7 +405,7 @@ class ContractState: return request_id in self._status_by_rid def should_compete_for_id(self, request_id: int) -> bool: - return bool( + return not bool( self._conn.config.non_compete & self.competitors_for_id(request_id) ) @@ -416,7 +416,7 @@ class ContractState: logging.info(f'request #{request_id} no longer in queue, likely its been filled by another worker, cancelling work...') return True - should_cancel = self.should_compete_for_id(request_id) + should_cancel = not self.should_compete_for_id(request_id) logging.info(f'cancel: {should_cancel}') return should_cancel diff --git a/uv.lock b/uv.lock index 57355d4..28c4443 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.10, <3.13" resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", @@ -271,7 +272,7 @@ name = "cffi" version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } wheels = [ @@ -1271,7 +1272,7 @@ name = "nvidia-cudnn-cu12" version = "9.1.0.70" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, @@ -1298,9 +1299,9 @@ name = "nvidia-cusolver-cu12" version = "11.4.5.107" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd", size = 124161928 }, @@ -1311,7 +1312,7 @@ name = "nvidia-cusparse-cu12" version = "12.1.0.106" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c", size = 195958278 }, @@ -1598,7 +1599,7 @@ wheels = [ [[package]] name = "py-leap" version = "0.1a35" -source = { git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap#18b3c73e724922a060db5f8ea2b9d9727b6152cc" } +source = { git = "https://github.com/guilledk/py-leap.git?rev=v0.1a35#20f2e1f74e98e3d75984e8e1eee13c3100c17652" } dependencies = [ { name = "base58" }, { name = "cryptos" }, @@ -1633,8 +1634,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/74/49f5d20c514ccc631b940cc9dfec45dcce418dc84a98463a2e2ebec33904/pycryptodomex-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:52e23a0a6e61691134aa8c8beba89de420602541afaae70f66e16060fdcd677e", size = 2257982 }, { url = "https://files.pythonhosted.org/packages/92/4b/d33ef74e2cc0025a259936661bb53432c5bbbadc561c5f2e023bcd73ce4c/pycryptodomex-3.21.0-cp36-abi3-win32.whl", hash = "sha256:a3d77919e6ff56d89aada1bd009b727b874d464cb0e2e3f00a49f7d2e709d76e", size = 1779052 }, { url = "https://files.pythonhosted.org/packages/5b/be/7c991840af1184009fc86267160948350d1bf875f153c97bb471ad944e40/pycryptodomex-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b0e9765f93fe4890f39875e6c90c96cb341767833cfa767f41b490b506fa9ec0", size = 1816307 }, - { url = "https://files.pythonhosted.org/packages/af/ac/24125ad36778914a36f08d61ba5338cb9159382c638d9761ee19c8de822c/pycryptodomex-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:feaecdce4e5c0045e7a287de0c4351284391fe170729aa9182f6bd967631b3a8", size = 1694999 }, - { url = "https://files.pythonhosted.org/packages/93/73/be7a54a5903508070e5508925ba94493a1f326cfeecfff750e3eb250ea28/pycryptodomex-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:365aa5a66d52fd1f9e0530ea97f392c48c409c2f01ff8b9a39c73ed6f527d36c", size = 1769437 }, { url = "https://files.pythonhosted.org/packages/e5/9f/39a6187f3986841fa6a9f35c6fdca5030ef73ff708b45a993813a51d7d10/pycryptodomex-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3efddfc50ac0ca143364042324046800c126a1d63816d532f2e19e6f2d8c0c31", size = 1619607 }, { url = "https://files.pythonhosted.org/packages/f8/70/60bb08e9e9841b18d4669fb69d84b64ce900aacd7eb0ebebd4c7b9bdecd3/pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df2608682db8279a9ebbaf05a72f62a321433522ed0e499bc486a6889b96bf3", size = 1653571 }, { url = "https://files.pythonhosted.org/packages/c9/6f/191b73509291c5ff0dddec9cc54797b1d73303c12b2e4017b24678e57099/pycryptodomex-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5823d03e904ea3e53aebd6799d6b8ec63b7675b5d2f4a4bd5e3adcb512d03b37", size = 1691548 }, @@ -2126,7 +2125,7 @@ requires-dist = [ { name = "outcome", specifier = ">=1.3.0.post0" }, { name = "pillow", specifier = ">=10.0.1,<11" }, { name = "protobuf", specifier = ">=5.29.3,<6" }, - { name = "py-leap", git = "https://github.com/guilledk/py-leap.git?branch=struct_unwrap" }, + { name = "py-leap", git = "https://github.com/guilledk/py-leap.git?rev=v0.1a35" }, { name = "pytz", specifier = "~=2023.3.post1" }, { name = "toml", specifier = ">=0.10.2,<0.11" }, { name = "trio", specifier = ">=0.22.2,<0.23" }, @@ -2436,7 +2435,7 @@ name = "triton" version = "3.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, + { name = "filelock", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/98/29/69aa56dc0b2eb2602b553881e34243475ea2afd9699be042316842788ff5/triton-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b0dd10a925263abbe9fa37dcde67a5e9b2383fc269fdf59f5657cac38c5d1d8", size = 209460013 },