chore: enhance .gitignore and remove obsolete documentation files - Reorganize .gitignore with categorized sections for better maintainability - Add comprehensive ignore patterns for Python, Node.js, databases, logs, and build artifacts - Add project-specific ignore rules for coordinator, explorer, and deployment files - Remove outdated documentation: BITCOIN-WALLET-SETUP.md, LOCAL_ASSETS_SUMMARY.md, README-CONTAINER-DEPLOYMENT.md, README-DOMAIN-DEPLOYMENT.md ```
183 lines
4.9 KiB
Python
183 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple GPU Miner Client for AITBC
|
|
Registers GPU with coordinator and sends heartbeats
|
|
"""
|
|
|
|
import json
|
|
import time
|
|
import httpx
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
# Configuration
|
|
COORDINATOR_URL = "http://localhost:8000"
|
|
MINER_ID = "localhost-gpu-miner"
|
|
AUTH_TOKEN = "REDACTED_MINER_KEY"
|
|
HEARTBEAT_INTERVAL = 15
|
|
|
|
# Setup logging
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# GPU capabilities (RTX 4060 Ti)
|
|
GPU_CAPABILITIES = {
|
|
"gpu": {
|
|
"model": "NVIDIA GeForce RTX 4060 Ti",
|
|
"memory_gb": 16,
|
|
"cuda_version": "12.4",
|
|
"compute_capability": "8.9",
|
|
"driver_version": "550.163.01"
|
|
},
|
|
"compute": {
|
|
"type": "GPU",
|
|
"platform": "CUDA",
|
|
"supported_tasks": ["inference", "training", "stable-diffusion", "llama"],
|
|
"max_concurrent_jobs": 1
|
|
}
|
|
}
|
|
|
|
def register_miner():
|
|
"""Register the miner with the coordinator"""
|
|
register_data = {
|
|
"capabilities": GPU_CAPABILITIES,
|
|
"concurrency": 1,
|
|
"region": "localhost"
|
|
}
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {AUTH_TOKEN}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
try:
|
|
response = httpx.post(
|
|
f"{COORDINATOR_URL}/miners/register",
|
|
json=register_data,
|
|
headers=headers,
|
|
timeout=10
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
logger.info(f"Successfully registered miner: {data}")
|
|
return data.get("session_token")
|
|
else:
|
|
logger.error(f"Registration failed: {response.status_code} - {response.text}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error registering miner: {e}")
|
|
return None
|
|
|
|
def send_heartbeat():
|
|
"""Send heartbeat to coordinator"""
|
|
heartbeat_data = {
|
|
"inflight": 0,
|
|
"status": "ONLINE",
|
|
"metadata": {
|
|
"last_seen": datetime.utcnow().isoformat(),
|
|
"gpu_utilization": 9, # Current GPU utilization from nvidia-smi
|
|
"gpu_memory_used": 2682, # MB
|
|
"gpu_temperature": 43
|
|
}
|
|
}
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {AUTH_TOKEN}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
try:
|
|
response = httpx.post(
|
|
f"{COORDINATOR_URL}/miners/heartbeat",
|
|
json=heartbeat_data,
|
|
headers=headers,
|
|
timeout=5
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
logger.info("Heartbeat sent successfully")
|
|
else:
|
|
logger.error(f"Heartbeat failed: {response.status_code} - {response.text}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error sending heartbeat: {e}")
|
|
|
|
def poll_for_jobs():
|
|
"""Poll for available jobs"""
|
|
poll_data = {
|
|
"max_wait_seconds": 5
|
|
}
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {AUTH_TOKEN}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
try:
|
|
response = httpx.post(
|
|
f"{COORDINATOR_URL}/miners/poll",
|
|
json=poll_data,
|
|
headers=headers,
|
|
timeout=10
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
job = response.json()
|
|
logger.info(f"Received job: {job}")
|
|
return job
|
|
elif response.status_code == 204:
|
|
logger.info("No jobs available")
|
|
return None
|
|
else:
|
|
logger.error(f"Poll failed: {response.status_code} - {response.text}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error polling for jobs: {e}")
|
|
return None
|
|
|
|
def main():
|
|
"""Main miner loop"""
|
|
logger.info("Starting GPU Miner Client...")
|
|
|
|
# Register with coordinator
|
|
session_token = register_miner()
|
|
if not session_token:
|
|
logger.error("Failed to register, exiting")
|
|
return
|
|
|
|
logger.info("Miner registered successfully, starting main loop...")
|
|
|
|
# Main loop
|
|
last_heartbeat = 0
|
|
last_poll = 0
|
|
|
|
try:
|
|
while True:
|
|
current_time = time.time()
|
|
|
|
# Send heartbeat
|
|
if current_time - last_heartbeat >= HEARTBEAT_INTERVAL:
|
|
send_heartbeat()
|
|
last_heartbeat = current_time
|
|
|
|
# Poll for jobs
|
|
if current_time - last_poll >= 3: # Poll every 3 seconds
|
|
job = poll_for_jobs()
|
|
if job:
|
|
# TODO: Execute job
|
|
logger.info(f"Would execute job: {job}")
|
|
last_poll = current_time
|
|
|
|
time.sleep(1)
|
|
|
|
except KeyboardInterrupt:
|
|
logger.info("Shutting down miner...")
|
|
except Exception as e:
|
|
logger.error(f"Error in main loop: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|