Skip to content

Commit 0f16484

Browse files
dylanuysbenliang99
andauthored
Release 3.0.11 (#223)
* V3 (#187) * V3 * removing v2 ci pipeline * removing outdated .gitmodules * keeping the noise that sample size of 50 and slight decay of .5 in EMA provides to avoid having any one model completely dominate the subnet. * release 2.2.6 datasets, models, and lora support (#188) * deprecate stable-diffusion-inpainting * .env templates * V3/RGB (#191) * bgr images --> rgb images * proper BGR -> RGB conversion * eradicate all usage of bgr in image challenge flow * extract frames as rgb * skip extraneous rgb conversion * fix deeperforesnics consistency * v2 frame sampling parity + eidon mp4 fix * missing import * handling improper reporting of fps in wembs * correct content-type on miner side * max_fpx setting * improved video metadata extraction * cleaning up ffprobe options * fixing first frame rotation edge case * i2i fix --------- Co-authored-by: Dylan Uys <dylan@bitmind.ai> * V3 frame extraction (#192) * bgr images --> rgb images * proper BGR -> RGB conversion * eradicate all usage of bgr in image challenge flow * extract frames as rgb * skip extraneous rgb conversion * fix deeperforesnics consistency * v2 frame sampling parity + eidon mp4 fix * missing import * handling improper reporting of fps in wembs * correct content-type on miner side * max_fpx setting * improved video metadata extraction * cleaning up ffprobe options * fixing first frame rotation edge case * i2i fix * frame extraction --------- Co-authored-by: Dylan Uys <dylan@bitmind.ai> * setup.sh * removing wandb log call from generator * V3/2.2.9 (#189) * mugshot dataset * black * i2v support and fixed prompt motion enhancement * gen pipeline updates for i2v * fixing prompt indexing * properly handling new prompt dictionary key (task type) * V3/2.2.11 (#190) * mugshot dataset * black * i2v support and fixed prompt motion enhancement * gen pipeline updates for i2v * prompt sanitation + i2v model * more retries for prompt sanitation * fixing truthy tuple assertion * Update min_compute.yml * fixing setup script name in docs * correct script name * updated requirements.txt with bittensor-cli * removing wandb.off * import cleanup * miner substrate thread restart + vali autoupdate test * temporary v3 branch set to test autoudpate * autoupdate update * lower frequency of audoupdate check * autoudpate test * check autoupdate at setp 0 * typo * autoupdate test * dont set weights immediately at startup in case of many restarts * Pyproject toml (#193) * pyproject setup * executable setup.sh * autoupdate test * resetting version after autoupdate tests * Add Hugging Face model access instructions to validator docs; improve logging and fix LLM device mapping for multi-GPU - Added section to Validating.md with instructions for gaining access to required Hugging Face models (FLUX.1-dev, DeepFloyd IF). - Added logging of generation arguments in generation_pipeline.py. - Fix LLM loading for multi-GPU in prompt_generator.py: use device_map and remove .to(self.device) for quantized models. Quantized LLMs must use device_map for correct device placement; calling .to(self.device) causes device mismatch errors. Parse GPU ID from device string for device_map assignment. * fixing image_samples check for i2i * hf_xet requirement * wandb autorestart * Fix: raise error if image is None for i2i/i2v tasks and ensure image is converted from array * fixing wandb autorestart * error log * Update setup.sh to install Node.js 20.x LTS from NodeSource for pm2 compatibility; add doc note for existing validators' Hugging Face access * external port for proxy cuz tensordock rugged us (#196) * incentive doc * Typo * proxy updates * v2 parity encoding (#197) * final autoupdate test * reset version --------- Co-authored-by: Benjamin S Liang <caliangben@gmail.com> Co-authored-by: Dylan Uys <dylan@bitmind.ai> * autoupdate set to main * testing autoupdate on testnet * autoupdate enabled by default * autoudpate testnet * pointing autoupdate at main by default * removing extra state load command * setting back to 360 epoch length * burn for initial v3 release rampup * debug log typo * fixed merge to testnet * Max Frames and Timeout (#203) * fixing wandb cache clean paths (#202) * max frames configuration * fn header update * slight increase to timeout * adding extra metadata to testnet requests for miners (#201) * remove max size arg * Testnet Metadata (#204) * adding extra metadata to testnet requests for miners * adding label and mediatype to testnet metadata * Log Augmentation Parameters (#205) * log augmentation params * braindead typo * bump verison * [testnet] Release 3.0.5 (#207) * fix hotkey check in sync_metagraph * bump version * [testnet] Miner healthchecks (#209) * healthcheck wip * remove old miner health task vars, change miner healtchechk endpoint name * health count logging * fixing query based on health logic * updating healthcheck interval to 10 * removing unecessary lock * use DEFAULT_TIMEOUT * adding blacklisting for bad responses * fixing detect_image fallback * a couple comments * update functionality for generator and proxy * [testnet] Revised Miner Healthcheck (#210) * adding basic health dict to miner tracker * move all request processing logic to epistula module * reflect request processing updates in eval engine * healthy/unhealthy miner uid functions * proxy using simpler miner health from tracker state * new fn signature for score_challenge * [testnet] Image Scraping (#213) * scraper wip * fixing queries with max date set in tbs, also adding placeholder for reverse image search which i cant get to work rn due to captchas * taking first sentence of prompt as initial version of search queries * - Adding specific media scraping interval config - Adding retry logic and error handling to scraping callback * Fixing enum value for output path in scraper * add selenium to requirements * Fixing str treated as enum * cleaning up * increasing media update intervals * [testnet] Safer Miner Prediction History (#214) * centralizing logic for safely getting valid predictions and associated labels * cleaning up * Aura Dataset and Mask W&B Logging (#217) * adding bm-aura-imagegen dataset * log mask as npy artifact * format * increasing media cache refresh default * disabling scrape_new_media_on_interval * warnings for missing keys in miner history state loading * lowering media scraping default (not currenlty used) * remvoning unused healthcheck endpoint in miner * [testnet] Binary Image Proxy Endpoint (#220) * binary image endpoint * adding video endpoint without preprocessing (#221) * fix frames list (should be np array) * adding wan2.1-t2v-1.3B (#222) * removing scraping from this release * version bump * whitespace * putting peft requirement back * ftfy requirement * refactor(models): make Wan2.1 VAE loading lazy - Convert VAE loading to use lazy tuple pattern (fn, args) - Update load_vae to use kwargs for consistency * Remove duplicate function * fix torch_dtype typo for wan * extending window to 200 * imports --------- Co-authored-by: Benjamin S Liang <caliangben@gmail.com> Co-authored-by: Dylan Uys <dylan@bitmind.ai>
1 parent 1a10825 commit 0f16484

File tree

8 files changed

+217
-11
lines changed

8 files changed

+217
-11
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.0.10
1+
3.0.11

bitmind/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "3.0.10"
1+
__version__ = "3.0.11"
22

33
version_split = __version__.split(".")
44
__spec_version__ = (

bitmind/generation/models.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
AutoPipelineForInpainting,
1818
CogView4Pipeline,
1919
CogVideoXImageToVideoPipeline,
20+
WanPipeline,
21+
AutoencoderKLWan
2022
)
2123

2224
from bitmind.generation.model_registry import ModelRegistry
2325
from bitmind.generation.util.model import (
2426
load_hunyuanvideo_transformer,
2527
load_annimatediff_motion_adapter,
28+
load_vae,
2629
JanusWrapper,
2730
)
2831
from bitmind.types import ModelConfig, ModelTask
@@ -255,6 +258,31 @@ def get_text_to_video_models() -> List[ModelConfig]:
255258
List of text-to-video model configurations
256259
"""
257260
return [
261+
ModelConfig(
262+
path="Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
263+
task=ModelTask.TEXT_TO_VIDEO,
264+
pipeline_cls=WanPipeline,
265+
pretrained_args={
266+
"vae": (
267+
load_vae,
268+
{
269+
"vae_cls": AutoencoderKLWan,
270+
"model_id": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
271+
"subfolder": "vae",
272+
"torch_dtype": torch.float32
273+
}
274+
),
275+
"torch_dtype": torch.bfloat16
276+
},
277+
generate_args={
278+
"resolution": [480, 832],
279+
"num_frames": 81,
280+
"guidance_scale": 5.0
281+
},
282+
save_args={"fps": 15},
283+
use_autocast=False,
284+
tags=["wan2.1"]
285+
),
258286
ModelConfig(
259287
path="tencent/HunyuanVideo",
260288
task=ModelTask.TEXT_TO_VIDEO,

bitmind/generation/util/model.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,25 @@
1414
from typing import Any, Dict, Optional
1515

1616

17+
def load_vae(vae_cls, model_id, subfolder, torch_dtype=torch.float32):
18+
"""
19+
Load a VAE model.
20+
21+
Args:
22+
vae_cls: The VAE class to instantiate
23+
model_id: The model ID to load from
24+
subfolder: The subfolder containing the VAE weights
25+
torch_dtype: The torch dtype to use (default: torch.float32)
26+
Returns:
27+
A loaded VAE model
28+
"""
29+
return vae_cls.from_pretrained(
30+
model_id,
31+
subfolder=subfolder,
32+
torch_dtype=torch_dtype
33+
)
34+
35+
1736
def load_hunyuanvideo_transformer(
1837
model_id: str = "tencent/HunyuanVideo",
1938
subfolder: str = "transformer",

bitmind/scoring/eval_engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def _get_rewards_for_challenge(
173173
miner_modality_metrics[modality] = self._empty_metrics()
174174
continue
175175

176-
metrics = self._get_metrics(uid, modality, window=100)
176+
metrics = self._get_metrics(uid, modality, window=200)
177177

178178
binary_weight = self.config.scoring.binary_weight
179179
multiclass_weight = self.config.scoring.multiclass_weight

bitmind/scoring/miner_history.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class MinerHistory:
1515

1616
VERSION = 2
1717

18-
def __init__(self, store_last_n_predictions: int = 100):
18+
def __init__(self, store_last_n_predictions: int = 200):
1919
self.predictions: Dict[int, Dict[Modality, deque]] = {}
2020
self.labels: Dict[int, Dict[Modality, deque]] = {}
2121
self.miner_hotkeys: Dict[int, str] = {}

neurons/proxy.py

Lines changed: 164 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import httpx
1616
import numpy as np
1717
import uvicorn
18+
from bittensor.core.settings import SS58_FORMAT, TYPE_REGISTRY
1819
from cryptography.exceptions import InvalidSignature
1920
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
2021
from fastapi import (
@@ -29,11 +30,12 @@
2930
from fastapi.security import APIKeyHeader
3031
from PIL import Image
3132
from bittensor.core.axon import FastAPIThreadedServer
33+
from substrateinterface import SubstrateInterface
3234

3335
from bitmind.config import MAINNET_UID
3436
from bitmind.encoding import media_to_bytes
3537
from bitmind.epistula import query_miner
36-
from bitmind.metagraph import get_miner_uids
38+
from bitmind.metagraph import get_miner_uids, run_block_callback_thread
3739
from bitmind.scoring.miner_history import MinerHistory
3840
from bitmind.transforms import get_base_transforms
3941
from bitmind.types import Modality, NeuronType
@@ -69,7 +71,7 @@ def process_image(self, b64_image: str) -> np.ndarray:
6971
bt.logging.error(f"Error processing image: {e}")
7072
raise ValueError(f"Failed to process image: {str(e)}")
7173

72-
def process_video(self, video_data: bytes) -> np.ndarray:
74+
def process_video(self, video_data: bytes, transform_frames=True) -> np.ndarray:
7375
"""
7476
Process raw video bytes into frames and preprocess
7577
@@ -105,10 +107,11 @@ def process_video(self, video_data: bytes) -> np.ndarray:
105107
bt.logging.error("No frames extracted from video")
106108
raise ValueError("No frames extracted from video")
107109

108-
transformed_frames = get_base_transforms(self.target_size)(
109-
np.stack(frames)
110-
)
111-
video_bytes, content_type = media_to_bytes(transformed_frames)
110+
frames = np.stack(frames)
111+
if transform_frames:
112+
frames = get_base_transforms(self.target_size)(frames)
113+
114+
video_bytes, content_type = media_to_bytes(frames)
112115
return video_bytes, content_type
113116

114117
except Exception as e:
@@ -266,12 +269,24 @@ def setup_app(self):
266269
methods=["POST"],
267270
dependencies=[Depends(self.verify_auth)],
268271
)
272+
router.add_api_route(
273+
"/forward_image_binary",
274+
self.handle_binary_image_request,
275+
methods=["POST"],
276+
dependencies=[Depends(self.verify_auth)],
277+
)
269278
router.add_api_route(
270279
"/forward_video",
271280
self.handle_video_request,
272281
methods=["POST"],
273282
dependencies=[Depends(self.verify_auth)],
274283
)
284+
router.add_api_route(
285+
"/forward_video_binary",
286+
self.handle_binary_video_request,
287+
methods=["POST"],
288+
dependencies=[Depends(self.verify_auth)],
289+
)
275290
router.add_api_route(
276291
"/healthcheck",
277292
self.healthcheck,
@@ -291,6 +306,69 @@ def setup_app(self):
291306
)
292307
self.fast_api = FastAPIThreadedServer(config=fast_config)
293308

309+
async def handle_binary_image_request(self, request: Request) -> Dict[str, Any]:
310+
"""
311+
Handle raw JPEG image processing requests.
312+
313+
Args:
314+
request: FastAPI request object with binary JPEG image data
315+
316+
Returns:
317+
Dictionary with prediction results
318+
"""
319+
start_time = time.time()
320+
request_id = str(uuid.uuid4())[:8]
321+
bt.logging.trace(f"[{request_id}] Starting binary image request processing")
322+
323+
try:
324+
image_data = await request.body()
325+
if not image_data:
326+
raise HTTPException(
327+
status_code=status.HTTP_400_BAD_REQUEST,
328+
detail="Empty image data",
329+
)
330+
331+
query_start = time.time()
332+
results = await self.query_miners(
333+
media_bytes=image_data,
334+
content_type="image/jpeg",
335+
modality=Modality.IMAGE,
336+
request_id=request_id,
337+
)
338+
bt.logging.debug(
339+
f"[{request_id}] Miners queried in {time.time() - query_start:.2f}s"
340+
)
341+
342+
predictions, uids = self.process_query_results(results)
343+
response = {
344+
"preds": [float(p) for p in predictions],
345+
"fqdn": socket.getfqdn(),
346+
}
347+
348+
# Add rich data if requested
349+
rich_param = request.query_params.get("rich", "").lower()
350+
if rich_param == "true":
351+
response.update(self.get_rich_data(uids))
352+
353+
total_time = time.time() - start_time
354+
bt.logging.debug(
355+
f"[{request_id}] Binary image request processed in {total_time:.2f}s"
356+
)
357+
358+
if len(self.request_times["image"]) >= self.max_request_history:
359+
self.request_times["image"].pop(0)
360+
self.request_times["image"].append(total_time)
361+
362+
return response
363+
364+
except Exception as e:
365+
bt.logging.error(f"[{request_id}] Error processing binary image request: {e}")
366+
bt.logging.error(traceback.format_exc())
367+
raise HTTPException(
368+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
369+
detail=f"Error processing request: {str(e)}",
370+
)
371+
294372
async def handle_image_request(self, request: Request) -> Dict[str, Any]:
295373
"""
296374
Handle image processing requests.
@@ -441,6 +519,86 @@ async def handle_video_request(self, request: Request) -> Dict[str, Any]:
441519
detail=f"Error processing request: {str(e)}",
442520
)
443521

522+
async def handle_binary_video_request(self, request: Request) -> Dict[str, Any]:
523+
"""
524+
Handle video processing requests.
525+
526+
Args:
527+
request: FastAPI request object with form data containing video file
528+
529+
Returns:
530+
Dictionary with prediction results
531+
"""
532+
start_time = time.time()
533+
request_id = str(uuid.uuid4())[:8]
534+
bt.logging.trace(f"[{request_id}] Starting video request processing")
535+
536+
try:
537+
form = await request.form()
538+
if "video" not in form:
539+
raise HTTPException(
540+
status_code=status.HTTP_400_BAD_REQUEST,
541+
detail="Missing 'video' field in form data",
542+
)
543+
544+
video_file = form["video"]
545+
video_data = await video_file.read()
546+
547+
if not video_data:
548+
raise HTTPException(
549+
status_code=status.HTTP_400_BAD_REQUEST, detail="Empty video file"
550+
)
551+
552+
rich_param = form.get("rich", "").lower()
553+
554+
proc_start = time.time()
555+
media_bytes, content_type = await asyncio.to_thread(
556+
self.media_processor.process_video, video_data, transform_frames=False
557+
)
558+
bt.logging.trace(
559+
f"[{request_id}] Video processed in {time.time() - proc_start:.2f}s"
560+
)
561+
562+
query_start = time.time()
563+
results = await self.query_miners(
564+
media_bytes=media_bytes,
565+
content_type=content_type,
566+
modality=Modality.VIDEO,
567+
request_id=request_id,
568+
)
569+
bt.logging.debug(
570+
f"[{request_id}] Miners queried in {time.time() - query_start:.2f}s"
571+
)
572+
573+
predictions, uids = self.process_query_results(results)
574+
response = {
575+
"preds": [float(p) for p in predictions],
576+
"fqdn": socket.getfqdn(),
577+
}
578+
579+
# Add rich data if requested
580+
if rich_param == "true":
581+
response.update(self.get_rich_data(uids))
582+
583+
total_time = time.time() - start_time
584+
bt.logging.debug(
585+
f"[{request_id}] Video request processed in {total_time:.2f}s"
586+
)
587+
588+
if len(self.request_times["video"]) >= self.max_request_history:
589+
self.request_times["video"].pop(0)
590+
self.request_times["video"].append(total_time)
591+
return response
592+
593+
except Exception as e:
594+
bt.logging.error(f"Error processing video request: {e}")
595+
bt.logging.error(traceback.format_exc())
596+
raise HTTPException(
597+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
598+
detail=f"Error processing request: {str(e)}",
599+
)
600+
601+
444602
async def query_miners(
445603
self,
446604
media_bytes: bytes,

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ opencv-python==4.11.0.86
2121
wandb==0.19.9
2222
uvicorn==0.27.1
2323
python-multipart==0.0.20
24-
peft==0.15.0
24+
peft==0.15.0
25+
ftfy==6.3.1

0 commit comments

Comments
 (0)