Files
aitbc/cli/handlers/market.py
aitbc e60cc3226c
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 9s
Blockchain Synchronization Verification / sync-verification (push) Failing after 1s
CLI Tests / test-cli (push) Failing after 3s
Documentation Validation / validate-docs (push) Successful in 6s
Documentation Validation / validate-policies-strict (push) Successful in 2s
Integration Tests / test-service-integration (push) Successful in 40s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 1s
P2P Network Verification / p2p-verification (push) Successful in 2s
Production Tests / Production Integration Tests (push) Successful in 21s
Python Tests / test-python (push) Successful in 13s
Security Scanning / security-scan (push) Failing after 46s
Smart Contract Tests / test-solidity (map[name:aitbc-token path:packages/solidity/aitbc-token]) (push) Successful in 17s
Smart Contract Tests / lint-solidity (push) Successful in 10s
Add sys import to test files and remove obsolete integration tests
- Add sys import to 29 test files across agent-coordinator, blockchain-event-bridge, blockchain-node, and coordinator-api
- Remove apps/blockchain-event-bridge/tests/test_integration.py (obsolete bridge integration tests)
- Remove apps/coordinator-api/tests/test_integration.py (obsolete API integration tests)
- Implement GPU registration in marketplace_gpu.py with GPURegistry model persistence
2026-04-23 16:43:17 +02:00

285 lines
11 KiB
Python

