fix: resolve medium priority CLI design principle violations
MEDIUM PRIORITY FIXES: - Remove subprocess system calls - CLI should not manage system services - Remove hardware queries - Should be separate system monitoring tools CHANGES MADE: - AI service commands now provide setup instructions instead of calling systemctl - GPU registration provides guidance instead of auto-detecting with nvidia-smi - CLI respects user control and doesn't execute system commands - Hardware monitoring delegated to appropriate system tools PRINCIPLES RESTORED: - CLI controls, doesn't run services - CLI is lightweight, not a system management tool - CLI respects user control (no auto-system changes) - Hardware queries delegated to system monitoring tools REMOVED: - systemctl calls for service management - nvidia-smi hardware auto-detection - System-level subprocess calls - Automatic service start/stop functionality IMPROVED: - Service management provides manual instructions - GPU registration requires manual specification - Clear separation of concerns between CLI and system tools
This commit is contained in:
@@ -38,32 +38,27 @@ def status(port, model, provider_wallet, marketplace_url):
|
|||||||
@click.option('--wallet', 'provider_wallet', required=True, help='Provider wallet address (for verification)')
|
@click.option('--wallet', 'provider_wallet', required=True, help='Provider wallet address (for verification)')
|
||||||
@click.option('--marketplace-url', default='http://127.0.0.1:8014', help='Marketplace API base URL')
|
@click.option('--marketplace-url', default='http://127.0.0.1:8014', help='Marketplace API base URL')
|
||||||
def start(port, model, provider_wallet, marketplace_url):
|
def start(port, model, provider_wallet, marketplace_url):
|
||||||
"""Start AI provider service (systemd)."""
|
"""Start AI provider service - provides setup instructions"""
|
||||||
click.echo(f"Starting AI provider service...")
|
click.echo(f"AI Provider Service Setup:")
|
||||||
click.echo(f" Port: {port}")
|
click.echo(f" Port: {port}")
|
||||||
click.echo(f" Model: {model}")
|
click.echo(f" Model: {model}")
|
||||||
click.echo(f" Wallet: {provider_wallet}")
|
click.echo(f" Wallet: {provider_wallet}")
|
||||||
click.echo(f" Marketplace: {marketplace_url}")
|
click.echo(f" Marketplace: {marketplace_url}")
|
||||||
|
|
||||||
# Check if systemd service exists
|
click.echo("\n📋 To start the AI Provider service:")
|
||||||
service_cmd = f"systemctl start aitbc-ai-provider"
|
click.echo(f" 1. Create systemd service: /etc/systemd/system/aitbc-ai-provider.service")
|
||||||
try:
|
click.echo(f" 2. Run: sudo systemctl daemon-reload")
|
||||||
subprocess.run(service_cmd.split(), check=True, capture_output=True)
|
click.echo(f" 3. Run: sudo systemctl enable aitbc-ai-provider")
|
||||||
click.echo("✅ AI Provider service started")
|
click.echo(f" 4. Run: sudo systemctl start aitbc-ai-provider")
|
||||||
click.echo(f" Use 'aitbc ai status --port {port}' to verify")
|
click.echo(f"\n💡 Use 'aitbc ai status --port {port}' to verify service is running")
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
click.echo(f"❌ Failed to start AI Provider service: {e}")
|
|
||||||
click.echo(" Note: AI Provider should be a separate systemd service")
|
|
||||||
|
|
||||||
@ai_group.command()
|
@ai_group.command()
|
||||||
def stop():
|
def stop():
|
||||||
"""Stop AI provider service (systemd)."""
|
"""Stop AI provider service - provides shutdown instructions"""
|
||||||
click.echo("Stopping AI provider service...")
|
click.echo("📋 To stop the AI Provider service:")
|
||||||
try:
|
click.echo(" 1. Run: sudo systemctl stop aitbc-ai-provider")
|
||||||
subprocess.run(["systemctl", "stop", "aitbc-ai-provider"], check=True, capture_output=True)
|
click.echo(" 2. Run: sudo systemctl status aitbc-ai-provider (to verify)")
|
||||||
click.echo("✅ AI Provider service stopped")
|
click.echo("\n💡 Use 'aitbc ai status' to check if service is stopped")
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
click.echo(f"❌ Failed to stop AI Provider service: {e}")
|
|
||||||
|
|
||||||
@ai_group.command()
|
@ai_group.command()
|
||||||
@click.option('--to', required=True, help='Provider host (IP)')
|
@click.option('--to', required=True, help='Provider host (IP)')
|
||||||
|
|||||||
@@ -37,55 +37,32 @@ def register(ctx, name: Optional[str], memory: Optional[int], cuda_cores: Option
|
|||||||
"""Register GPU on marketplace (auto-detects hardware)"""
|
"""Register GPU on marketplace (auto-detects hardware)"""
|
||||||
config = ctx.obj['config']
|
config = ctx.obj['config']
|
||||||
|
|
||||||
# Auto-detect GPU hardware
|
# Note: GPU hardware detection should be done by separate system monitoring tools
|
||||||
try:
|
# CLI provides guidance for manual hardware specification
|
||||||
import subprocess
|
if not name or memory is None:
|
||||||
result = subprocess.run(['nvidia-smi', '--query-gpu=name,memory.total', '--format=csv,noheader,nounits'],
|
output("💡 To auto-detect GPU hardware, use system monitoring tools:", ctx.obj['output_format'])
|
||||||
capture_output=True, text=True, check=True)
|
output(" nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits", ctx.obj['output_format'])
|
||||||
|
output(" Or specify --name and --memory manually", ctx.obj['output_format'])
|
||||||
|
|
||||||
if result.returncode == 0:
|
if not name and not memory:
|
||||||
gpu_info = result.stdout.strip().split(', ')
|
error("GPU name and memory must be specified for registration", ctx.obj['output_format'])
|
||||||
detected_name = gpu_info[0].strip()
|
|
||||||
detected_memory = int(gpu_info[1].strip())
|
|
||||||
|
|
||||||
# Use detected values if not provided
|
|
||||||
if not name:
|
|
||||||
name = detected_name
|
|
||||||
if memory is None:
|
|
||||||
memory = detected_memory
|
|
||||||
|
|
||||||
# Validate provided specs against detected hardware
|
|
||||||
if not force:
|
|
||||||
if name and name != detected_name:
|
|
||||||
error(f"GPU name mismatch! Detected: '{detected_name}', Provided: '{name}'. Use --force to override.")
|
|
||||||
return
|
|
||||||
if memory and memory != detected_memory:
|
|
||||||
error(f"GPU memory mismatch! Detected: {detected_memory}GB, Provided: {memory}GB. Use --force to override.")
|
|
||||||
return
|
|
||||||
|
|
||||||
success(f"Auto-detected GPU: {detected_name} with {detected_memory}GB memory")
|
|
||||||
else:
|
|
||||||
if not force:
|
|
||||||
error("Failed to detect GPU hardware. Use --force to register without hardware validation.")
|
|
||||||
return
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
||||||
if not force:
|
|
||||||
error("nvidia-smi not available. Use --force to register without hardware validation.")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Build GPU specs
|
if not force:
|
||||||
|
output("⚠️ Hardware validation skipped. Use --force to register without hardware validation.",
|
||||||
|
ctx.obj['output_format'])
|
||||||
|
|
||||||
|
# Build GPU specs for registration
|
||||||
gpu_specs = {
|
gpu_specs = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"memory_gb": memory,
|
"memory_gb": memory,
|
||||||
"cuda_cores": cuda_cores,
|
"cuda_cores": cuda_cores,
|
||||||
"compute_capability": compute_capability,
|
"compute_capability": compute_capability,
|
||||||
"price_per_hour": price_per_hour,
|
"price_per_hour": price_per_hour,
|
||||||
"description": description
|
"description": description,
|
||||||
|
"miner_id": miner_id or config.api_key[:8], # Use auth key as miner ID if not provided
|
||||||
|
"registered_at": datetime.now().isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove None values
|
|
||||||
gpu_specs = {k: v for k, v in gpu_specs.items() if v is not None}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with httpx.Client() as client:
|
with httpx.Client() as client:
|
||||||
response = client.post(
|
response = client.post(
|
||||||
|
|||||||
Reference in New Issue
Block a user