diff --git a/.env.miner.template b/.env.miner.template
new file mode 100644
index 00000000..0acf7b65
--- /dev/null
+++ b/.env.miner.template
@@ -0,0 +1 @@
+MINER_HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY'
diff --git a/.env.template b/.env.validator.template
similarity index 50%
rename from .env.template
rename to .env.validator.template
index 7bf9ea67..6e0602f5 100644
--- a/.env.template
+++ b/.env.validator.template
@@ -1,10 +1,10 @@
-# Copy the contents on this file to a new file called .env
WANDB_API_KEY='REPLACE_WITH_WANDB_API_KEY'
-MINER_HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY'
+
HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY'
+HF_COLLECTION_SLUG='REPLACE_WITH_HUGGINGFACE_COLLECTION_SLUG'
# Git credentials
GIT_TOKEN='REPLACE_WITH_GIT_TOKEN'
GIT_USERNAME='REPLACE_WITH_GIT_USERNAME'
-GIT_NAME="REPLACE_WITH_GIT_NAME"
-GIT_EMAIL="REPLACE_WITH_GIT_EMAIL"
+GIT_NAME='REPLACE_WITH_GIT_NAME'
+GIT_EMAIL='REPLACE_WITH_GIT_EMAIL'
diff --git a/Makefile b/Makefile
index 8c024aaf..bf7ae621 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,6 @@ netuid = $(localnet_netuid)
network = $(localnet)
logging_level = debug # options= ['info', 'debug', 'trace']
-
################################################################################
# Network Parameters #
################################################################################
@@ -44,6 +43,7 @@ validator:
--netuid $(netuid) \
--logging.$(logging_level)
+
miner:
pm2 start python --name miner -- ./snp_oracle/neurons/miner.py \
--wallet.name $(coldkey) \
@@ -54,5 +54,5 @@ miner:
--logging.$(logging_level) \
--vpermit_tao_limit 2 \
--blacklist.force_validator_permit true \
- --hf_repo_id foundryservices/mining_models \
+ --hf_repo_id your_repo_id \
--model mining_models/base_lstm_new.h5
diff --git a/docs/Release Notes.md b/docs/Release Notes.md
index 83e3e28e..1152329f 100644
--- a/docs/Release Notes.md
+++ b/docs/Release Notes.md
@@ -1,6 +1,13 @@
Release Notes
=============
+3.0.1
+-----
+Released on January 12 2025
+- Improvements in validator README instructions
+- Add flags to opt into additional functionality
+
+
3.0.0
-----
Released on January 10th 2025
diff --git a/docs/miners.md b/docs/miners.md
index 6a3db448..3fa6f80d 100644
--- a/docs/miners.md
+++ b/docs/miners.md
@@ -57,10 +57,10 @@ poetry install
## Configuration
#### Environment Variables
-First copy the `.env.template` file to `.env`
+First copy the `.env.miner.template` file to `.env`
```shell
-cp .env.template .env
+cp .env.miner.template .env
```
Update the `.env` file with your miner's values for the following properties.
diff --git a/docs/validators.md b/docs/validators.md
index 4e4437b1..e15ea6ce 100644
--- a/docs/validators.md
+++ b/docs/validators.md
@@ -49,21 +49,25 @@ poetry install
## Configuration
#### Environment Variables
-First copy the `.env.template` file to `.env`
+First copy the `.env.validator.template` file to `.env`
```shell
-cp .env.template .env
+cp .env.validator.template .env
```
Update the `.env` file with your validator's values.
```text
WANDB_API_KEY='REPLACE_WITH_WANDB_API_KEY'
+
HF_ACCESS_TOKEN='REPLACE_WITH_HUGGINGFACE_ACCESS_KEY'
+HF_COLLECTION_SLUG='REPLACE_WITH_HUGGINGFACE_COLLECTION_SLUG'
+
+(Optional - See Miner Data Upload to Hugging Face section)
GIT_TOKEN='REPLACE_WITH_GIT_TOKEN'
GIT_USERNAME='REPLACE_WITH_GIT_USERNAME'
-GIT_NAME="REPLACE_WITH_GIT_NAME"
-GIT_EMAIL="REPLACE_WITH_GIT_EMAIL"
+GIT_NAME='REPLACE_WITH_GIT_NAME'
+GIT_EMAIL='REPLACE_WITH_GIT_EMAIL'
```
See WandB API Key and HuggingFace setup below.
@@ -76,10 +80,22 @@ Before starting the process, validators would be required to procure a WANDB API
- Finally, run `wandb login` and paste your API key. Now you're all set with weights & biases.
#### HuggingFace Access Token
-A huggingface access token can be procured from the huggingface platform. Follow the steps mentioned here to get your huggingface access token.
+A huggingface access token can be procured from the huggingface platform. Follow the steps mentioned here to get your huggingface access token and add it to the ```HF_ACCESS_TOKEN``` environment variable. Ensure that your access token has all repository permissions and collection permissions checked.
+
+#### HuggingFace Collection Slug
+A Hugging Face collection is where the references to miner models will be stored. In order to create one, follow the steps mentioned here.
+
+Once you have created a collection, copy and paste the collection slug into the ```HF_COLLECTION_SLUG``` environment variable.
+
+#### (Optional) Miner Data Upload to Hugging Face
+Optionally, validators can choose to upload miner data at the end of each day to Hugging Face. The goal of this is to increase the transparency of our subnet. In order to participate, validators will need to create a Hugging Face organization.
+
+Once you have created an organization, pass the organization namespace into the ```--neuron.organization``` argument in the Makefile with your organizations namespace.
+
+To turn on this feature, you will also need to add the ```--neuron.data_upload_on``` argument to the Makefile and set it to ```True```.
-#### Git Access Token
-A git token can be procured from the huggingface platform. Follow the steps mentioned here to get your huggingface access token. Be sure to scope this token to the organization repository. The `username`, `name`, and `email` environment variable properties are all tied to your HuggingFace account.
+#### (Optional) Git Access Token
+A git token can be procured from the huggingface platform. Follow the steps mentioned here to get your huggingface access token. Be sure to scope this token to the organization repository set with the argument above. The `GIT_TOKEN`, `GIT_USERNAME`, `GIT_EMAIL` and `GIT_NAME` environment variable properties are all tied to your HuggingFace account.
## Deploying a Validator
**IMPORTANT**
diff --git a/pyproject.toml b/pyproject.toml
index 9aa64a42..22d21207 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "snp_oracle"
-version = "3.0.0"
+version = "3.0.1"
description = ""
authors = ["Foundry Digital"]
readme = "README.md"
diff --git a/snp_oracle/neurons/validator.py b/snp_oracle/neurons/validator.py
index 29fa3a2e..3ab590be 100644
--- a/snp_oracle/neurons/validator.py
+++ b/snp_oracle/neurons/validator.py
@@ -51,6 +51,7 @@ def __init__(self, config=None):
wandb.init(
project=f"sn{self.config.netuid}-validators",
+ mode="disabled" if not getattr(self.config.neuron, "wandb_on", False) else "online",
entity="foundryservices",
config={
"hotkey": self.wallet.hotkey.ss58_address,
diff --git a/snp_oracle/predictionnet/utils/config.py b/snp_oracle/predictionnet/utils/config.py
index 8930d99a..a44fc885 100644
--- a/snp_oracle/predictionnet/utils/config.py
+++ b/snp_oracle/predictionnet/utils/config.py
@@ -142,7 +142,16 @@ def add_args(cls, parser):
"--neuron.organization",
type=str,
help="HuggingFace organization name for dataset storage",
- default="foundryservices",
+ default="your_hugging_face_org",
+ )
+
+ parser.add_argument("--neuron.wandb_on", type=bool, help="Boolean toggle for wandb integration", default=False)
+
+ parser.add_argument(
+ "--neuron.data_upload_on",
+ type=bool,
+ help="Boolean toggle for validator HuggingFace data upload",
+ default=False,
)
# MINER ONLY CONFIG
diff --git a/snp_oracle/predictionnet/validator/forward.py b/snp_oracle/predictionnet/validator/forward.py
index c5b5ab94..11ad4368 100644
--- a/snp_oracle/predictionnet/validator/forward.py
+++ b/snp_oracle/predictionnet/validator/forward.py
@@ -26,22 +26,27 @@ def can_process_data(response) -> bool:
return all([bool(response.repo_id), bool(response.data), bool(response.decryption_key), bool(response.prediction)])
-async def process_miner_data(response, timestamp: str, organization: str, hotkey: str, uid: int) -> bool:
+async def process_miner_data(
+ response, timestamp: str, organization: str, hotkey: str, uid: int, data_upload_on: bool = False
+) -> bool:
"""
- Verify that miner's data can be decrypted and attempt to store it.
-
+ Verify that miner's data can be decrypted and attempt to store it.
Args:
response: Response from miner containing encrypted data
timestamp: Current timestamp
organization: Organization name for HuggingFace
hotkey: Miner's hotkey for data organization
uid: Miner's UID
-
Returns:
bool: True if data was successfully decrypted, False otherwise
"""
try:
bt.logging.info(f"Processing data from UID {uid}...")
+
+ if not data_upload_on:
+ bt.logging.info(f"Data upload disabled, skipping storage for UID {uid}")
+ return True # Return True since this isn't a failure case
+
dataset_manager = DatasetManager(organization=organization)
data_path = f"{response.repo_id}/{response.data}"
@@ -72,9 +77,13 @@ async def process_miner_data(response, timestamp: str, organization: str, hotkey
return False
-async def handle_market_close(self, dataset_manager: DatasetManager) -> None:
+async def handle_market_close(self, dataset_manager: DatasetManager, data_upload_on: bool) -> None:
"""Handle data management operations when market is closed."""
try:
+ if not data_upload_on:
+ bt.logging.info("Data upload disabled, skipping market close operations")
+ return
+
# Clean up old data
dataset_manager.cleanup_local_storage(days_to_keep=2)
@@ -92,6 +101,22 @@ async def handle_market_close(self, dataset_manager: DatasetManager) -> None:
bt.logging.error(f"Error during market close operations: {str(e)}")
+def log_to_wandb(wandb_on, miner_uids, responses, rewards, decryption_success):
+ if wandb_on:
+ # Log results to wandb
+ wandb_val_log = {
+ "miners_info": {
+ miner_uid: {
+ "miner_response": response.prediction,
+ "miner_reward": reward,
+ "decryption_success": success,
+ }
+ for miner_uid, response, reward, success in zip(miner_uids, responses, rewards, decryption_success)
+ }
+ }
+ wandb.log(wandb_val_log)
+
+
async def forward(self):
"""
The forward function is called by the validator every time step.
@@ -100,33 +125,31 @@ async def forward(self):
"""
ny_timezone = timezone("America/New_York")
current_time_ny = datetime.now(ny_timezone)
- dataset_manager = DatasetManager(organization=self.config.neuron.organization)
daily_ops_done = False
+ dataset_manager = None
+ data_upload_on = getattr(self.config.neuron, "data_upload_on", False)
+
+ if data_upload_on:
+ dataset_manager = DatasetManager(organization=self.config.neuron.organization)
while True:
if await self.is_valid_time():
+ bt.logging.info("Market is open. Begin processes requests")
daily_ops_done = False # Reset flag when market opens
break
-
- if not daily_ops_done:
- await handle_market_close(self, dataset_manager)
+ else:
+ bt.logging.info("Market is closed. Sleeping for 2 minutes...")
+ time.sleep(120) # Sleep for 5 minutes before checking again
+ if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1):
+ self.resync_metagraph()
+ self.set_weights()
+ self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys)
+ current_time_ny = datetime.now(ny_timezone)
+
+ if not daily_ops_done and data_upload_on and dataset_manager:
+ await handle_market_close(self, dataset_manager, data_upload_on)
daily_ops_done = True
- # Check metagraph every hour
- if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1):
- self.resync_metagraph()
- self.set_weights()
- self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys)
- current_time_ny = datetime.now(ny_timezone)
-
- time.sleep(120) # Sleep for 2 minutes
-
- if datetime.now(ny_timezone) - current_time_ny >= timedelta(hours=1):
- self.resync_metagraph()
- self.set_weights()
- self.past_predictions = [full((self.N_TIMEPOINTS, self.N_TIMEPOINTS), nan)] * len(self.hotkeys)
- current_time_ny = datetime.now(ny_timezone)
-
# Get available miner UIDs
miner_uids = []
for uid in range(len(self.metagraph.S)):
@@ -161,6 +184,7 @@ async def forward(self):
organization=self.config.neuron.organization,
hotkey=self.metagraph.hotkeys[uid],
uid=uid,
+ data_upload_on=data_upload_on,
)
decryption_tasks.append(task)
else:
@@ -173,16 +197,10 @@ async def forward(self):
rewards = get_rewards(self, responses=responses, miner_uids=miner_uids)
# Zero out rewards for failed decryption
- rewards = [reward if success else 0 for reward, success in zip(rewards, decryption_success)]
+ # rewards = [reward if success else 0 for reward, success in zip(rewards, decryption_success)]
- # Log results to wandb
- wandb_val_log = {
- "miners_info": {
- miner_uid: {"miner_response": response.prediction, "miner_reward": reward, "decryption_success": success}
- for miner_uid, response, reward, success in zip(miner_uids, responses, rewards, decryption_success)
- }
- }
- wandb.log(wandb_val_log)
+ wandb_on = self.config.neuron.wandb_on
+ log_to_wandb(wandb_on, miner_uids, responses, rewards, decryption_success)
# Log scores and update
bt.logging.info(f"Scored responses: {rewards}")
diff --git a/tests/test_package.py b/tests/test_package.py
index 46c0775d..749dc267 100644
--- a/tests/test_package.py
+++ b/tests/test_package.py
@@ -11,4 +11,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__, "3.0.0")
+ self.assertEqual(__version__, "3.0.1")