refactor: flatten CLI directory structure - remove 'box in a box'
BEFORE: /opt/aitbc/cli/ ├── aitbc_cli/ # Python package (box in a box) │ ├── commands/ │ ├── main.py │ └── ... ├── setup.py AFTER: /opt/aitbc/cli/ # Flat structure ├── commands/ # Direct access ├── main.py # Direct access ├── auth/ ├── config/ ├── core/ ├── models/ ├── utils/ ├── plugins.py └── setup.py CHANGES MADE: - Moved all files from aitbc_cli/ to cli/ root - Fixed all relative imports (from . to absolute imports) - Updated setup.py entry point: aitbc_cli.main → main - Added CLI directory to Python path in entry script - Simplified deployment.py to remove dependency on deleted core.deployment - Fixed import paths in all command files - Recreated virtual environment with new structure BENEFITS: - Eliminated 'box in a box' nesting - Simpler directory structure - Direct access to all modules - Cleaner imports - Easier maintenance and development - CLI works with both 'python main.py' and 'aitbc' commands
This commit is contained in:
246
cli/commands/swarm.py
Executable file
246
cli/commands/swarm.py
Executable file
@@ -0,0 +1,246 @@
|
||||
"""Swarm intelligence commands for AITBC CLI"""
|
||||
|
||||
import click
|
||||
import httpx
|
||||
import json
|
||||
from typing import Optional, Dict, Any, List
|
||||
from utils import output, error, success, warning
|
||||
|
||||
|
||||
@click.group()
|
||||
def swarm():
|
||||
"""Swarm intelligence and collective optimization"""
|
||||
pass
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.option("--role", required=True,
|
||||
type=click.Choice(["load-balancer", "resource-optimizer", "task-coordinator", "monitor"]),
|
||||
help="Swarm role")
|
||||
@click.option("--capability", required=True, help="Agent capability")
|
||||
@click.option("--region", help="Operating region")
|
||||
@click.option("--priority", default="normal",
|
||||
type=click.Choice(["low", "normal", "high"]),
|
||||
help="Swarm priority")
|
||||
@click.pass_context
|
||||
def join(ctx, role: str, capability: str, region: Optional[str], priority: str):
|
||||
"""Join agent swarm for collective optimization"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
swarm_data = {
|
||||
"role": role,
|
||||
"capability": capability,
|
||||
"priority": priority
|
||||
}
|
||||
|
||||
if region:
|
||||
swarm_data["region"] = region
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/swarm/join",
|
||||
headers={"X-Api-Key": config.api_key or ""},
|
||||
json=swarm_data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
success(f"Joined swarm: {result['swarm_id']}")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to join swarm: {response.status_code}")
|
||||
if response.text:
|
||||
error(response.text)
|
||||
ctx.exit(1)
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
ctx.exit(1)
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.option("--task", required=True, help="Swarm task type")
|
||||
@click.option("--collaborators", type=int, default=5, help="Number of collaborators")
|
||||
@click.option("--strategy", default="consensus",
|
||||
type=click.Choice(["consensus", "leader-election", "distributed"]),
|
||||
help="Coordination strategy")
|
||||
@click.option("--timeout", default=3600, help="Task timeout in seconds")
|
||||
@click.pass_context
|
||||
def coordinate(ctx, task: str, collaborators: int, strategy: str, timeout: int):
|
||||
"""Coordinate swarm task execution"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
coordination_data = {
|
||||
"task": task,
|
||||
"collaborators": collaborators,
|
||||
"strategy": strategy,
|
||||
"timeout_seconds": timeout
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/swarm/coordinate",
|
||||
headers={"X-Api-Key": config.api_key or ""},
|
||||
json=coordination_data
|
||||
)
|
||||
|
||||
if response.status_code == 202:
|
||||
result = response.json()
|
||||
success(f"Swarm coordination started: {result['task_id']}")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to start coordination: {response.status_code}")
|
||||
if response.text:
|
||||
error(response.text)
|
||||
ctx.exit(1)
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
ctx.exit(1)
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.option("--swarm-id", help="Filter by swarm ID")
|
||||
@click.option("--status", help="Filter by status")
|
||||
@click.option("--limit", default=20, help="Number of swarms to list")
|
||||
@click.pass_context
|
||||
def list(ctx, swarm_id: Optional[str], status: Optional[str], limit: int):
|
||||
"""List active swarms"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
params = {"limit": limit}
|
||||
if swarm_id:
|
||||
params["swarm_id"] = swarm_id
|
||||
if status:
|
||||
params["status"] = status
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/swarm/list",
|
||||
headers={"X-Api-Key": config.api_key or ""},
|
||||
params=params
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
swarms = response.json()
|
||||
output(swarms, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to list swarms: {response.status_code}")
|
||||
ctx.exit(1)
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
ctx.exit(1)
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.argument("task_id")
|
||||
@click.option("--real-time", is_flag=True, help="Show real-time progress")
|
||||
@click.option("--interval", default=10, help="Update interval for real-time monitoring")
|
||||
@click.pass_context
|
||||
def status(ctx, task_id: str, real_time: bool, interval: int):
|
||||
"""Get swarm task status"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
def get_status():
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/swarm/tasks/{task_id}/status",
|
||||
headers={"X-Api-Key": config.api_key or ""}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
error(f"Failed to get task status: {response.status_code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
return None
|
||||
|
||||
if real_time:
|
||||
click.echo(f"Monitoring swarm task {task_id} (Ctrl+C to stop)...")
|
||||
while True:
|
||||
status_data = get_status()
|
||||
if status_data:
|
||||
click.clear()
|
||||
click.echo(f"Task ID: {task_id}")
|
||||
click.echo(f"Status: {status_data.get('status', 'Unknown')}")
|
||||
click.echo(f"Progress: {status_data.get('progress', 0)}%")
|
||||
click.echo(f"Collaborators: {status_data.get('active_collaborators', 0)}/{status_data.get('total_collaborators', 0)}")
|
||||
|
||||
if status_data.get('status') in ['completed', 'failed', 'cancelled']:
|
||||
break
|
||||
|
||||
time.sleep(interval)
|
||||
else:
|
||||
status_data = get_status()
|
||||
if status_data:
|
||||
output(status_data, ctx.obj['output_format'])
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.argument("swarm_id")
|
||||
@click.pass_context
|
||||
def leave(ctx, swarm_id: str):
|
||||
"""Leave swarm"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
if not click.confirm(f"Leave swarm {swarm_id}?"):
|
||||
click.echo("Operation cancelled")
|
||||
return
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/swarm/{swarm_id}/leave",
|
||||
headers={"X-Api-Key": config.api_key or ""}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success(f"Left swarm {swarm_id}")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to leave swarm: {response.status_code}")
|
||||
if response.text:
|
||||
error(response.text)
|
||||
ctx.exit(1)
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
ctx.exit(1)
|
||||
|
||||
|
||||
@swarm.command()
|
||||
@click.argument("task_id")
|
||||
@click.option("--consensus-threshold", default=0.7, help="Consensus threshold (0.0-1.0)")
|
||||
@click.pass_context
|
||||
def consensus(ctx, task_id: str, consensus_threshold: float):
|
||||
"""Achieve swarm consensus on task result"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
consensus_data = {
|
||||
"consensus_threshold": consensus_threshold
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/swarm/tasks/{task_id}/consensus",
|
||||
headers={"X-Api-Key": config.api_key or ""},
|
||||
json=consensus_data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success(f"Consensus achieved: {result.get('consensus_reached', False)}")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to achieve consensus: {response.status_code}")
|
||||
if response.text:
|
||||
error(response.text)
|
||||
ctx.exit(1)
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
ctx.exit(1)
|
||||
Reference in New Issue
Block a user