"""Marketplace command handlers."""
import json
import sys
import requests
def handle_market_listings(args, default_coordinator_url, output_format, render_mapping):
"""Handle marketplace listings command."""
coordinator_url = getattr(args, 'coordinator_url', default_coordinator_url)
chain_id = getattr(args, "chain_id", None)
print(f"Getting marketplace listings from {coordinator_url}...")
try:
params = {}
if chain_id:
params["chain_id"] = chain_id
response = requests.get(f"{coordinator_url}/v1/marketplace/gpu/list", params=params, timeout=10)
if response.status_code == 200:
listings = response.json()
if output_format(args) == "json":
print(json.dumps(listings, indent=2))
else:
print("Marketplace listings:")
if isinstance(listings, list):
if listings:
for listing in listings:
print(f" - ID: {listing.get('id', 'N/A')}")
print(f" Model: {listing.get('model', 'N/A')}")
print(f" Price: ${listing.get('price_per_hour', 0)}/hour")
print(f" Status: {listing.get('status', 'N/A')}")
else:
print(" No GPU listings found")
else:
render_mapping("Listings:", listings)
else:
print(f"Query failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error getting listings: {e}")
sys.exit(1)
def handle_market_create(args, default_coordinator_url, read_password, render_mapping):
"""Handle marketplace create command."""
coordinator_url = getattr(args, 'coordinator_url', default_coordinator_url)
chain_id = getattr(args, "chain_id", None)
if not args.wallet or not args.item_type or not args.price:
print("Error: --wallet, --type, and --price are required")
sys.exit(1)
# Get auth headers
password = read_password(args)
from ..keystore_auth import get_auth_headers
headers = get_auth_headers(args.wallet, password, args.password_file)
listing_data = {
"wallet": args.wallet,
"item_type": args.item_type,
"price": args.price,
"description": getattr(args, "description", ""),
}
if chain_id:
listing_data["chain_id"] = chain_id
print(f"Creating marketplace listing on {coordinator_url}...")
try:
response = requests.post(f"{coordinator_url}/v1/marketplace/create", json=listing_data, headers=headers, timeout=30)
if response.status_code == 200:
result = response.json()
print("Listing created successfully")
render_mapping("Listing:", result)
else:
print(f"Creation failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error creating listing: {e}")
sys.exit(1)
def handle_market_get(args, default_rpc_url):
"""Handle marketplace get command."""
rpc_url = args.rpc_url or default_rpc_url
chain_id = getattr(args, "chain_id", None)
if not args.listing_id:
print("Error: --listing-id is required")
sys.exit(1)
print(f"Getting listing {args.listing_id} from {rpc_url}...")
try:
import requests
response = requests.get(f"{rpc_url}/marketplace/get/{args.listing_id}", timeout=10)
if response.status_code == 200:
listing = response.json()
print(json.dumps(listing, indent=2))
else:
print(f"Query failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error getting listing: {e}")
sys.exit(1)
def handle_market_delete(args, default_coordinator_url, read_password, render_mapping):
"""Handle marketplace delete command."""
coordinator_url = getattr(args, 'coordinator_url', default_coordinator_url)
chain_id = getattr(args, "chain_id", None)
if not args.listing_id or not args.wallet:
print("Error: --listing-id and --wallet are required")
sys.exit(1)
# Get auth headers
password = read_password(args)
from ..keystore_auth import get_auth_headers
headers = get_auth_headers(args.wallet, password, args.password_file)
delete_data = {
"listing_id": args.listing_id,
"wallet": args.wallet,
}
if chain_id:
delete_data["chain_id"] = chain_id
print(f"Deleting listing {args.listing_id} on {coordinator_url}...")
try:
response = requests.delete(f"{coordinator_url}/v1/marketplace/delete", json=delete_data, headers=headers, timeout=30)
if response.status_code == 200:
result = response.json()
print("Listing deleted successfully")
render_mapping("Delete result:", result)
else:
print(f"Deletion failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error deleting listing: {e}")
sys.exit(1)
def handle_market_gpu_register(args, default_coordinator_url):
"""Handle GPU registration command with nvidia-smi auto-detection."""
coordinator_url = getattr(args, 'coordinator_url', default_coordinator_url)
# Auto-detect GPU specs from nvidia-smi
gpu_name = args.name
memory_gb = args.memory
compute_capability = getattr(args, "compute_capability", None)
if not gpu_name or memory_gb is None:
print("Auto-detecting GPU specifications from nvidia-smi...")
try:
import subprocess
result = subprocess.run(
["nvidia-smi", "--query-gpu=name,memory.total,compute_cap", "--format=csv,noheader"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
# Parse output: "NVIDIA GeForce RTX 4060 Ti, 16380 MiB, 8.9"
parts = result.stdout.strip().split(", ")
if len(parts) >= 3:
detected_name = parts[0]
detected_memory = parts[1].strip() # "16380 MiB"
detected_compute = parts[2].strip() # "8.9"
# Convert memory to GB
memory_value = int(detected_memory.split()[0]) # 16380
memory_gb_detected = round(memory_value / 1024, 1) # 16.0
if not gpu_name:
gpu_name = detected_name
print(f" Detected GPU: {gpu_name}")
if memory_gb is None:
memory_gb = memory_gb_detected
print(f" Detected Memory: {memory_gb} GB")
if not compute_capability:
compute_capability = detected_compute
print(f" Detected Compute Capability: {compute_capability}")
else:
print(" Warning: nvidia-smi failed, using manual input or defaults")
except (subprocess.TimeoutExpired, FileNotFoundError, Exception) as e:
print(f" Warning: Could not run nvidia-smi: {e}")
# Fallback to manual input if auto-detection failed
if not gpu_name or memory_gb is None:
print("Error: Could not auto-detect GPU specs. Please provide --name and --memory manually.")
print(" Example: aitbc-cli market gpu register --name 'NVIDIA GeForce RTX 4060 Ti' --memory 16 --price-per-hour 0.05")
sys.exit(1)
if not args.price_per_hour:
print("Error: --price-per-hour is required")
sys.exit(1)
# Build GPU specs
gpu_specs = {
"name": gpu_name,
"memory_gb": memory_gb,
"cuda_cores": getattr(args, "cuda_cores", None),
"compute_capability": compute_capability,
"price_per_hour": args.price_per_hour,
"description": getattr(args, "description", ""),
"miner_id": getattr(args, "miner_id", "default_miner"),
"registered_at": __import__("datetime").datetime.now().isoformat()
}
print(f"Registering GPU on {coordinator_url}...")
try:
response = requests.post(
f"{coordinator_url}/v1/marketplace/gpu/register",
headers={
"Content-Type": "application/json",
"X-Miner-ID": gpu_specs["miner_id"]
},
json={"gpu": gpu_specs},
timeout=30
)
if response.status_code in (200, 201):
result = response.json()
print(f"GPU registered successfully: {result.get('gpu_id', 'N/A')}")
from ..utils import render_mapping
render_mapping("Registration result:", result)
else:
print(f"Registration failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error registering GPU: {e}")
sys.exit(1)
def handle_market_gpu_list(args, default_coordinator_url, output_format):
"""Handle GPU list command."""
coordinator_url = getattr(args, 'coordinator_url', default_coordinator_url)
print(f"Listing GPUs from {coordinator_url}...")
try:
params = {}
if getattr(args, "available", None):
params["available"] = True
if getattr(args, "price_max", None):
params["price_max"] = args.price_max
if getattr(args, "region", None):
params["region"] = args.region
if getattr(args, "model", None):
params["model"] = args.model
if getattr(args, "limit", None):
params["limit"] = args.limit
response = requests.get(f"{coordinator_url}/v1/marketplace/gpu/list", params=params, timeout=10)
if response.status_code == 200:
gpus = response.json()
if output_format(args) == "json":
print(json.dumps(gpus, indent=2))
else:
print("GPU Listings:")
if isinstance(gpus, list):
if gpus:
for gpu in gpus:
print(f" - ID: {gpu.get('id', 'N/A')}")
print(f" Model: {gpu.get('model', 'N/A')}")
print(f" Memory: {gpu.get('memory_gb', 'N/A')} GB")
print(f" Price: ${gpu.get('price_per_hour', 0)}/hour")
print(f" Status: {gpu.get('status', 'N/A')}")
print(f" Region: {gpu.get('region', 'N/A')}")
else:
print(" No GPUs found")
else:
from ..utils import render_mapping
render_mapping("GPUs:", gpus)
else:
print(f"Query failed: {response.status_code}")
print(f"Error: {response.text}")
sys.exit(1)
except Exception as e:
print(f"Error listing GPUs: {e}")
sys.exit(1)