diff --git a/.env.miner.example b/.env.miner.example index 06d144d..bb42523 100644 --- a/.env.miner.example +++ b/.env.miner.example @@ -1,5 +1,6 @@ # Network Configuration -NETWORK=localnet # Options: localnet, testnet, finney +# Options: localnet, testnet, finney +NETWORK=testnet # Wallet Configuration COLDKEY=miner @@ -8,8 +9,6 @@ MINER_HOTKEY=default # Node Configuration MINER_NAME=miner MINER_PORT=8092 -AXON_IP=127.0.0.1 -AXON_EXTERNAL_IP=127.0.0.1 # Miner Settings TIMEOUT=16 @@ -17,4 +16,5 @@ VPERMIT_TAO_LIMIT=2 FORWARD_FUNCTION=base_miner # Logging -LOGGING_LEVEL=info # Options: info, debug, trace +# Options: info, debug, trace +LOGGING_LEVEL=debug diff --git a/.env.validator.example b/.env.validator.example index 9de8931..c81f7ba 100644 --- a/.env.validator.example +++ b/.env.validator.example @@ -1,5 +1,6 @@ # Network Configuration -NETWORK=testnet # Options: localnet, testnet, finney +# Options: localnet, testnet, finney +NETWORK=testnet # Wallet Configuration COLDKEY=validator @@ -8,8 +9,7 @@ VALIDATOR_HOTKEY=default # Node Configuration VALIDATOR_NAME=validator VALIDATOR_PORT=8091 -AXON_IP=127.0.0.1 -AXON_EXTERNAL_IP=127.0.0.1 # Logging -LOGGING_LEVEL=info # Options: info, debug, trace +# Options: info, debug, trace +LOGGING_LEVEL=debug diff --git a/Makefile b/Makefile index 58babb6..802eb95 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,3 @@ -ENV_FILE ?= .env - include $(ENV_FILE) export @@ -12,7 +10,7 @@ ifeq ($(NETWORK),localnet) else ifeq ($(NETWORK),testnet) netuid = 256 else ifeq ($(NETWORK),finney) - #netuid = 64 + netuid = 55 $(error Finney network not supported yet) endif @@ -27,28 +25,26 @@ register: } validator: - python start_validator.py \ + pm2 start --name $(VALIDATOR_NAME) python3 -- precog/validators/validator.py \ --neuron.name $(VALIDATOR_NAME) \ --wallet.name $(COLDKEY) \ --wallet.hotkey $(VALIDATOR_HOTKEY) \ --subtensor.chain_endpoint $($(NETWORK)) \ --axon.port $(VALIDATOR_PORT) \ - --axon.ip $(AXON_IP) \ - --axon.external_ip $(AXON_EXTERNAL_IP) \ --netuid $(netuid) \ - --logging.level $(LOGGING_LEVEL) + --logging.level $(LOGGING_LEVEL) \ + --wandb.off miner: - python start_miner.py \ + pm2 start --name $(MINER_NAME) python3 -- precog/miners/miner.py \ --neuron.name $(MINER_NAME) \ --wallet.name $(COLDKEY) \ --wallet.hotkey $(MINER_HOTKEY) \ --subtensor.chain_endpoint $($(NETWORK)) \ --axon.port $(MINER_PORT) \ - --axon.ip $(AXON_IP) \ - --axon.external_ip $(AXON_EXTERNAL_IP) \ --netuid $(netuid) \ --logging.level $(LOGGING_LEVEL) \ --timeout $(TIMEOUT) \ --vpermit_tao_limit $(VPERMIT_TAO_LIMIT) \ - --forward_function $(FORWARD_FUNCTION) + --forward_function $(FORWARD_FUNCTION) \ + --wandb.off diff --git a/README.md b/README.md index e4782ea..c2c384c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ # **CoinMetrics Precog Subnet** +
+ +| **Testnet UID:** 256
**Mainnet UID:** 55 | +| - | + +
+ +
+ | | | | :-: | :-: | | **Status** |

| @@ -35,6 +44,29 @@ The incentive mechanism was specifically designed to reward precise statements o | 2 vCPUs | 2 vCPUs | --- + +## Prerequisites + +Before beginning, ensure you have: + +1. **Python Installation** + - Python version 3.9, 3.10, or 3.11 installed + - We recommend using `pyenv` for Python version management + +2. **Bittensor Knowledge** + - Understanding of the Bittensor ecosystem and wallet management + - Familiarity with creating and managing Bittensor wallets + - Review the [Bittensor Wallet Documentation](https://docs.bittensor.com/getting-started/wallets) + - For general understanding, see the [Bittensor Documentation](https://docs.bittensor.com/) + +3. **Poetry** + - Basic understanding of Poetry for dependency management + - See the [Poetry Documentation](https://python-poetry.org/docs/) for installation and usage + +4. **System Requirements** + - NodeJS and NPM (for PM2 installation) + - Basic understanding of command-line interfaces + ## Installation First, install PM2: @@ -70,21 +102,8 @@ poetry install --- ## Configuration -### Makefile -Start by editing the Makefile with your wallet and network information: -``` -################################################################################ -# User Parameters # -################################################################################ -coldkey = default -validator_hotkey = validator -miner_hotkey = miner -netuid = $(testnet_netuid) -network = $(testnet) -``` - ### .env Files -Copy the example `.env` files and edit all desired values: +Copy the example `.env` files and edit all desired values. If you are running a validator, you will only need to copy the .env.validator file. If you are running a miner, you will only need to copy the .env.miner file: #### .env.validator ``` @@ -92,18 +111,61 @@ cp .env.validator.example .env.validator ``` Edit `.env.validator` with your desired values. +``` +# Network Configuration +# Options: localnet, testnet, finney +NETWORK=testnet + +# Wallet Configuration +COLDKEY=your_validator_coldkey +VALIDATOR_HOTKEY=your_validator_hotkey + +# Node Configuration +VALIDATOR_NAME=validator +VALIDATOR_PORT=8091 + +# Logging +# Options: info, debug, trace +LOGGING_LEVEL=debug +``` + #### .env.miner ``` cp .env.miner.example .env.miner ``` Edit `.env.miner` with your desired values. -### Wandb -Wandb integration is planned for mainnet launch and does not currently work. +```# Network Configuration +# Options: localnet, testnet, finney +NETWORK=testnet + +# Wallet Configuration +COLDKEY=your_miner_coldkey +MINER_HOTKEY=your_miner_hotkey + +# Node Configuration +MINER_NAME=miner +# This port must be open to accept incoming TCP connections. +MINER_PORT=8092 + +# Miner Settings +TIMEOUT=16 +VPERMIT_TAO_LIMIT=2 + +#Adjust this function if you would like to specify a custom forward function +FORWARD_FUNCTION=base_miner + +# Logging +# Options: info, debug, trace +LOGGING_LEVEL=debug +``` --- ## Deployment +### Registering a Hotkey +Once you have configured your .env files as per the instructions above, you can register a miner with `make register ENV_FILE=.env.miner` or register a validator with `make register ENV_FILE=.env.validator`. + ### Running a Miner Base miner: 1. Run the command: diff --git a/docs/Release Notes.md b/docs/Release Notes.md index ee8f021..43408dd 100644 --- a/docs/Release Notes.md +++ b/docs/Release Notes.md @@ -1,6 +1,11 @@ Release Notes ============= +1.0.0 +----- +Released on January 14th 2025 +- Release to mainnet + 0.3.0 ----- Released on January 7th 2025 diff --git a/poetry.lock b/poetry.lock index 2c548b8..c34fa1f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -150,13 +150,13 @@ files = [ [[package]] name = "anyio" -version = "4.7.0" +version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, - {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, + {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, + {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, ] [package.dependencies] @@ -167,7 +167,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -1050,21 +1050,21 @@ test = ["asn1tools (>=0.146.2)", "eth-hash[pysha3]", "factory-boy (>=3.0.1)", "h [[package]] name = "eth-typing" -version = "5.0.1" +version = "5.1.0" description = "eth-typing: Common type annotations for ethereum python packages" optional = false python-versions = "<4,>=3.8" files = [ - {file = "eth_typing-5.0.1-py3-none-any.whl", hash = "sha256:f30d1af16aac598f216748a952eeb64fbcb6e73efa691d2de31148138afe96de"}, - {file = "eth_typing-5.0.1.tar.gz", hash = "sha256:83debf88c9df286db43bb7374974681ebcc9f048fac81be2548dbc549a3203c0"}, + {file = "eth_typing-5.1.0-py3-none-any.whl", hash = "sha256:c0d6b93f5385aa84efc4b47ae2bd478da069bc0ffda8b67e0ccb573f43defd29"}, + {file = "eth_typing-5.1.0.tar.gz", hash = "sha256:8581f212ee6252aaa285377a77620f6e5f6e16ac3f144c61f098fafd47967b1a"}, ] [package.dependencies] -typing-extensions = ">=4.5.0" +typing_extensions = ">=4.5.0" [package.extras] -dev = ["build (>=0.9.0)", "bumpversion (>=0.5.3)", "ipython", "mypy (==1.10.0)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)", "tox (>=4.0.0)", "twine", "wheel"] -docs = ["sphinx (>=6.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] +dev = ["build (>=0.9.0)", "bump_my_version (>=0.19.0)", "ipython", "mypy (==1.10.0)", "pre-commit (>=3.4.0)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)", "sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)", "tox (>=4.0.0)", "twine", "wheel"] +docs = ["sphinx (>=6.0.0)", "sphinx-autobuild (>=2021.3.14)", "sphinx_rtd_theme (>=1.0.0)", "towncrier (>=24,<25)"] test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] [[package]] @@ -1315,13 +1315,13 @@ files = [ [[package]] name = "hypothesis" -version = "6.123.2" +version = "6.123.13" description = "A library for property-based testing" optional = false python-versions = ">=3.9" files = [ - {file = "hypothesis-6.123.2-py3-none-any.whl", hash = "sha256:0a8bf07753f1436f1b8697a13ea955f3fef3ef7b477c2972869b1d142bcdb30e"}, - {file = "hypothesis-6.123.2.tar.gz", hash = "sha256:02c25552783764146b191c69eef69d8375827b58a75074055705ab8fdbc95fc5"}, + {file = "hypothesis-6.123.13-py3-none-any.whl", hash = "sha256:57c3b797664f6b45ce322a2e7854ee1c49fca67c07803cfa6ce2a9b825629d34"}, + {file = "hypothesis-6.123.13.tar.gz", hash = "sha256:4a680787b7f84d7df918d9c05203677931495e548bbc962aa1c9e4643add58f3"}, ] [package.dependencies] @@ -1330,10 +1330,10 @@ exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.78)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.18)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.81)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.18)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"] cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] -crosshair = ["crosshair-tool (>=0.0.78)", "hypothesis-crosshair (>=0.0.18)"] +crosshair = ["crosshair-tool (>=0.0.81)", "hypothesis-crosshair (>=0.0.18)"] dateutil = ["python-dateutil (>=1.4)"] django = ["django (>=4.2)"] dpcontracts = ["dpcontracts (>=0.4)"] @@ -1348,13 +1348,13 @@ zoneinfo = ["tzdata (>=2024.2)"] [[package]] name = "identify" -version = "2.6.4" +version = "2.6.5" description = "File identification library for Python" optional = false python-versions = ">=3.9" files = [ - {file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"}, - {file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"}, + {file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"}, + {file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"}, ] [package.extras] @@ -2015,86 +2015,86 @@ files = [ [[package]] name = "orjson" -version = "3.10.13" +version = "3.10.14" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.13-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1232c5e873a4d1638ef957c5564b4b0d6f2a6ab9e207a9b3de9de05a09d1d920"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d26a0eca3035619fa366cbaf49af704c7cb1d4a0e6c79eced9f6a3f2437964b6"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d4b6acd7c9c829895e50d385a357d4b8c3fafc19c5989da2bae11783b0fd4977"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1884e53c6818686891cc6fc5a3a2540f2f35e8c76eac8dc3b40480fb59660b00"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a428afb5720f12892f64920acd2eeb4d996595bf168a26dd9190115dbf1130d"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba5b13b8739ce5b630c65cb1c85aedbd257bcc2b9c256b06ab2605209af75a2e"}, - {file = "orjson-3.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cab83e67f6aabda1b45882254b2598b48b80ecc112968fc6483fa6dae609e9f0"}, - {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:62c3cc00c7e776c71c6b7b9c48c5d2701d4c04e7d1d7cdee3572998ee6dc57cc"}, - {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:dc03db4922e75bbc870b03fc49734cefbd50fe975e0878327d200022210b82d8"}, - {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:22f1c9a30b43d14a041a6ea190d9eca8a6b80c4beb0e8b67602c82d30d6eec3e"}, - {file = "orjson-3.10.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b42f56821c29e697c68d7d421410d7c1d8f064ae288b525af6a50cf99a4b1200"}, - {file = "orjson-3.10.13-cp310-cp310-win32.whl", hash = "sha256:0dbf3b97e52e093d7c3e93eb5eb5b31dc7535b33c2ad56872c83f0160f943487"}, - {file = "orjson-3.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:46c249b4e934453be4ff2e518cd1adcd90467da7391c7a79eaf2fbb79c51e8c7"}, - {file = "orjson-3.10.13-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a36c0d48d2f084c800763473020a12976996f1109e2fcb66cfea442fdf88047f"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0065896f85d9497990731dfd4a9991a45b0a524baec42ef0a63c34630ee26fd6"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92b4ec30d6025a9dcdfe0df77063cbce238c08d0404471ed7a79f309364a3d19"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a94542d12271c30044dadad1125ee060e7a2048b6c7034e432e116077e1d13d2"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3723e137772639af8adb68230f2aa4bcb27c48b3335b1b1e2d49328fed5e244c"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f00c7fb18843bad2ac42dc1ce6dd214a083c53f1e324a0fd1c8137c6436269b"}, - {file = "orjson-3.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0e2759d3172300b2f892dee85500b22fca5ac49e0c42cfff101aaf9c12ac9617"}, - {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ee948c6c01f6b337589c88f8e0bb11e78d32a15848b8b53d3f3b6fea48842c12"}, - {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa6fe68f0981fba0d4bf9cdc666d297a7cdba0f1b380dcd075a9a3dd5649a69e"}, - {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbcd7aad6bcff258f6896abfbc177d54d9b18149c4c561114f47ebfe74ae6bfd"}, - {file = "orjson-3.10.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2149e2fcd084c3fd584881c7f9d7f9e5ad1e2e006609d8b80649655e0d52cd02"}, - {file = "orjson-3.10.13-cp311-cp311-win32.whl", hash = "sha256:89367767ed27b33c25c026696507c76e3d01958406f51d3a2239fe9e91959df2"}, - {file = "orjson-3.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:dca1d20f1af0daff511f6e26a27354a424f0b5cf00e04280279316df0f604a6f"}, - {file = "orjson-3.10.13-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a3614b00621c77f3f6487792238f9ed1dd8a42f2ec0e6540ee34c2d4e6db813a"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c976bad3996aa027cd3aef78aa57873f3c959b6c38719de9724b71bdc7bd14b"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f74d878d1efb97a930b8a9f9898890067707d683eb5c7e20730030ecb3fb930"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33ef84f7e9513fb13b3999c2a64b9ca9c8143f3da9722fbf9c9ce51ce0d8076e"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd2bcde107221bb9c2fa0c4aaba735a537225104173d7e19cf73f70b3126c993"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:064b9dbb0217fd64a8d016a8929f2fae6f3312d55ab3036b00b1d17399ab2f3e"}, - {file = "orjson-3.10.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0044b0b8c85a565e7c3ce0a72acc5d35cda60793edf871ed94711e712cb637d"}, - {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7184f608ad563032e398f311910bc536e62b9fbdca2041be889afcbc39500de8"}, - {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d36f689e7e1b9b6fb39dbdebc16a6f07cbe994d3644fb1c22953020fc575935f"}, - {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54433e421618cd5873e51c0e9d0b9fb35f7bf76eb31c8eab20b3595bb713cd3d"}, - {file = "orjson-3.10.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e1ba0c5857dd743438acecc1cd0e1adf83f0a81fee558e32b2b36f89e40cee8b"}, - {file = "orjson-3.10.13-cp312-cp312-win32.whl", hash = "sha256:a42b9fe4b0114b51eb5cdf9887d8c94447bc59df6dbb9c5884434eab947888d8"}, - {file = "orjson-3.10.13-cp312-cp312-win_amd64.whl", hash = "sha256:3a7df63076435f39ec024bdfeb4c9767ebe7b49abc4949068d61cf4857fa6d6c"}, - {file = "orjson-3.10.13-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2cdaf8b028a976ebab837a2c27b82810f7fc76ed9fb243755ba650cc83d07730"}, - {file = "orjson-3.10.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48a946796e390cbb803e069472de37f192b7a80f4ac82e16d6eb9909d9e39d56"}, - {file = "orjson-3.10.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7d64f1db5ecbc21eb83097e5236d6ab7e86092c1cd4c216c02533332951afc"}, - {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:711878da48f89df194edd2ba603ad42e7afed74abcd2bac164685e7ec15f96de"}, - {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cf16f06cb77ce8baf844bc222dbcb03838f61d0abda2c3341400c2b7604e436e"}, - {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8257c3fb8dd7b0b446b5e87bf85a28e4071ac50f8c04b6ce2d38cb4abd7dff57"}, - {file = "orjson-3.10.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9c3a87abe6f849a4a7ac8a8a1dede6320a4303d5304006b90da7a3cd2b70d2c"}, - {file = "orjson-3.10.13-cp313-cp313-win32.whl", hash = "sha256:527afb6ddb0fa3fe02f5d9fba4920d9d95da58917826a9be93e0242da8abe94a"}, - {file = "orjson-3.10.13-cp313-cp313-win_amd64.whl", hash = "sha256:b5f7c298d4b935b222f52d6c7f2ba5eafb59d690d9a3840b7b5c5cda97f6ec5c"}, - {file = "orjson-3.10.13-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e49333d1038bc03a25fdfe11c86360df9b890354bfe04215f1f54d030f33c342"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:003721c72930dbb973f25c5d8e68d0f023d6ed138b14830cc94e57c6805a2eab"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63664bf12addb318dc8f032160e0f5dc17eb8471c93601e8f5e0d07f95003784"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6066729cf9552d70de297b56556d14b4f49c8f638803ee3c90fd212fa43cc6af"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a1152e2761025c5d13b5e1908d4b1c57f3797ba662e485ae6f26e4e0c466388"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b21d91c5c5ef8a201036d207b1adf3aa596b930b6ca3c71484dd11386cf6c3"}, - {file = "orjson-3.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b12a63f48bb53dba8453d36ca2661f2330126d54e26c1661e550b32864b28ce3"}, - {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a5a7624ab4d121c7e035708c8dd1f99c15ff155b69a1c0affc4d9d8b551281ba"}, - {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0fee076134398d4e6cb827002468679ad402b22269510cf228301b787fdff5ae"}, - {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ae537fcf330b3947e82c6ae4271e092e6cf16b9bc2cef68b14ffd0df1fa8832a"}, - {file = "orjson-3.10.13-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f81b26c03f5fb5f0d0ee48d83cea4d7bc5e67e420d209cc1a990f5d1c62f9be0"}, - {file = "orjson-3.10.13-cp38-cp38-win32.whl", hash = "sha256:0bc858086088b39dc622bc8219e73d3f246fb2bce70a6104abd04b3a080a66a8"}, - {file = "orjson-3.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:3ca6f17467ebbd763f8862f1d89384a5051b461bb0e41074f583a0ebd7120e8e"}, - {file = "orjson-3.10.13-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4a11532cbfc2f5752c37e84863ef8435b68b0e6d459b329933294f65fa4bda1a"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c96d2fb80467d1d0dfc4d037b4e1c0f84f1fe6229aa7fea3f070083acef7f3d7"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dda4ba4d3e6f6c53b6b9c35266788053b61656a716a7fef5c884629c2a52e7aa"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f998bbf300690be881772ee9c5281eb9c0044e295bcd4722504f5b5c6092ff"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1cc42ed75b585c0c4dc5eb53a90a34ccb493c09a10750d1a1f9b9eff2bd12"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b0f29d485411e3c13d79604b740b14e4e5fb58811743f6f4f9693ee6480a8f"}, - {file = "orjson-3.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:233aae4474078d82f425134bb6a10fb2b3fc5a1a1b3420c6463ddd1b6a97eda8"}, - {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e384e330a67cf52b3597ee2646de63407da6f8fc9e9beec3eaaaef5514c7a1c9"}, - {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4222881d0aab76224d7b003a8e5fdae4082e32c86768e0e8652de8afd6c4e2c1"}, - {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e400436950ba42110a20c50c80dff4946c8e3ec09abc1c9cf5473467e83fd1c5"}, - {file = "orjson-3.10.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f47c9e7d224b86ffb086059cdcf634f4b3f32480f9838864aa09022fe2617ce2"}, - {file = "orjson-3.10.13-cp39-cp39-win32.whl", hash = "sha256:a9ecea472f3eb653e1c0a3d68085f031f18fc501ea392b98dcca3e87c24f9ebe"}, - {file = "orjson-3.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:5385935a73adce85cc7faac9d396683fd813566d3857fa95a0b521ef84a5b588"}, - {file = "orjson-3.10.13.tar.gz", hash = "sha256:eb9bfb14ab8f68d9d9492d4817ae497788a15fd7da72e14dfabc289c3bb088ec"}, + {file = "orjson-3.10.14-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:849ea7845a55f09965826e816cdc7689d6cf74fe9223d79d758c714af955bcb6"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5947b139dfa33f72eecc63f17e45230a97e741942955a6c9e650069305eb73d"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cde6d76910d3179dae70f164466692f4ea36da124d6fb1a61399ca589e81d69a"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6dfbaeb7afa77ca608a50e2770a0461177b63a99520d4928e27591b142c74b1"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa45e489ef80f28ff0e5ba0a72812b8cfc7c1ef8b46a694723807d1b07c89ebb"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5007abfdbb1d866e2aa8990bd1c465f0f6da71d19e695fc278282be12cffa5"}, + {file = "orjson-3.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b49e2af011c84c3f2d541bb5cd1e3c7c2df672223e7e3ea608f09cf295e5f8a"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:164ac155109226b3a2606ee6dda899ccfbe6e7e18b5bdc3fbc00f79cc074157d"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6b1225024cf0ef5d15934b5ffe9baf860fe8bc68a796513f5ea4f5056de30bca"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d6546e8073dc382e60fcae4a001a5a1bc46da5eab4a4878acc2d12072d6166d5"}, + {file = "orjson-3.10.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f1d2942605c894162252d6259b0121bf1cb493071a1ea8cb35d79cb3e6ac5bc"}, + {file = "orjson-3.10.14-cp310-cp310-win32.whl", hash = "sha256:397083806abd51cf2b3bbbf6c347575374d160331a2d33c5823e22249ad3118b"}, + {file = "orjson-3.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:fa18f949d3183a8d468367056be989666ac2bef3a72eece0bade9cdb733b3c28"}, + {file = "orjson-3.10.14-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f506fd666dd1ecd15a832bebc66c4df45c1902fd47526292836c339f7ba665a9"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efe5fd254cfb0eeee13b8ef7ecb20f5d5a56ddda8a587f3852ab2cedfefdb5f6"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ddc8c866d7467f5ee2991397d2ea94bcf60d0048bdd8ca555740b56f9042725"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af8e42ae4363773658b8d578d56dedffb4f05ceeb4d1d4dd3fb504950b45526"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84dd83110503bc10e94322bf3ffab8bc49150176b49b4984dc1cce4c0a993bf9"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36f5bfc0399cd4811bf10ec7a759c7ab0cd18080956af8ee138097d5b5296a95"}, + {file = "orjson-3.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868943660fb2a1e6b6b965b74430c16a79320b665b28dd4511d15ad5038d37d5"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33449c67195969b1a677533dee9d76e006001213a24501333624623e13c7cc8e"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e4c9f60f9fb0b5be66e416dcd8c9d94c3eabff3801d875bdb1f8ffc12cf86905"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0de4d6315cfdbd9ec803b945c23b3a68207fd47cbe43626036d97e8e9561a436"}, + {file = "orjson-3.10.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83adda3db595cb1a7e2237029b3249c85afbe5c747d26b41b802e7482cb3933e"}, + {file = "orjson-3.10.14-cp311-cp311-win32.whl", hash = "sha256:998019ef74a4997a9d741b1473533cdb8faa31373afc9849b35129b4b8ec048d"}, + {file = "orjson-3.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:9d034abdd36f0f0f2240f91492684e5043d46f290525d1117712d5b8137784eb"}, + {file = "orjson-3.10.14-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2ad4b7e367efba6dc3f119c9a0fcd41908b7ec0399a696f3cdea7ec477441b09"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f496286fc85e93ce0f71cc84fc1c42de2decf1bf494094e188e27a53694777a7"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7f189bbfcded40e41a6969c1068ba305850ba016665be71a217918931416fbf"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cc8204f0b75606869c707da331058ddf085de29558b516fc43c73ee5ee2aadb"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deaa2899dff7f03ab667e2ec25842d233e2a6a9e333efa484dfe666403f3501c"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1c3ea52642c9714dc6e56de8a451a066f6d2707d273e07fe8a9cc1ba073813d"}, + {file = "orjson-3.10.14-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d3f9ed72e7458ded9a1fb1b4d4ed4c4fdbaf82030ce3f9274b4dc1bff7ace2b"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:07520685d408a2aba514c17ccc16199ff2934f9f9e28501e676c557f454a37fe"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:76344269b550ea01488d19a2a369ab572c1ac4449a72e9f6ac0d70eb1cbfb953"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e2979d0f2959990620f7e62da6cd954e4620ee815539bc57a8ae46e2dacf90e3"}, + {file = "orjson-3.10.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03f61ca3674555adcb1aa717b9fc87ae936aa7a63f6aba90a474a88701278780"}, + {file = "orjson-3.10.14-cp312-cp312-win32.whl", hash = "sha256:d5075c54edf1d6ad81d4c6523ce54a748ba1208b542e54b97d8a882ecd810fd1"}, + {file = "orjson-3.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:175cafd322e458603e8ce73510a068d16b6e6f389c13f69bf16de0e843d7d406"}, + {file = "orjson-3.10.14-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:0905ca08a10f7e0e0c97d11359609300eb1437490a7f32bbaa349de757e2e0c7"}, + {file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92d13292249f9f2a3e418cbc307a9fbbef043c65f4bd8ba1eb620bc2aaba3d15"}, + {file = "orjson-3.10.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90937664e776ad316d64251e2fa2ad69265e4443067668e4727074fe39676414"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9ed3d26c4cb4f6babaf791aa46a029265850e80ec2a566581f5c2ee1a14df4f1"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:56ee546c2bbe9599aba78169f99d1dc33301853e897dbaf642d654248280dc6e"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:901e826cb2f1bdc1fcef3ef59adf0c451e8f7c0b5deb26c1a933fb66fb505eae"}, + {file = "orjson-3.10.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26336c0d4b2d44636e1e1e6ed1002f03c6aae4a8a9329561c8883f135e9ff010"}, + {file = "orjson-3.10.14-cp313-cp313-win32.whl", hash = "sha256:e2bc525e335a8545c4e48f84dd0328bc46158c9aaeb8a1c2276546e94540ea3d"}, + {file = "orjson-3.10.14-cp313-cp313-win_amd64.whl", hash = "sha256:eca04dfd792cedad53dc9a917da1a522486255360cb4e77619343a20d9f35364"}, + {file = "orjson-3.10.14-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a0fba3b8a587a54c18585f077dcab6dd251c170d85cfa4d063d5746cd595a0f"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:175abf3d20e737fec47261d278f95031736a49d7832a09ab684026528c4d96db"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29ca1a93e035d570e8b791b6c0feddd403c6a5388bfe870bf2aa6bba1b9d9b8e"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f77202c80e8ab5a1d1e9faf642343bee5aaf332061e1ada4e9147dbd9eb00c46"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2ec73b7099b6a29b40a62e08a23b936423bd35529f8f55c42e27acccde7954"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d1679df9f9cd9504f8dff24555c1eaabba8aad7f5914f28dab99e3c2552c9d"}, + {file = "orjson-3.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691ab9a13834310a263664313e4f747ceb93662d14a8bdf20eb97d27ed488f16"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b11ed82054fce82fb74cea33247d825d05ad6a4015ecfc02af5fbce442fbf361"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:e70a1d62b8288677d48f3bea66c21586a5f999c64ecd3878edb7393e8d1b548d"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:16642f10c1ca5611251bd835de9914a4b03095e28a34c8ba6a5500b5074338bd"}, + {file = "orjson-3.10.14-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3871bad546aa66c155e3f36f99c459780c2a392d502a64e23fb96d9abf338511"}, + {file = "orjson-3.10.14-cp38-cp38-win32.whl", hash = "sha256:0293a88815e9bb5c90af4045f81ed364d982f955d12052d989d844d6c4e50945"}, + {file = "orjson-3.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:6169d3868b190d6b21adc8e61f64e3db30f50559dfbdef34a1cd6c738d409dfc"}, + {file = "orjson-3.10.14-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:06d4ec218b1ec1467d8d64da4e123b4794c781b536203c309ca0f52819a16c03"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962c2ec0dcaf22b76dee9831fdf0c4a33d4bf9a257a2bc5d4adc00d5c8ad9034"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21d3be4132f71ef1360385770474f29ea1538a242eef72ac4934fe142800e37f"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28ed60597c149a9e3f5ad6dd9cebaee6fb2f0e3f2d159a4a2b9b862d4748860"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e947f70167fe18469f2023644e91ab3d24f9aed69a5e1c78e2c81b9cea553fb"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64410696c97a35af2432dea7bdc4ce32416458159430ef1b4beb79fd30093ad6"}, + {file = "orjson-3.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8050a5d81c022561ee29cd2739de5b4445f3c72f39423fde80a63299c1892c52"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b49a28e30d3eca86db3fe6f9b7f4152fcacbb4a467953cd1b42b94b479b77956"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ca041ad20291a65d853a9523744eebc3f5a4b2f7634e99f8fe88320695ddf766"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d313a2998b74bb26e9e371851a173a9b9474764916f1fc7971095699b3c6e964"}, + {file = "orjson-3.10.14-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7796692136a67b3e301ef9052bde6fe8e7bd5200da766811a3a608ffa62aaff0"}, + {file = "orjson-3.10.14-cp39-cp39-win32.whl", hash = "sha256:eee4bc767f348fba485ed9dc576ca58b0a9eac237f0e160f7a59bce628ed06b3"}, + {file = "orjson-3.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:96a1c0ee30fb113b3ae3c748fd75ca74a157ff4c58476c47db4d61518962a011"}, + {file = "orjson-3.10.14.tar.gz", hash = "sha256:cf31f6f071a6b8e7aa1ead1fa27b935b48d00fbfa6a28ce856cfff2d5dd68eed"}, ] [[package]] @@ -2375,22 +2375,22 @@ files = [ [[package]] name = "protobuf" -version = "5.29.2" +version = "5.29.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851"}, - {file = "protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9"}, - {file = "protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb"}, - {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e"}, - {file = "protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e"}, - {file = "protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19"}, - {file = "protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a"}, - {file = "protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9"}, - {file = "protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355"}, - {file = "protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181"}, - {file = "protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e"}, + {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, + {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, + {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"}, + {file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"}, + {file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"}, + {file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"}, + {file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"}, + {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"}, + {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"}, ] [[package]] @@ -2773,13 +2773,13 @@ files = [ [[package]] name = "pydantic" -version = "2.10.4" +version = "2.10.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"}, - {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"}, + {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, + {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, ] [package.dependencies] @@ -2916,13 +2916,13 @@ files = [ [[package]] name = "pygments" -version = "2.18.0" +version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, ] [package.extras] @@ -3248,13 +3248,13 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.18.8" +version = "0.18.10" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.18.8-py3-none-any.whl", hash = "sha256:a7c02af6ec9789495b4d19335addabc4d04ab1e0dad3e491c0c9457bbc881100"}, - {file = "ruamel.yaml-0.18.8.tar.gz", hash = "sha256:1b7e14f28a4b8d09f8cd40dca158852db9b22ac84f22da5bb711def35cb5c548"}, + {file = "ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1"}, + {file = "ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58"}, ] [package.dependencies] diff --git a/precog/miners/miner.py b/precog/miners/miner.py index a603728..69faac9 100644 --- a/precog/miners/miner.py +++ b/precog/miners/miner.py @@ -1,3 +1,4 @@ +import argparse import asyncio import importlib import time @@ -10,8 +11,7 @@ from precog.protocol import Challenge from precog.utils.bittensor import print_info, setup_bittensor_objects -from precog.utils.classes import Config -from precog.utils.general import parse_arguments +from precog.utils.config import config class Miner: @@ -20,8 +20,6 @@ class Miner: """ def __init__(self, config=None): - args = parse_arguments() - config = Config(args) self.forward_module = importlib.import_module(f"precog.miners.{config.forward_function}") self.config = config self.config.neuron.type = "Miner" @@ -216,7 +214,7 @@ def _to_seconds(self, nano: int) -> int: # Run the miner if __name__ == "__main__": - args = parse_arguments() - config = Config(args) + parser = argparse.ArgumentParser() + config = config(parser, neuron_type="miner") miner = Miner(config=config) miner.loop.run_forever() diff --git a/precog/utils/bittensor.py b/precog/utils/bittensor.py index df6080c..b283c0f 100644 --- a/precog/utils/bittensor.py +++ b/precog/utils/bittensor.py @@ -20,38 +20,19 @@ def setup_bittensor_objects(self): )[1] else: # if chain endpoint is set, overwrite network arg - self.config.subtensor.network = self.config.subtensor.chain_endpoint + if "test" in self.config.subtensor.chain_endpoint: + self.config.subtensor.network = "test" + elif "finney" in self.config.subtensor.chain_endpoint: + self.config.subtensor.network = "finney" + else: + self.config.subtensor.network = self.config.subtensor.chain_endpoint # Initialize subtensor. self.subtensor = bt.subtensor(config=self.config, network=self.config.subtensor.chain_endpoint) self.metagraph = self.subtensor.metagraph(self.config.netuid) self.wallet = bt.wallet(config=self.config) self.dendrite = bt.dendrite(wallet=self.wallet) + self.axon = bt.axon(wallet=self.wallet, config=self.config, port=self.config.axon.port) - # Initialize axon config - axon_config = bt.axon.config() - axon_config.max_workers = self.config.axon.max_workers - axon_config.port = self.config.axon.port - axon_config.ip = self.config.axon.ip - axon_config.external_ip = self.config.axon.external_ip - axon_config.external_port = self.config.axon.external_port - self.config.axon = axon_config - - # Debug prints - bt.logging.debug(f"Axon config - port: {self.config.axon.port}") - bt.logging.debug(f"Axon config - ip: {self.config.axon.ip}") - bt.logging.debug(f"Axon config - external_ip: {self.config.axon.external_ip}") - bt.logging.debug(f"Axon config - external_port: {self.config.axon.external_port}") - bt.logging.debug(f"Axon config - max_workers: {self.config.axon.max_workers}") - - self.axon = bt.axon( - wallet=self.wallet, - config=self.config, - port=self.config.axon.port, - ip=self.config.axon.ip, - external_ip=self.config.axon.external_ip, - external_port=self.config.axon.external_port, - max_workers=self.config.axon.max_workers, - ) # Connect the validator to the network. if self.wallet.hotkey.ss58_address not in self.metagraph.hotkeys: bt.logging.error( @@ -68,6 +49,7 @@ def setup_bittensor_objects(self): ).expanduser() full_path.mkdir(parents=True, exist_ok=True) self.config.full_path = str(full_path) + bt.logging.info(f"Config: {self.config}") def print_info(self) -> None: @@ -83,7 +65,7 @@ def print_info(self) -> None: f"VTrust:{self.metagraph.Tv[self.my_uid]:.3f} | " f"Dividend:{self.metagraph.D[self.my_uid]:.3f} | " f"Emission:{self.metagraph.E[self.my_uid]:.3f} | " - f"Seting weights in {weight_timing} blocks" + f"Setting weights in {weight_timing} blocks" ) elif self.config.neuron.type == "Miner": log = ( diff --git a/precog/utils/classes.py b/precog/utils/classes.py index 4cdd7ba..21ef038 100644 --- a/precog/utils/classes.py +++ b/precog/utils/classes.py @@ -1,58 +1,9 @@ -import argparse from datetime import datetime, timedelta from typing import List from precog.utils.timestamp import get_now, get_timezone, round_minute_down, to_datetime -class Config: - def __init__(self, args): - # Add command-line arguments to the Config object - for key, value in vars(args).items(): - setattr(self, key, value) - - def get(self, key, default=None): - return getattr(self, key, default) - - def to_str(self): - arguments = "" - for key, value in vars(self).items(): - if isinstance(value, bool): - if value: - arguments = arguments + f" --{key}" - else: - try: - if isinstance(value, NestedNamespace): - for key_2, value_2 in vars(value).items(): - if isinstance(value_2, bool): - if value_2: - arguments = arguments + f" --{key}.{key_2}" - else: - arguments = arguments + f" --{key}.{key_2} {value_2}" - else: - arguments = arguments + (f" --{key} {value}") - except Exception: - continue - return arguments - - -class NestedNamespace(argparse.Namespace): - def __setattr__(self, name, value): - if "." in name: - group, name = name.split(".", 1) - ns = getattr(self, group, NestedNamespace()) - setattr(ns, name, value) - self.__dict__[group] = ns - else: - self.__dict__[name] = value - - def get(self, key, default=None): - if "." in key: - group, key = key.split(".", 1) - return getattr(self, group, NestedNamespace()).get(key, default) - return self.__dict__.get(key, default) - - class MinerHistory: """This class is used to store miner predictions along with their timestamps. Allows for easy formatting, filtering, and lookup of predictions by timestamp. diff --git a/precog/utils/config.py b/precog/utils/config.py new file mode 100644 index 0000000..95fd895 --- /dev/null +++ b/precog/utils/config.py @@ -0,0 +1,281 @@ +import argparse +import os +import subprocess +from typing import Optional + +import bittensor as bt + + +def is_cuda_available(): + try: + output = subprocess.check_output(["nvidia-smi", "-L"], stderr=subprocess.STDOUT) + if "NVIDIA" in output.decode("utf-8"): + return "cuda" + except Exception: + pass + try: + output = subprocess.check_output(["nvcc", "--version"]).decode("utf-8") + if "release" in output: + return "cuda" + except Exception: + pass + return "cpu" + + +def check_config(cls, config: "bt.Config"): + r"""Checks/validates the config namespace object.""" + bt.logging.check_config(config) + + full_path = os.path.expanduser( + "{}/{}/{}/netuid{}/{}".format( + config.logging.logging_dir, # TODO: change from ~/.bittensor/miners to ~/.bittensor/neurons + config.wallet.name, + config.wallet.hotkey, + config.netuid, + config.neuron.name, + ) + ) + print("full path:", full_path) + config.neuron.full_path = os.path.expanduser(full_path) + if not os.path.exists(config.neuron.full_path): + os.makedirs(config.neuron.full_path, exist_ok=True) + + +def add_args(parser): + """ + Adds relevant arguments to the parser for operation. + """ + + parser.add_argument("--netuid", type=int, help="Subnet netuid", default=1) + + parser.add_argument( + "--neuron.device", + type=str, + help="Device to run on.", + default=is_cuda_available(), + ) + + parser.add_argument( + "--neuron.epoch_length", + type=int, + help="The default epoch length (how often we set weights, measured in 12 second blocks).", + default=100, + ) + + parser.add_argument( + "--mock", + action="store_true", + help="Mock neuron and all network components.", + default=False, + ) + + parser.add_argument( + "--neuron.events_retention_size", + type=str, + help="Events retention size.", + default=2 * 1024 * 1024 * 1024, # 2 GB + ) + + parser.add_argument( + "--neuron.dont_save_events", + action="store_true", + help="If set, we dont save events to a log file.", + default=False, + ) + + parser.add_argument( + "--wandb.off", + action="store_true", + help="Turn off wandb.", + default=False, + ) + + parser.add_argument( + "--wandb.offline", + action="store_true", + help="Runs wandb in offline mode.", + default=False, + ) + + parser.add_argument( + "--wandb.notes", + type=str, + help="Notes to add to the wandb run.", + default="", + ) + + parser.add_argument("--logging.level", type=str, choices=["info", "debug", "trace"], default="info") + + parser.add_argument("--print_cadence", type=float, default=12, help="how often to print stats (seconds)") + + parser.add_argument("--autoupdate", action="store_true", dest="autoupdate") + + +def add_miner_args(parser): + """Add miner specific arguments to the parser.""" + + parser.add_argument( + "--neuron.name", + type=str, + help="Trials for this neuron go in neuron.root / (wallet_cold - wallet_hot) / neuron.name. ", + default="miner", + ) + + parser.add_argument( + "--blacklist.force_validator_permit", + action="store_true", + help="If set, we will force incoming requests to have a permit.", + default=False, + ) + + parser.add_argument( + "--blacklist.allow_non_registered", + action="store_true", + help="If set, miners will accept queries from non registered entities. (Dangerous!)", + default=False, + ) + + parser.add_argument( + "--wandb.project_name", + type=str, + default="template-miners", + help="Wandb project to log to.", + ) + + parser.add_argument( + "--wandb.entity", + type=str, + default="opentensor-dev", + help="Wandb entity to log to.", + ) + + parser.add_argument( + "--forward_function", + type=str, + default="base_miner", + help="name of the forward function to use", + ) + + +def add_validator_args(parser): + """Add validator specific arguments to the parser.""" + + parser.add_argument( + "--neuron.name", + type=str, + help="Trials for this neuron go in neuron.root / (wallet_cold - wallet_hot) / neuron.name. ", + default="validator", + ) + + parser.add_argument( + "--neuron.timeout", + type=float, + help="The timeout for each forward call in seconds.", + default=10, + ) + + parser.add_argument( + "--neuron.num_concurrent_forwards", + type=int, + help="The number of concurrent forwards running at any time.", + default=1, + ) + + parser.add_argument( + "--neuron.sample_size", + type=int, + help="The number of miners to query in a single step.", + default=50, + ) + + parser.add_argument( + "--neuron.disable_set_weights", + action="store_true", + help="Disables setting weights.", + default=False, + ) + + parser.add_argument( + "--neuron.moving_average_alpha", + type=float, + help="Moving average alpha parameter, how much to add of the new observation.", + default=0.1, + ) + + parser.add_argument( + "--neuron.axon_off", + "--axon_off", + action="store_true", + # Note: the validator needs to serve an Axon with their IP or they may + # be blacklisted by the firewall of serving peers on the network. + help="Set this flag to not attempt to serve an Axon.", + default=False, + ) + + parser.add_argument( + "--neuron.vpermit_tao_limit", + type=int, + help="The maximum number of TAO allowed to query a validator with a vpermit.", + default=4096, + ) + + parser.add_argument( + "--wandb.project_name", + type=str, + help="The name of the project where you are sending the new run.", + default="template-validators", + ) + + parser.add_argument( + "--wandb.entity", + type=str, + help="The name of the project where you are sending the new run.", + default="opentensor-dev", + ) + + parser.add_argument("--prediction_interval", type=int, default=5) + + parser.add_argument("--N_TIMEPOINTS", type=int, default=12) + + parser.add_argument("--reset_state", action="store_true", dest="reset_state", help="Overwrites the state file") + + +def to_string(bt_config: bt.Config): + string = "" + for key, value in bt_config.items(): + ignore = key == "__is_set" + if ignore: + continue + if isinstance(value, bool): + string += f"--{key} " + elif isinstance(value, bt.core.config.Config): + for key_2, value_2 in value.items(): + ignore = key_2 == "__is_set" + if ignore: + continue + elif isinstance(value_2, bool): + if value_2: + string += f"--{key}.{key_2} " + else: + string += f"--{key}.{key_2} {value_2} " + else: + string += f"--{key} {value} " + return string.strip() + + +def config(parser: Optional[argparse.ArgumentParser] = argparse.ArgumentParser(), neuron_type: str = "validator"): + """ + Returns the configuration object specific to this miner or validator after adding relevant arguments. + """ + bt.wallet.add_args(parser) + bt.subtensor.add_args(parser) + bt.logging.add_args(parser) + bt.axon.add_args(parser) + add_args(parser) + if neuron_type == "validator": + add_validator_args(parser) + parser.add_argument("--neuron.type", type=str, default="Validator") + elif neuron_type == "miner": + add_miner_args(parser) + parser.add_argument("--neuron.type", type=str, default="Miner") + return bt.config(parser) diff --git a/precog/utils/general.py b/precog/utils/general.py index 7888be6..975ea6e 100644 --- a/precog/utils/general.py +++ b/precog/utils/general.py @@ -1,4 +1,3 @@ -import argparse import asyncio import re import time @@ -10,67 +9,6 @@ from numpy import argsort, array, concatenate, cumsum, empty_like from pandas import DataFrame -from precog.utils.classes import NestedNamespace - - -def parse_arguments(parser: Optional[argparse.ArgumentParser] = None): - """Used to overwrite defaults when params are passed into the script. - - Args: - parser (Optional[argparse.ArgumentParser], optional): _description_. Default arguments shown below. - - Example: - >>> python3 -m start_miner.py --netuid 2 - >>> args = parse_arguments() - >>> print(args.subtensor.chain_endpoint) - - Returns: - namespace (NestedNamespace): Returns a nested arparse.namespace object which contains all the arguments - """ - if parser is None: - parser = argparse.ArgumentParser(description="Configuration") - parser.add_argument( - "--subtensor.chain_endpoint", type=str, default=None - ) # for testnet: wss://test.finney.opentensor.ai:443 - parser.add_argument( - "--subtensor.network", - choices=["finney", "test", "local"], - default="finney", - ) - parser.add_argument("--wallet.name", type=str, default="default", help="Coldkey name") - parser.add_argument("--wallet.hotkey", type=str, default="default", help="Hotkey name") - parser.add_argument("--netuid", type=int, default=1, help="Subnet netuid") - parser.add_argument("--neuron.name", type=str, default="validator", help="What to call this process") - parser.add_argument( - "--neuron.type", - type=str, - choices=["Validator", "Miner"], - default="Validator", - help="What type of neuron this is", - ) - parser.add_argument("--axon.port", type=int, default=8000) - parser.add_argument("--axon.ip", type=str, default="127.0.0.1") - parser.add_argument("--axon.external_ip", type=str, default="127.0.0.1") - parser.add_argument("--axon.external_port", type=int, default=8000) - parser.add_argument("--axon.max_workers", type=int, default=10) - parser.add_argument("--logging.level", type=str, choices=["info", "debug", "trace"], default="info") - parser.add_argument("--logging.logging_dir", type=str, default="~/.bittensor/validators") - parser.add_argument( - "--blacklist.force_validator_permit", action="store_false", dest="blacklist.force_validator_permit" - ) - parser.add_argument("--blacklist.allow_non_registered", action="store_true", dest="blacklist.allow_non_registered") - parser.add_argument("--autoupdate", action="store_true", dest="autoupdate") - parser.add_argument("--alpha", type=float, default=0.1) - parser.add_argument("--prediction_interval", type=int, default=5) - parser.add_argument("--N_TIMEPOINTS", type=int, default=12) - parser.add_argument("--vpermit_tao_limit", type=int, default=1024) - parser.add_argument("--wandb_on", action="store_true", dest="wandb_on") - parser.add_argument("--reset_state", action="store_true", dest="reset_state", help="Overwrites the state file") - parser.add_argument("--timeout", type=int, default=16, help="allowable nonce delay time (seconds)") - parser.add_argument("--print_cadence", type=float, default=12, help="how often to print stats (seconds)") - parser.add_argument("--forward_function", type=str, default="forward", help="name of the forward function to use") - return parser.parse_args(namespace=NestedNamespace()) - def get_version() -> Optional[str]: """Pulls the version of the precog-subnet repo from GitHub. diff --git a/precog/validators/validator.py b/precog/validators/validator.py index b8bf9c3..4a4c16a 100755 --- a/precog/validators/validator.py +++ b/precog/validators/validator.py @@ -1,16 +1,14 @@ +import argparse import asyncio from pathlib import Path -from precog.utils.classes import Config -from precog.utils.general import parse_arguments +from precog.utils.config import config from precog.validators.weight_setter import weight_setter class Validator: - def __init__(self): - args = parse_arguments() - self.config = Config(args) - self.config.neuron.type = "Validator" + def __init__(self, config): + self.config = config full_path = Path( f"{self.config.logging.logging_dir}/{self.config.wallet.name}/{self.config.wallet.hotkey}/netuid{self.config.netuid}/validator" ).expanduser() @@ -28,5 +26,7 @@ async def reset_instance(self): # Run the validator. if __name__ == "__main__": - validator = Validator() + parser = argparse.ArgumentParser() + config = config(parser, neuron_type="validator") + validator = Validator(config) asyncio.run(validator.main()) diff --git a/precog/validators/weight_setter.py b/precog/validators/weight_setter.py index fe0b99c..c8cc71c 100755 --- a/precog/validators/weight_setter.py +++ b/precog/validators/weight_setter.py @@ -32,7 +32,7 @@ def __init__(self, config=None, loop=None): f"Running validator for subnet: {self.config.netuid} on network: {self.config.subtensor.network}" ) self.available_uids = asyncio.run(self.get_available_uids()) - self.hotkeys = self.metagraph.hotkeys + self.hotkeys = {uid: value for uid, value in enumerate(self.metagraph.hotkeys)} if self.config.reset_state: self.scores = [0.0] * len(self.metagraph.S) self.moving_average_scores = {uid: 0 for uid in self.metagraph.uids} @@ -44,7 +44,7 @@ def __init__(self, config=None, loop=None): self.blocks_since_last_update = self.subtensor.blocks_since_last_update( netuid=self.config.netuid, uid=self.my_uid ) - if self.config.wandb_on: + if not self.config.wandb.off: setup_wandb(self) self.stop_event = asyncio.Event() bt.logging.info("Setup complete, starting loop") @@ -83,26 +83,67 @@ def __reset_instance__(self): async def get_available_uids(self): miner_uids = [] for uid in range(len(self.metagraph.S)): - uid_is_available = check_uid_availability(self.metagraph, uid, self.config.vpermit_tao_limit) + uid_is_available = check_uid_availability(self.metagraph, uid, self.config.neuron.vpermit_tao_limit) if uid_is_available: miner_uids.append(uid) return miner_uids async def resync_metagraph(self): - """Resyncs the metagraph and updates the hotkeys and moving averages based on the new metagraph.""" + """Resyncs the metagraph and updates the hotkeys, available UIDs, and MinerHistory. + Ensures all data structures remain in sync.""" + # Resync subtensor and metagraph self.subtensor = bt.subtensor(config=self.config, network=self.config.subtensor.chain_endpoint) bt.logging.info("Syncing Metagraph...") self.metagraph.sync(subtensor=self.subtensor) bt.logging.info("Metagraph updated, re-syncing hotkeys, dendrite pool and moving averages") - # Zero out all hotkeys that have been replaced. + + # Get current state for logging + old_uids = set(self.available_uids) + old_history = set(self.MinerHistory.keys()) + bt.logging.debug(f"Before sync - Available UIDs: {old_uids}") + bt.logging.debug(f"Before sync - MinerHistory keys: {old_history}") + + # Update available UIDs self.available_uids = asyncio.run(self.get_available_uids()) + new_uids = set(self.available_uids) + + # Process hotkey changes for uid, hotkey in enumerate(self.metagraph.hotkeys): - if (uid not in self.MinerHistory and uid in self.available_uids) or self.hotkeys[uid] != hotkey: + new_miner = uid in new_uids and uid not in old_uids + if not new_miner: + replaced_miner = self.hotkeys[uid] != hotkey + else: + replaced_miner = False + + if new_miner or replaced_miner: bt.logging.info(f"Replacing hotkey on {uid} with {self.metagraph.hotkeys[uid]}") - self.hotkeys = self.metagraph.hotkeys + self.moving_average_scores[uid] = 0 + if uid in new_uids: # Only create history for available UIDs + self.MinerHistory[uid] = MinerHistory(uid, timezone=self.timezone) + + # Update hotkeys dictionary + self.hotkeys = {uid: value for uid, value in enumerate(self.metagraph.hotkeys)} + + # Ensure all available UIDs have MinerHistory entries + for uid in self.available_uids: + if uid not in self.MinerHistory: + bt.logging.info(f"Creating new MinerHistory for available UID {uid}") self.MinerHistory[uid] = MinerHistory(uid, timezone=self.timezone) self.moving_average_scores[uid] = 0 - self.scores = list(self.moving_average_scores.values()) + + # Clean up old MinerHistory entries + for uid in list(self.MinerHistory.keys()): + if uid not in new_uids: + bt.logging.info(f"Removing MinerHistory for inactive UID {uid}") + del self.MinerHistory[uid] + + # Update scores list + self.scores = list(self.moving_average_scores.values()) + + bt.logging.debug(f"After sync - Available UIDs: {new_uids}") + bt.logging.debug(f"After sync - MinerHistory keys: {set(self.MinerHistory.keys())}") + + # Save updated state self.save_state() def query_miners(self): @@ -124,7 +165,14 @@ async def set_weights(self): self.current_block = func_with_retry(self.subtensor.get_current_block) except Exception as e: bt.logging.error(f"Failed to get current block with error {e}, skipping block update") + return + if self.blocks_since_last_update >= self.hyperparameters.weights_rate_limit: + for uid in self.available_uids: + if uid not in self.moving_average_scores: + bt.logging.debug(f"Initializing score for new UID: {uid}") + self.moving_average_scores[uid] = 0.0 + uids = array(self.available_uids) weights = [self.moving_average_scores[uid] for uid in self.available_uids] if not weights: @@ -169,17 +217,35 @@ async def scheduled_prediction_request(self): if is_query_time(self.prediction_interval, self.timestamp) or query_lag >= 60 * self.prediction_interval: responses, self.timestamp = self.query_miners() try: + bt.logging.debug(f"Processing responses for UIDs: {self.available_uids}") + bt.logging.debug(f"Number of responses: {len(responses)}") + for uid, response in zip(self.available_uids, responses): + bt.logging.debug(f"Response from UID {uid}: {response}") + rewards = calc_rewards(self, responses=responses) + # Adjust the scores based on responses from miners and update moving average. for i, value in zip(self.available_uids, rewards): - self.moving_average_scores[i] = (1 - self.config.alpha) * self.moving_average_scores[ - i - ] + self.config.alpha * value + self.moving_average_scores[i] = ( + 1 - self.config.neuron.moving_average_alpha + ) * self.moving_average_scores[i] + self.config.neuron.moving_average_alpha * value self.scores = list(self.moving_average_scores.values()) - if self.config.wandb_on: + if not self.config.wandb.off: log_wandb(responses, rewards, self.available_uids) except Exception as e: - bt.logging.error(f"Failed to calculate rewards with error: {e}") + import traceback + + bt.logging.error(f"Failed to calculate rewards with error: {str(e)}") + bt.logging.error(f"Error type: {type(e)}") + bt.logging.error("Full traceback:") + bt.logging.error(traceback.format_exc()) + bt.logging.error(f"Available UIDs: {self.available_uids}") + bt.logging.error(f"Response count: {len(responses)}") + bt.logging.error(f"MinerHistory keys: {list(self.MinerHistory.keys())}") + bt.logging.error(f"Full MinerHistory: {self.MinerHistory}") + for uid, response in zip(self.available_uids, responses): + bt.logging.error(f"UID {uid} response status: {getattr(response, 'status_code', 'unknown')}") + bt.logging.error(f"UID {uid} response type: {type(response)}") else: print_info(self) diff --git a/pyproject.toml b/pyproject.toml index 4d70659..79819cd 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "precog" -version = "0.3.0" +version = "1.0.0" description = "Bitcoin Price Prediction Subnet" authors = ["Coin Metrics", "Yuma Group"] readme = "README.md" diff --git a/start_miner.py b/start_miner.py deleted file mode 100644 index 82ec596..0000000 --- a/start_miner.py +++ /dev/null @@ -1,22 +0,0 @@ -import subprocess - -from precog.utils.classes import Config -from precog.utils.general import parse_arguments - - -def main(config): - start_command = ["pm2", "start", "--name", f"{config.neuron.name}"] - arguments = "python3 -m precog.miners.miner" + config.to_str() - - start_command.append(arguments) - subprocess.run(start_command) - - -if __name__ == "__main__": - args = parse_arguments() - config = Config(args) - config.neuron.type = "Miner" - try: - main(config) - except Exception as e: - print(e) diff --git a/start_validator.py b/start_validator.py deleted file mode 100644 index c15a24e..0000000 --- a/start_validator.py +++ /dev/null @@ -1,42 +0,0 @@ -import subprocess -import time - -import precog -from precog.utils.classes import Config -from precog.utils.general import get_version, parse_arguments - -webhook_url = "" -current_version = precog.__version__ - - -def update_and_restart(config): - global current_version - start_command = ["pm2", "start", "--name", f"{config.neuron.name}"] - arguments = "python3 -m precog.validators.validator" + config.to_str() - - start_command.append(arguments) - subprocess.run(start_command) - if config.autoupdate: - while True: - latest_version = get_version() - print(f"Current version: {current_version}") - print(f"Latest version: {latest_version}") - if current_version != latest_version and latest_version is not None: - print("Updating to the latest version...") - subprocess.run(["pm2", "delete", config.neuron.name]) - subprocess.run(["git", "reset", "--hard"]) - subprocess.run(["git", "pull"]) - subprocess.run(["pip", "install", "-e", "."]) - subprocess.run(start_command) - current_version = latest_version - print("All up to date!") - time.sleep(300) - - -if __name__ == "__main__": - args = parse_arguments() - config = Config(args) - try: - update_and_restart(config) - except Exception as e: - print(e) diff --git a/tests/test_package.py b/tests/test_package.py index d94327e..89b0eb0 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -16,4 +16,4 @@ def setUp(self): def test_package_version(self): # Check that version is as expected # Must update to increment package version successfully - self.assertEqual(__version__, "0.3.0") + self.assertEqual(__version__, "1.0.0")