Update Python version requirements and fix compatibility issues

- Bump minimum Python version from 3.11 to 3.13 across all apps
- Add Python 3.11-3.13 test matrix to CLI workflow
- Document Python 3.11+ requirement in .env.example
- Fix Starlette Broadcast removal with in-process fallback implementation
- Add _InProcessBroadcast class for tests when Starlette Broadcast is unavailable
- Refactor API key validators to read live settings instead of cached values
- Update database models with explicit
This commit is contained in:
oib
2026-02-24 18:41:08 +01:00
parent 24b3a37733
commit 825f157749
270 changed files with 66674 additions and 2027 deletions

View File

@@ -0,0 +1,627 @@
"""Agent commands for AITBC CLI - Advanced AI Agent Management"""
import click
import httpx
import json
import time
import uuid
from typing import Optional, Dict, Any, List
from pathlib import Path
from ..utils import output, error, success, warning
@click.group()
def agent():
"""Advanced AI agent workflow and execution management"""
pass
@agent.command()
@click.option("--name", required=True, help="Agent workflow name")
@click.option("--description", default="", help="Agent description")
@click.option("--workflow-file", type=click.File('r'), help="Workflow definition from JSON file")
@click.option("--verification", default="basic", type=click.Choice(["basic", "full", "zero-knowledge"]),
help="Verification level for agent execution")
@click.option("--max-execution-time", default=3600, help="Maximum execution time in seconds")
@click.option("--max-cost-budget", default=0.0, help="Maximum cost budget")
@click.pass_context
def create(ctx, name: str, description: str, workflow_file, verification: str,
max_execution_time: int, max_cost_budget: float):
"""Create a new AI agent workflow"""
config = ctx.obj['config']
# Build workflow data
workflow_data = {
"name": name,
"description": description,
"verification_level": verification,
"max_execution_time": max_execution_time,
"max_cost_budget": max_cost_budget
}
if workflow_file:
try:
workflow_spec = json.load(workflow_file)
workflow_data.update(workflow_spec)
except Exception as e:
error(f"Failed to read workflow file: {e}")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/workflows",
headers={"X-Api-Key": config.api_key or ""},
json=workflow_data
)
if response.status_code == 201:
workflow = response.json()
success(f"Agent workflow created: {workflow['id']}")
output(workflow, ctx.obj['output_format'])
else:
error(f"Failed to create agent workflow: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@agent.command()
@click.option("--type", "agent_type", help="Filter by agent type")
@click.option("--status", help="Filter by status")
@click.option("--verification", help="Filter by verification level")
@click.option("--limit", default=20, help="Number of agents to list")
@click.option("--owner", help="Filter by owner ID")
@click.pass_context
def list(ctx, agent_type: Optional[str], status: Optional[str],
verification: Optional[str], limit: int, owner: Optional[str]):
"""List available AI agent workflows"""
config = ctx.obj['config']
params = {"limit": limit}
if agent_type:
params["type"] = agent_type
if status:
params["status"] = status
if verification:
params["verification"] = verification
if owner:
params["owner"] = owner
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/workflows",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
workflows = response.json()
output(workflows, ctx.obj['output_format'])
else:
error(f"Failed to list agent workflows: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@agent.command()
@click.argument("agent_id")
@click.option("--inputs", type=click.File('r'), help="Input data from JSON file")
@click.option("--verification", default="basic", type=click.Choice(["basic", "full", "zero-knowledge"]),
help="Verification level for this execution")
@click.option("--priority", default="normal", type=click.Choice(["low", "normal", "high"]),
help="Execution priority")
@click.option("--timeout", default=3600, help="Execution timeout in seconds")
@click.pass_context
def execute(ctx, agent_id: str, inputs, verification: str, priority: str, timeout: int):
"""Execute an AI agent workflow"""
config = ctx.obj['config']
# Prepare execution data
execution_data = {
"verification_level": verification,
"priority": priority,
"timeout_seconds": timeout
}
if inputs:
try:
input_data = json.load(inputs)
execution_data["inputs"] = input_data
except Exception as e:
error(f"Failed to read inputs file: {e}")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/{agent_id}/execute",
headers={"X-Api-Key": config.api_key or ""},
json=execution_data
)
if response.status_code == 202:
execution = response.json()
success(f"Agent execution started: {execution['id']}")
output(execution, ctx.obj['output_format'])
else:
error(f"Failed to start agent execution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@agent.command()
@click.argument("execution_id")
@click.option("--watch", is_flag=True, help="Watch execution status in real-time")
@click.option("--interval", default=5, help="Watch interval in seconds")
@click.pass_context
def status(ctx, execution_id: str, watch: bool, interval: int):
"""Get status of agent execution"""
config = ctx.obj['config']
def get_status():
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/executions/{execution_id}",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
return response.json()
else:
error(f"Failed to get execution status: {response.status_code}")
return None
except Exception as e:
error(f"Network error: {e}")
return None
if watch:
click.echo(f"Watching execution {execution_id} (Ctrl+C to stop)...")
while True:
status_data = get_status()
if status_data:
click.clear()
click.echo(f"Execution Status: {status_data.get('status', 'Unknown')}")
click.echo(f"Progress: {status_data.get('progress', 0)}%")
click.echo(f"Current Step: {status_data.get('current_step', 'N/A')}")
click.echo(f"Cost: ${status_data.get('total_cost', 0.0):.4f}")
if status_data.get('status') in ['completed', 'failed']:
break
time.sleep(interval)
else:
status_data = get_status()
if status_data:
output(status_data, ctx.obj['output_format'])
@agent.command()
@click.argument("execution_id")
@click.option("--verify", is_flag=True, help="Verify cryptographic receipt")
@click.option("--download", type=click.Path(), help="Download receipt to file")
@click.pass_context
def receipt(ctx, execution_id: str, verify: bool, download: Optional[str]):
"""Get verifiable receipt for completed execution"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/executions/{execution_id}/receipt",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
receipt_data = response.json()
if verify:
# Verify receipt
verify_response = client.post(
f"{config.coordinator_url}/v1/agents/receipts/verify",
headers={"X-Api-Key": config.api_key or ""},
json={"receipt": receipt_data}
)
if verify_response.status_code == 200:
verification_result = verify_response.json()
receipt_data["verification"] = verification_result
if verification_result.get("valid"):
success("Receipt verification: PASSED")
else:
warning("Receipt verification: FAILED")
else:
warning("Could not verify receipt")
if download:
with open(download, 'w') as f:
json.dump(receipt_data, f, indent=2)
success(f"Receipt downloaded to {download}")
else:
output(receipt_data, ctx.obj['output_format'])
else:
error(f"Failed to get execution receipt: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def network():
"""Multi-agent collaborative network management"""
pass
@agent.add_command(network)
@network.command()
@click.option("--name", required=True, help="Network name")
@click.option("--agents", required=True, help="Comma-separated list of agent IDs")
@click.option("--description", default="", help="Network description")
@click.option("--coordination", default="centralized",
type=click.Choice(["centralized", "decentralized", "hybrid"]),
help="Coordination strategy")
@click.pass_context
def create(ctx, name: str, agents: str, description: str, coordination: str):
"""Create collaborative agent network"""
config = ctx.obj['config']
agent_ids = [agent_id.strip() for agent_id in agents.split(',')]
network_data = {
"name": name,
"description": description,
"agents": agent_ids,
"coordination_strategy": coordination
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/networks",
headers={"X-Api-Key": config.api_key or ""},
json=network_data
)
if response.status_code == 201:
network = response.json()
success(f"Agent network created: {network['id']}")
output(network, ctx.obj['output_format'])
else:
error(f"Failed to create agent network: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@network.command()
@click.argument("network_id")
@click.option("--task", type=click.File('r'), required=True, help="Task definition JSON file")
@click.option("--priority", default="normal", type=click.Choice(["low", "normal", "high"]),
help="Execution priority")
@click.pass_context
def execute(ctx, network_id: str, task, priority: str):
"""Execute collaborative task on agent network"""
config = ctx.obj['config']
try:
task_data = json.load(task)
except Exception as e:
error(f"Failed to read task file: {e}")
return
execution_data = {
"task": task_data,
"priority": priority
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/networks/{network_id}/execute",
headers={"X-Api-Key": config.api_key or ""},
json=execution_data
)
if response.status_code == 202:
execution = response.json()
success(f"Network execution started: {execution['id']}")
output(execution, ctx.obj['output_format'])
else:
error(f"Failed to start network execution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@network.command()
@click.argument("network_id")
@click.option("--metrics", default="all", help="Comma-separated metrics to show")
@click.option("--real-time", is_flag=True, help="Show real-time metrics")
@click.pass_context
def status(ctx, network_id: str, metrics: str, real_time: bool):
"""Get agent network status and performance metrics"""
config = ctx.obj['config']
params = {}
if metrics != "all":
params["metrics"] = metrics
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/networks/{network_id}/status",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
status_data = response.json()
output(status_data, ctx.obj['output_format'])
else:
error(f"Failed to get network status: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@network.command()
@click.argument("network_id")
@click.option("--objective", default="efficiency",
type=click.Choice(["speed", "efficiency", "cost", "quality"]),
help="Optimization objective")
@click.pass_context
def optimize(ctx, network_id: str, objective: str):
"""Optimize agent network collaboration"""
config = ctx.obj['config']
optimization_data = {"objective": objective}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/networks/{network_id}/optimize",
headers={"X-Api-Key": config.api_key or ""},
json=optimization_data
)
if response.status_code == 200:
result = response.json()
success(f"Network optimization completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to optimize network: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def learning():
"""Agent adaptive learning and training management"""
pass
@agent.add_command(learning)
@learning.command()
@click.argument("agent_id")
@click.option("--mode", default="reinforcement",
type=click.Choice(["reinforcement", "transfer", "meta"]),
help="Learning mode")
@click.option("--feedback-source", help="Feedback data source")
@click.option("--learning-rate", default=0.001, help="Learning rate")
@click.pass_context
def enable(ctx, agent_id: str, mode: str, feedback_source: Optional[str], learning_rate: float):
"""Enable adaptive learning for agent"""
config = ctx.obj['config']
learning_config = {
"mode": mode,
"learning_rate": learning_rate
}
if feedback_source:
learning_config["feedback_source"] = feedback_source
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/{agent_id}/learning/enable",
headers={"X-Api-Key": config.api_key or ""},
json=learning_config
)
if response.status_code == 200:
result = response.json()
success(f"Adaptive learning enabled for agent {agent_id}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to enable learning: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@learning.command()
@click.argument("agent_id")
@click.option("--feedback", type=click.File('r'), required=True, help="Feedback data JSON file")
@click.option("--epochs", default=10, help="Number of training epochs")
@click.pass_context
def train(ctx, agent_id: str, feedback, epochs: int):
"""Train agent with feedback data"""
config = ctx.obj['config']
try:
feedback_data = json.load(feedback)
except Exception as e:
error(f"Failed to read feedback file: {e}")
return
training_data = {
"feedback": feedback_data,
"epochs": epochs
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/{agent_id}/learning/train",
headers={"X-Api-Key": config.api_key or ""},
json=training_data
)
if response.status_code == 202:
training = response.json()
success(f"Training started: {training['id']}")
output(training, ctx.obj['output_format'])
else:
error(f"Failed to start training: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@learning.command()
@click.argument("agent_id")
@click.option("--metrics", default="accuracy,efficiency", help="Comma-separated metrics to show")
@click.pass_context
def progress(ctx, agent_id: str, metrics: str):
"""Review agent learning progress"""
config = ctx.obj['config']
params = {"metrics": metrics}
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/{agent_id}/learning/progress",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
progress_data = response.json()
output(progress_data, ctx.obj['output_format'])
else:
error(f"Failed to get learning progress: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@learning.command()
@click.argument("agent_id")
@click.option("--format", default="onnx", type=click.Choice(["onnx", "pickle", "torch"]),
help="Export format")
@click.option("--output", type=click.Path(), help="Output file path")
@click.pass_context
def export(ctx, agent_id: str, format: str, output: Optional[str]):
"""Export learned agent model"""
config = ctx.obj['config']
params = {"format": format}
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/agents/{agent_id}/learning/export",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
if output:
with open(output, 'wb') as f:
f.write(response.content)
success(f"Model exported to {output}")
else:
# Output metadata about the export
export_info = response.headers.get('X-Export-Info', '{}')
try:
info_data = json.loads(export_info)
output(info_data, ctx.obj['output_format'])
except:
output({"status": "export_ready", "format": format}, ctx.obj['output_format'])
else:
error(f"Failed to export model: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.command()
@click.option("--type", required=True,
type=click.Choice(["optimization", "feature", "bugfix", "documentation"]),
help="Contribution type")
@click.option("--description", required=True, help="Contribution description")
@click.option("--github-repo", default="oib/AITBC", help="GitHub repository")
@click.option("--branch", default="main", help="Target branch")
@click.pass_context
def submit_contribution(ctx, type: str, description: str, github_repo: str, branch: str):
"""Submit contribution to platform via GitHub"""
config = ctx.obj['config']
contribution_data = {
"type": type,
"description": description,
"github_repo": github_repo,
"target_branch": branch
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/agents/contributions",
headers={"X-Api-Key": config.api_key or ""},
json=contribution_data
)
if response.status_code == 201:
result = response.json()
success(f"Contribution submitted: {result['id']}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to submit contribution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
agent.add_command(submit_contribution)

View File

@@ -97,7 +97,7 @@ def status(ctx, node: int):
# Map node to RPC URL
node_urls = {
1: "http://localhost:8082",
2: "http://localhost:8081",
2: "http://localhost:9080/rpc", # Use RPC API with correct endpoint
3: "http://aitbc.keisanki.net/rpc"
}
@@ -109,7 +109,7 @@ def status(ctx, node: int):
try:
with httpx.Client() as client:
response = client.get(
f"{rpc_url}/status",
f"{rpc_url}/head",
timeout=5
)

View File

@@ -0,0 +1,654 @@
"""Advanced marketplace commands for AITBC CLI - Enhanced marketplace operations"""
import click
import httpx
import json
import base64
from typing import Optional, Dict, Any, List
from pathlib import Path
from ..utils import output, error, success, warning
@click.group()
def advanced():
"""Advanced marketplace operations and analytics"""
pass
@click.group()
def models():
"""Advanced model NFT operations"""
pass
advanced.add_command(models)
@models.command()
@click.option("--nft-version", default="2.0", help="NFT version filter")
@click.option("--category", help="Filter by model category")
@click.option("--tags", help="Comma-separated tags to filter")
@click.option("--rating-min", type=float, help="Minimum rating filter")
@click.option("--limit", default=20, help="Number of models to list")
@click.pass_context
def list(ctx, nft_version: str, category: Optional[str], tags: Optional[str],
rating_min: Optional[float], limit: int):
"""List advanced NFT models"""
config = ctx.obj['config']
params = {"nft_version": nft_version, "limit": limit}
if category:
params["category"] = category
if tags:
params["tags"] = [t.strip() for t in tags.split(',')]
if rating_min:
params["rating_min"] = rating_min
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/marketplace/advanced/models",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
models = response.json()
output(models, ctx.obj['output_format'])
else:
error(f"Failed to list models: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@models.command()
@click.option("--model-file", type=click.Path(exists=True), required=True, help="Model file path")
@click.option("--metadata", type=click.File('r'), required=True, help="Model metadata JSON file")
@click.option("--price", type=float, help="Initial price")
@click.option("--royalty", type=float, default=0.0, help="Royalty percentage")
@click.option("--supply", default=1, help="NFT supply")
@click.pass_context
def mint(ctx, model_file: str, metadata, price: Optional[float], royalty: float, supply: int):
"""Create model NFT with advanced metadata"""
config = ctx.obj['config']
# Read model file
try:
with open(model_file, 'rb') as f:
model_data = f.read()
except Exception as e:
error(f"Failed to read model file: {e}")
return
# Read metadata
try:
metadata_data = json.load(metadata)
except Exception as e:
error(f"Failed to read metadata file: {e}")
return
nft_data = {
"metadata": metadata_data,
"royalty_percentage": royalty,
"supply": supply
}
if price:
nft_data["initial_price"] = price
files = {
"model": model_data
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/models/mint",
headers={"X-Api-Key": config.api_key or ""},
data=nft_data,
files=files
)
if response.status_code == 201:
nft = response.json()
success(f"Model NFT minted: {nft['id']}")
output(nft, ctx.obj['output_format'])
else:
error(f"Failed to mint NFT: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@models.command()
@click.argument("nft_id")
@click.option("--new-version", type=click.Path(exists=True), required=True, help="New model version file")
@click.option("--version-notes", default="", help="Version update notes")
@click.option("--compatibility", default="backward",
type=click.Choice(["backward", "forward", "breaking"]),
help="Compatibility type")
@click.pass_context
def update(ctx, nft_id: str, new_version: str, version_notes: str, compatibility: str):
"""Update model NFT with new version"""
config = ctx.obj['config']
# Read new version file
try:
with open(new_version, 'rb') as f:
version_data = f.read()
except Exception as e:
error(f"Failed to read version file: {e}")
return
update_data = {
"version_notes": version_notes,
"compatibility": compatibility
}
files = {
"version": version_data
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/models/{nft_id}/update",
headers={"X-Api-Key": config.api_key or ""},
data=update_data,
files=files
)
if response.status_code == 200:
result = response.json()
success(f"Model NFT updated: {result['version']}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to update NFT: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@models.command()
@click.argument("nft_id")
@click.option("--deep-scan", is_flag=True, help="Perform deep authenticity scan")
@click.option("--check-integrity", is_flag=True, help="Check model integrity")
@click.option("--verify-performance", is_flag=True, help="Verify performance claims")
@click.pass_context
def verify(ctx, nft_id: str, deep_scan: bool, check_integrity: bool, verify_performance: bool):
"""Verify model authenticity and quality"""
config = ctx.obj['config']
verify_data = {
"deep_scan": deep_scan,
"check_integrity": check_integrity,
"verify_performance": verify_performance
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/models/{nft_id}/verify",
headers={"X-Api-Key": config.api_key or ""},
json=verify_data
)
if response.status_code == 200:
verification = response.json()
if verification.get("authentic"):
success("Model authenticity: VERIFIED")
else:
warning("Model authenticity: FAILED")
output(verification, ctx.obj['output_format'])
else:
error(f"Failed to verify model: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def analytics():
"""Marketplace analytics and insights"""
pass
advanced.add_command(analytics)
@analytics.command()
@click.option("--period", default="30d", help="Time period (1d, 7d, 30d, 90d)")
@click.option("--metrics", default="volume,trends", help="Comma-separated metrics")
@click.option("--category", help="Filter by category")
@click.option("--format", "output_format", default="json",
type=click.Choice(["json", "csv", "pdf"]),
help="Output format")
@click.pass_context
def analytics(ctx, period: str, metrics: str, category: Optional[str], output_format: str):
"""Get comprehensive marketplace analytics"""
config = ctx.obj['config']
params = {
"period": period,
"metrics": [m.strip() for m in metrics.split(',')],
"format": output_format
}
if category:
params["category"] = category
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/marketplace/advanced/analytics",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
if output_format == "pdf":
# Handle PDF download
filename = f"marketplace_analytics_{period}.pdf"
with open(filename, 'wb') as f:
f.write(response.content)
success(f"Analytics report downloaded: {filename}")
else:
analytics_data = response.json()
output(analytics_data, ctx.obj['output_format'])
else:
error(f"Failed to get analytics: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@analytics.command()
@click.argument("model_id")
@click.option("--competitors", is_flag=True, help="Include competitor analysis")
@click.option("--datasets", default="standard", help="Test datasets to use")
@click.option("--iterations", default=100, help="Benchmark iterations")
@click.pass_context
def benchmark(ctx, model_id: str, competitors: bool, datasets: str, iterations: int):
"""Model performance benchmarking"""
config = ctx.obj['config']
benchmark_data = {
"competitors": competitors,
"datasets": datasets,
"iterations": iterations
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/models/{model_id}/benchmark",
headers={"X-Api-Key": config.api_key or ""},
json=benchmark_data
)
if response.status_code == 202:
benchmark = response.json()
success(f"Benchmark started: {benchmark['id']}")
output(benchmark, ctx.obj['output_format'])
else:
error(f"Failed to start benchmark: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@analytics.command()
@click.option("--category", help="Filter by category")
@click.option("--forecast", default="7d", help="Forecast period")
@click.option("--confidence", default=0.8, help="Confidence threshold")
@click.pass_context
def trends(ctx, category: Optional[str], forecast: str, confidence: float):
"""Market trend analysis and forecasting"""
config = ctx.obj['config']
params = {
"forecast_period": forecast,
"confidence_threshold": confidence
}
if category:
params["category"] = category
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/marketplace/advanced/trends",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
trends_data = response.json()
output(trends_data, ctx.obj['output_format'])
else:
error(f"Failed to get trends: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@analytics.command()
@click.option("--format", default="pdf", type=click.Choice(["pdf", "html", "json"]),
help="Report format")
@click.option("--email", help="Email address to send report")
@click.option("--sections", default="all", help="Comma-separated report sections")
@click.pass_context
def report(ctx, format: str, email: Optional[str], sections: str):
"""Generate comprehensive marketplace report"""
config = ctx.obj['config']
report_data = {
"format": format,
"sections": [s.strip() for s in sections.split(',')]
}
if email:
report_data["email"] = email
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/reports/generate",
headers={"X-Api-Key": config.api_key or ""},
json=report_data
)
if response.status_code == 202:
report_job = response.json()
success(f"Report generation started: {report_job['id']}")
output(report_job, ctx.obj['output_format'])
else:
error(f"Failed to generate report: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def trading():
"""Advanced trading features"""
pass
advanced.add_command(trading)
@trading.command()
@click.argument("auction_id")
@click.option("--amount", type=float, required=True, help="Bid amount")
@click.option("--max-auto-bid", type=float, help="Maximum auto-bid amount")
@click.option("--proxy", is_flag=True, help="Use proxy bidding")
@click.pass_context
def bid(ctx, auction_id: str, amount: float, max_auto_bid: Optional[float], proxy: bool):
"""Participate in model auction"""
config = ctx.obj['config']
bid_data = {
"amount": amount,
"proxy_bidding": proxy
}
if max_auto_bid:
bid_data["max_auto_bid"] = max_auto_bid
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/auctions/{auction_id}/bid",
headers={"X-Api-Key": config.api_key or ""},
json=bid_data
)
if response.status_code == 200:
result = response.json()
success(f"Bid placed successfully")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to place bid: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@trading.command()
@click.argument("model_id")
@click.option("--recipients", required=True, help="Comma-separated recipient:percentage pairs")
@click.option("--smart-contract", is_flag=True, help="Use smart contract distribution")
@click.pass_context
def royalties(ctx, model_id: str, recipients: str, smart_contract: bool):
"""Create royalty distribution agreement"""
config = ctx.obj['config']
# Parse recipients
royalty_recipients = []
for recipient in recipients.split(','):
if ':' in recipient:
address, percentage = recipient.split(':', 1)
royalty_recipients.append({
"address": address.strip(),
"percentage": float(percentage.strip())
})
royalty_data = {
"recipients": royalty_recipients,
"smart_contract": smart_contract
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/models/{model_id}/royalties",
headers={"X-Api-Key": config.api_key or ""},
json=royalty_data
)
if response.status_code == 201:
result = response.json()
success(f"Royalty agreement created: {result['id']}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to create royalty agreement: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@trading.command()
@click.option("--strategy", default="arbitrage",
type=click.Choice(["arbitrage", "trend-following", "mean-reversion", "custom"]),
help="Trading strategy")
@click.option("--budget", type=float, required=True, help="Trading budget")
@click.option("--risk-level", default="medium",
type=click.Choice(["low", "medium", "high"]),
help="Risk level")
@click.option("--config", type=click.File('r'), help="Custom strategy configuration")
@click.pass_context
def execute(ctx, strategy: str, budget: float, risk_level: str, config):
"""Execute complex trading strategy"""
config_obj = ctx.obj['config']
strategy_data = {
"strategy": strategy,
"budget": budget,
"risk_level": risk_level
}
if config:
try:
custom_config = json.load(config)
strategy_data["custom_config"] = custom_config
except Exception as e:
error(f"Failed to read strategy config: {e}")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config_obj.coordinator_url}/v1/marketplace/advanced/trading/execute",
headers={"X-Api-Key": config_obj.api_key or ""},
json=strategy_data
)
if response.status_code == 202:
execution = response.json()
success(f"Trading strategy execution started: {execution['id']}")
output(execution, ctx.obj['output_format'])
else:
error(f"Failed to execute strategy: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def dispute():
"""Dispute resolution operations"""
pass
advanced.add_command(dispute)
@dispute.command()
@click.argument("transaction_id")
@click.option("--reason", required=True, help="Dispute reason")
@click.option("--evidence", type=click.File('rb'), multiple=True, help="Evidence files")
@click.option("--category", default="quality",
type=click.Choice(["quality", "delivery", "payment", "fraud", "other"]),
help="Dispute category")
@click.pass_context
def file(ctx, transaction_id: str, reason: str, evidence, category: str):
"""File dispute resolution request"""
config = ctx.obj['config']
dispute_data = {
"transaction_id": transaction_id,
"reason": reason,
"category": category
}
files = {}
for i, evidence_file in enumerate(evidence):
files[f"evidence_{i}"] = evidence_file.read()
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/disputes",
headers={"X-Api-Key": config.api_key or ""},
data=dispute_data,
files=files
)
if response.status_code == 201:
dispute = response.json()
success(f"Dispute filed: {dispute['id']}")
output(dispute, ctx.obj['output_format'])
else:
error(f"Failed to file dispute: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@dispute.command()
@click.argument("dispute_id")
@click.pass_context
def status(ctx, dispute_id: str):
"""Get dispute status and progress"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/marketplace/advanced/disputes/{dispute_id}",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
dispute_data = response.json()
output(dispute_data, ctx.obj['output_format'])
else:
error(f"Failed to get dispute status: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@dispute.command()
@click.argument("dispute_id")
@click.option("--resolution", required=True, help="Proposed resolution")
@click.option("--evidence", type=click.File('rb'), multiple=True, help="Additional evidence")
@click.pass_context
def resolve(ctx, dispute_id: str, resolution: str, evidence):
"""Propose dispute resolution"""
config = ctx.obj['config']
resolution_data = {
"resolution": resolution
}
files = {}
for i, evidence_file in enumerate(evidence):
files[f"evidence_{i}"] = evidence_file.read()
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/marketplace/advanced/disputes/{dispute_id}/resolve",
headers={"X-Api-Key": config.api_key or ""},
data=resolution_data,
files=files
)
if response.status_code == 200:
result = response.json()
success(f"Resolution proposal submitted")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to submit resolution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)

View File

@@ -0,0 +1,470 @@
"""Multi-modal processing commands for AITBC CLI"""
import click
import httpx
import json
import base64
import mimetypes
from typing import Optional, Dict, Any, List
from pathlib import Path
from ..utils import output, error, success, warning
@click.group()
def multimodal():
"""Multi-modal agent processing and cross-modal operations"""
pass
@multimodal.command()
@click.option("--name", required=True, help="Multi-modal agent name")
@click.option("--modalities", required=True, help="Comma-separated modalities (text,image,audio,video)")
@click.option("--description", default="", help="Agent description")
@click.option("--model-config", type=click.File('r'), help="Model configuration JSON file")
@click.option("--gpu-acceleration", is_flag=True, help="Enable GPU acceleration")
@click.pass_context
def agent(ctx, name: str, modalities: str, description: str, model_config, gpu_acceleration: bool):
"""Create multi-modal agent"""
config = ctx.obj['config']
modality_list = [mod.strip() for mod in modalities.split(',')]
agent_data = {
"name": name,
"description": description,
"modalities": modality_list,
"gpu_acceleration": gpu_acceleration,
"agent_type": "multimodal"
}
if model_config:
try:
config_data = json.load(model_config)
agent_data["model_config"] = config_data
except Exception as e:
error(f"Failed to read model config file: {e}")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents",
headers={"X-Api-Key": config.api_key or ""},
json=agent_data
)
if response.status_code == 201:
agent = response.json()
success(f"Multi-modal agent created: {agent['id']}")
output(agent, ctx.obj['output_format'])
else:
error(f"Failed to create multi-modal agent: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@multimodal.command()
@click.argument("agent_id")
@click.option("--text", help="Text input")
@click.option("--image", type=click.Path(exists=True), help="Image file path")
@click.option("--audio", type=click.Path(exists=True), help="Audio file path")
@click.option("--video", type=click.Path(exists=True), help="Video file path")
@click.option("--output-format", default="json", type=click.Choice(["json", "text", "binary"]),
help="Output format for results")
@click.pass_context
def process(ctx, agent_id: str, text: Optional[str], image: Optional[str],
audio: Optional[str], video: Optional[str], output_format: str):
"""Process multi-modal inputs with agent"""
config = ctx.obj['config']
# Prepare multi-modal data
modal_data = {}
if text:
modal_data["text"] = text
if image:
try:
with open(image, 'rb') as f:
image_data = f.read()
modal_data["image"] = {
"data": base64.b64encode(image_data).decode(),
"mime_type": mimetypes.guess_type(image)[0] or "image/jpeg",
"filename": Path(image).name
}
except Exception as e:
error(f"Failed to read image file: {e}")
return
if audio:
try:
with open(audio, 'rb') as f:
audio_data = f.read()
modal_data["audio"] = {
"data": base64.b64encode(audio_data).decode(),
"mime_type": mimetypes.guess_type(audio)[0] or "audio/wav",
"filename": Path(audio).name
}
except Exception as e:
error(f"Failed to read audio file: {e}")
return
if video:
try:
with open(video, 'rb') as f:
video_data = f.read()
modal_data["video"] = {
"data": base64.b64encode(video_data).decode(),
"mime_type": mimetypes.guess_type(video)[0] or "video/mp4",
"filename": Path(video).name
}
except Exception as e:
error(f"Failed to read video file: {e}")
return
if not modal_data:
error("At least one modality input must be provided")
return
process_data = {
"modalities": modal_data,
"output_format": output_format
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/process",
headers={"X-Api-Key": config.api_key or ""},
json=process_data
)
if response.status_code == 200:
result = response.json()
success("Multi-modal processing completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to process multi-modal inputs: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@multimodal.command()
@click.argument("agent_id")
@click.option("--dataset", default="coco_vqa", help="Dataset name for benchmarking")
@click.option("--metrics", default="accuracy,latency", help="Comma-separated metrics to evaluate")
@click.option("--iterations", default=100, help="Number of benchmark iterations")
@click.pass_context
def benchmark(ctx, agent_id: str, dataset: str, metrics: str, iterations: int):
"""Benchmark multi-modal agent performance"""
config = ctx.obj['config']
benchmark_data = {
"dataset": dataset,
"metrics": [m.strip() for m in metrics.split(',')],
"iterations": iterations
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/benchmark",
headers={"X-Api-Key": config.api_key or ""},
json=benchmark_data
)
if response.status_code == 202:
benchmark = response.json()
success(f"Benchmark started: {benchmark['id']}")
output(benchmark, ctx.obj['output_format'])
else:
error(f"Failed to start benchmark: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@multimodal.command()
@click.argument("agent_id")
@click.option("--objective", default="throughput",
type=click.Choice(["throughput", "latency", "accuracy", "efficiency"]),
help="Optimization objective")
@click.option("--target", help="Target value for optimization")
@click.pass_context
def optimize(ctx, agent_id: str, objective: str, target: Optional[str]):
"""Optimize multi-modal agent pipeline"""
config = ctx.obj['config']
optimization_data = {"objective": objective}
if target:
optimization_data["target"] = target
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/optimize",
headers={"X-Api-Key": config.api_key or ""},
json=optimization_data
)
if response.status_code == 200:
result = response.json()
success(f"Multi-modal optimization completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to optimize agent: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def convert():
"""Cross-modal conversion operations"""
pass
multimodal.add_command(convert)
@convert.command()
@click.option("--input", "input_path", required=True, type=click.Path(exists=True), help="Input file path")
@click.option("--output", "output_format", required=True,
type=click.Choice(["text", "image", "audio", "video"]),
help="Output modality")
@click.option("--model", default="blip", help="Conversion model to use")
@click.option("--output-file", type=click.Path(), help="Output file path")
@click.pass_context
def convert(ctx, input_path: str, output_format: str, model: str, output_file: Optional[str]):
"""Convert between modalities"""
config = ctx.obj['config']
# Read input file
try:
with open(input_path, 'rb') as f:
input_data = f.read()
except Exception as e:
error(f"Failed to read input file: {e}")
return
conversion_data = {
"input": {
"data": base64.b64encode(input_data).decode(),
"mime_type": mimetypes.guess_type(input_path)[0] or "application/octet-stream",
"filename": Path(input_path).name
},
"output_modality": output_format,
"model": model
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/convert",
headers={"X-Api-Key": config.api_key or ""},
json=conversion_data
)
if response.status_code == 200:
result = response.json()
if output_file and result.get("output_data"):
# Decode and save output
output_data = base64.b64decode(result["output_data"])
with open(output_file, 'wb') as f:
f.write(output_data)
success(f"Conversion output saved to {output_file}")
else:
output(result, ctx.obj['output_format'])
else:
error(f"Failed to convert modality: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def search():
"""Multi-modal search operations"""
pass
multimodal.add_command(search)
@search.command()
@click.argument("query")
@click.option("--modalities", default="image,text", help="Comma-separated modalities to search")
@click.option("--limit", default=20, help="Number of results to return")
@click.option("--threshold", default=0.5, help="Similarity threshold")
@click.pass_context
def search(ctx, query: str, modalities: str, limit: int, threshold: float):
"""Multi-modal search across different modalities"""
config = ctx.obj['config']
search_data = {
"query": query,
"modalities": [m.strip() for m in modalities.split(',')],
"limit": limit,
"threshold": threshold
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/search",
headers={"X-Api-Key": config.api_key or ""},
json=search_data
)
if response.status_code == 200:
results = response.json()
output(results, ctx.obj['output_format'])
else:
error(f"Failed to perform multi-modal search: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def attention():
"""Cross-modal attention analysis"""
pass
multimodal.add_command(attention)
@attention.command()
@click.argument("agent_id")
@click.option("--inputs", type=click.File('r'), required=True, help="Multi-modal inputs JSON file")
@click.option("--visualize", is_flag=True, help="Generate attention visualization")
@click.option("--output", type=click.Path(), help="Output file for visualization")
@click.pass_context
def attention(ctx, agent_id: str, inputs, visualize: bool, output: Optional[str]):
"""Analyze cross-modal attention patterns"""
config = ctx.obj['config']
try:
inputs_data = json.load(inputs)
except Exception as e:
error(f"Failed to read inputs file: {e}")
return
attention_data = {
"inputs": inputs_data,
"visualize": visualize
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/attention",
headers={"X-Api-Key": config.api_key or ""},
json=attention_data
)
if response.status_code == 200:
result = response.json()
if visualize and output and result.get("visualization"):
# Save visualization
viz_data = base64.b64decode(result["visualization"])
with open(output, 'wb') as f:
f.write(viz_data)
success(f"Attention visualization saved to {output}")
else:
output(result, ctx.obj['output_format'])
else:
error(f"Failed to analyze attention: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@multimodal.command()
@click.argument("agent_id")
@click.pass_context
def capabilities(ctx, agent_id: str):
"""List multi-modal agent capabilities"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/capabilities",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
capabilities = response.json()
output(capabilities, ctx.obj['output_format'])
else:
error(f"Failed to get agent capabilities: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@multimodal.command()
@click.argument("agent_id")
@click.option("--modality", required=True,
type=click.Choice(["text", "image", "audio", "video"]),
help="Modality to test")
@click.option("--test-data", type=click.File('r'), help="Test data JSON file")
@click.pass_context
def test(ctx, agent_id: str, modality: str, test_data):
"""Test individual modality processing"""
config = ctx.obj['config']
test_input = {}
if test_data:
try:
test_input = json.load(test_data)
except Exception as e:
error(f"Failed to read test data file: {e}")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/multimodal/agents/{agent_id}/test/{modality}",
headers={"X-Api-Key": config.api_key or ""},
json=test_input
)
if response.status_code == 200:
result = response.json()
success(f"Modality test completed for {modality}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to test modality: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)

View File

@@ -0,0 +1,604 @@
"""OpenClaw integration commands for AITBC CLI"""
import click
import httpx
import json
import time
from typing import Optional, Dict, Any, List
from ..utils import output, error, success, warning
@click.group()
def openclaw():
"""OpenClaw integration with edge computing deployment"""
pass
@click.group()
def deploy():
"""Agent deployment operations"""
pass
openclaw.add_command(deploy)
@deploy.command()
@click.argument("agent_id")
@click.option("--region", required=True, help="Deployment region")
@click.option("--instances", default=1, help="Number of instances to deploy")
@click.option("--instance-type", default="standard", help="Instance type")
@click.option("--edge-locations", help="Comma-separated edge locations")
@click.option("--auto-scale", is_flag=True, help="Enable auto-scaling")
@click.pass_context
def deploy(ctx, agent_id: str, region: str, instances: int, instance_type: str,
edge_locations: Optional[str], auto_scale: bool):
"""Deploy agent to OpenClaw network"""
config = ctx.obj['config']
deployment_data = {
"agent_id": agent_id,
"region": region,
"instances": instances,
"instance_type": instance_type,
"auto_scale": auto_scale
}
if edge_locations:
deployment_data["edge_locations"] = [loc.strip() for loc in edge_locations.split(',')]
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/deploy",
headers={"X-Api-Key": config.api_key or ""},
json=deployment_data
)
if response.status_code == 202:
deployment = response.json()
success(f"Agent deployment started: {deployment['id']}")
output(deployment, ctx.obj['output_format'])
else:
error(f"Failed to start deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@deploy.command()
@click.argument("deployment_id")
@click.option("--instances", required=True, type=int, help="New number of instances")
@click.option("--auto-scale", is_flag=True, help="Enable auto-scaling")
@click.option("--min-instances", default=1, help="Minimum instances for auto-scaling")
@click.option("--max-instances", default=10, help="Maximum instances for auto-scaling")
@click.pass_context
def scale(ctx, deployment_id: str, instances: int, auto_scale: bool, min_instances: int, max_instances: int):
"""Scale agent deployment"""
config = ctx.obj['config']
scale_data = {
"instances": instances,
"auto_scale": auto_scale,
"min_instances": min_instances,
"max_instances": max_instances
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/deployments/{deployment_id}/scale",
headers={"X-Api-Key": config.api_key or ""},
json=scale_data
)
if response.status_code == 200:
result = response.json()
success(f"Deployment scaled successfully")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to scale deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@deploy.command()
@click.argument("deployment_id")
@click.option("--objective", default="cost",
type=click.Choice(["cost", "performance", "latency", "efficiency"]),
help="Optimization objective")
@click.pass_context
def optimize(ctx, deployment_id: str, objective: str):
"""Optimize agent deployment"""
config = ctx.obj['config']
optimization_data = {"objective": objective}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/deployments/{deployment_id}/optimize",
headers={"X-Api-Key": config.api_key or ""},
json=optimization_data
)
if response.status_code == 200:
result = response.json()
success(f"Deployment optimization completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to optimize deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def monitor():
"""OpenClaw monitoring operations"""
pass
openclaw.add_command(monitor)
@monitor.command()
@click.argument("deployment_id")
@click.option("--metrics", default="latency,cost", help="Comma-separated metrics to monitor")
@click.option("--real-time", is_flag=True, help="Show real-time metrics")
@click.option("--interval", default=10, help="Update interval for real-time monitoring")
@click.pass_context
def monitor(ctx, deployment_id: str, metrics: str, real_time: bool, interval: int):
"""Monitor OpenClaw agent performance"""
config = ctx.obj['config']
params = {"metrics": [m.strip() for m in metrics.split(',')]}
def get_metrics():
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/deployments/{deployment_id}/metrics",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
return response.json()
else:
error(f"Failed to get metrics: {response.status_code}")
return None
except Exception as e:
error(f"Network error: {e}")
return None
if real_time:
click.echo(f"Monitoring deployment {deployment_id} (Ctrl+C to stop)...")
while True:
metrics_data = get_metrics()
if metrics_data:
click.clear()
click.echo(f"Deployment ID: {deployment_id}")
click.echo(f"Status: {metrics_data.get('status', 'Unknown')}")
click.echo(f"Instances: {metrics_data.get('instances', 'N/A')}")
metrics_list = metrics_data.get('metrics', {})
for metric in [m.strip() for m in metrics.split(',')]:
if metric in metrics_list:
value = metrics_list[metric]
click.echo(f"{metric.title()}: {value}")
if metrics_data.get('status') in ['terminated', 'failed']:
break
time.sleep(interval)
else:
metrics_data = get_metrics()
if metrics_data:
output(metrics_data, ctx.obj['output_format'])
@monitor.command()
@click.argument("deployment_id")
@click.pass_context
def status(ctx, deployment_id: str):
"""Get deployment status"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/deployments/{deployment_id}/status",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
status_data = response.json()
output(status_data, ctx.obj['output_format'])
else:
error(f"Failed to get deployment status: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def edge():
"""Edge computing operations"""
pass
openclaw.add_command(edge)
@edge.command()
@click.argument("agent_id")
@click.option("--locations", required=True, help="Comma-separated edge locations")
@click.option("--strategy", default="latency",
type=click.Choice(["latency", "cost", "availability", "hybrid"]),
help="Edge deployment strategy")
@click.option("--replicas", default=1, help="Number of replicas per location")
@click.pass_context
def deploy(ctx, agent_id: str, locations: str, strategy: str, replicas: int):
"""Deploy agent to edge locations"""
config = ctx.obj['config']
edge_data = {
"agent_id": agent_id,
"locations": [loc.strip() for loc in locations.split(',')],
"strategy": strategy,
"replicas": replicas
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/edge/deploy",
headers={"X-Api-Key": config.api_key or ""},
json=edge_data
)
if response.status_code == 202:
deployment = response.json()
success(f"Edge deployment started: {deployment['id']}")
output(deployment, ctx.obj['output_format'])
else:
error(f"Failed to start edge deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@edge.command()
@click.option("--location", help="Filter by location")
@click.pass_context
def resources(ctx, location: Optional[str]):
"""Manage edge resources"""
config = ctx.obj['config']
params = {}
if location:
params["location"] = location
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/edge/resources",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
resources = response.json()
output(resources, ctx.obj['output_format'])
else:
error(f"Failed to get edge resources: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@edge.command()
@click.argument("deployment_id")
@click.option("--latency-target", type=int, help="Target latency in milliseconds")
@click.option("--cost-budget", type=float, help="Cost budget")
@click.option("--availability", type=float, help="Target availability (0.0-1.0)")
@click.pass_context
def optimize(ctx, deployment_id: str, latency_target: Optional[int],
cost_budget: Optional[float], availability: Optional[float]):
"""Optimize edge deployment performance"""
config = ctx.obj['config']
optimization_data = {}
if latency_target:
optimization_data["latency_target_ms"] = latency_target
if cost_budget:
optimization_data["cost_budget"] = cost_budget
if availability:
optimization_data["availability_target"] = availability
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/edge/deployments/{deployment_id}/optimize",
headers={"X-Api-Key": config.api_key or ""},
json=optimization_data
)
if response.status_code == 200:
result = response.json()
success(f"Edge optimization completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to optimize edge deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@edge.command()
@click.argument("deployment_id")
@click.option("--standards", help="Comma-separated compliance standards")
@click.pass_context
def compliance(ctx, deployment_id: str, standards: Optional[str]):
"""Check edge security compliance"""
config = ctx.obj['config']
params = {}
if standards:
params["standards"] = [s.strip() for s in standards.split(',')]
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/edge/deployments/{deployment_id}/compliance",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
compliance_data = response.json()
output(compliance_data, ctx.obj['output_format'])
else:
error(f"Failed to check compliance: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def routing():
"""Agent skill routing and job offloading"""
pass
openclaw.add_command(routing)
@routing.command()
@click.argument("deployment_id")
@click.option("--algorithm", default="load-balanced",
type=click.Choice(["load-balanced", "skill-based", "cost-based", "latency-based"]),
help="Routing algorithm")
@click.option("--weights", help="Comma-separated weights for routing factors")
@click.pass_context
def optimize(ctx, deployment_id: str, algorithm: str, weights: Optional[str]):
"""Optimize agent skill routing"""
config = ctx.obj['config']
routing_data = {"algorithm": algorithm}
if weights:
routing_data["weights"] = [w.strip() for w in weights.split(',')]
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/routing/deployments/{deployment_id}/optimize",
headers={"X-Api-Key": config.api_key or ""},
json=routing_data
)
if response.status_code == 200:
result = response.json()
success(f"Routing optimization completed")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to optimize routing: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@routing.command()
@click.argument("deployment_id")
@click.pass_context
def status(ctx, deployment_id: str):
"""Get routing status and statistics"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/routing/deployments/{deployment_id}/status",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
status_data = response.json()
output(status_data, ctx.obj['output_format'])
else:
error(f"Failed to get routing status: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def ecosystem():
"""OpenClaw ecosystem development"""
pass
openclaw.add_command(ecosystem)
@ecosystem.command()
@click.option("--name", required=True, help="Solution name")
@click.option("--type", required=True,
type=click.Choice(["agent", "workflow", "integration", "tool"]),
help="Solution type")
@click.option("--description", default="", help="Solution description")
@click.option("--package", type=click.File('rb'), help="Solution package file")
@click.pass_context
def create(ctx, name: str, type: str, description: str, package):
"""Create OpenClaw ecosystem solution"""
config = ctx.obj['config']
solution_data = {
"name": name,
"type": type,
"description": description
}
files = {}
if package:
files["package"] = package.read()
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/ecosystem/solutions",
headers={"X-Api-Key": config.api_key or ""},
data=solution_data,
files=files
)
if response.status_code == 201:
solution = response.json()
success(f"OpenClaw solution created: {solution['id']}")
output(solution, ctx.obj['output_format'])
else:
error(f"Failed to create solution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@ecosystem.command()
@click.option("--type", help="Filter by solution type")
@click.option("--category", help="Filter by category")
@click.option("--limit", default=20, help="Number of solutions to list")
@click.pass_context
def list(ctx, type: Optional[str], category: Optional[str], limit: int):
"""List OpenClaw ecosystem solutions"""
config = ctx.obj['config']
params = {"limit": limit}
if type:
params["type"] = type
if category:
params["category"] = category
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/openclaw/ecosystem/solutions",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
solutions = response.json()
output(solutions, ctx.obj['output_format'])
else:
error(f"Failed to list solutions: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@ecosystem.command()
@click.argument("solution_id")
@click.pass_context
def install(ctx, solution_id: str):
"""Install OpenClaw ecosystem solution"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/openclaw/ecosystem/solutions/{solution_id}/install",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
result = response.json()
success(f"Solution installed successfully")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to install solution: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@openclaw.command()
@click.argument("deployment_id")
@click.pass_context
def terminate(ctx, deployment_id: str):
"""Terminate OpenClaw deployment"""
config = ctx.obj['config']
if not click.confirm(f"Terminate deployment {deployment_id}? This action cannot be undone."):
click.echo("Operation cancelled")
return
try:
with httpx.Client() as client:
response = client.delete(
f"{config.coordinator_url}/v1/openclaw/deployments/{deployment_id}",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
result = response.json()
success(f"Deployment {deployment_id} terminated")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to terminate deployment: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)

View File

@@ -0,0 +1,518 @@
"""Autonomous optimization commands for AITBC CLI"""
import click
import httpx
import json
import time
from typing import Optional, Dict, Any, List
from ..utils import output, error, success, warning
@click.group()
def optimize():
"""Autonomous optimization and predictive operations"""
pass
@click.group()
def self_opt():
"""Self-optimization operations"""
pass
optimize.add_command(self_opt)
@self_opt.command()
@click.argument("agent_id")
@click.option("--mode", default="auto-tune",
type=click.Choice(["auto-tune", "self-healing", "performance"]),
help="Optimization mode")
@click.option("--scope", default="full",
type=click.Choice(["full", "performance", "cost", "latency"]),
help="Optimization scope")
@click.option("--aggressiveness", default="moderate",
type=click.Choice(["conservative", "moderate", "aggressive"]),
help="Optimization aggressiveness")
@click.pass_context
def enable(ctx, agent_id: str, mode: str, scope: str, aggressiveness: str):
"""Enable autonomous optimization for agent"""
config = ctx.obj['config']
optimization_config = {
"mode": mode,
"scope": scope,
"aggressiveness": aggressiveness
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/enable",
headers={"X-Api-Key": config.api_key or ""},
json=optimization_config
)
if response.status_code == 200:
result = response.json()
success(f"Autonomous optimization enabled for agent {agent_id}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to enable optimization: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@self_opt.command()
@click.argument("agent_id")
@click.option("--metrics", default="performance,cost", help="Comma-separated metrics to monitor")
@click.option("--real-time", is_flag=True, help="Show real-time optimization status")
@click.option("--interval", default=10, help="Update interval for real-time monitoring")
@click.pass_context
def status(ctx, agent_id: str, metrics: str, real_time: bool, interval: int):
"""Monitor optimization progress and status"""
config = ctx.obj['config']
params = {"metrics": [m.strip() for m in metrics.split(',')]}
def get_status():
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/status",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
return response.json()
else:
error(f"Failed to get optimization status: {response.status_code}")
return None
except Exception as e:
error(f"Network error: {e}")
return None
if real_time:
click.echo(f"Monitoring optimization for agent {agent_id} (Ctrl+C to stop)...")
while True:
status_data = get_status()
if status_data:
click.clear()
click.echo(f"Optimization Status: {status_data.get('status', 'Unknown')}")
click.echo(f"Mode: {status_data.get('mode', 'N/A')}")
click.echo(f"Progress: {status_data.get('progress', 0)}%")
metrics_data = status_data.get('metrics', {})
for metric in [m.strip() for m in metrics.split(',')]:
if metric in metrics_data:
value = metrics_data[metric]
click.echo(f"{metric.title()}: {value}")
if status_data.get('status') in ['completed', 'failed', 'disabled']:
break
time.sleep(interval)
else:
status_data = get_status()
if status_data:
output(status_data, ctx.obj['output_format'])
@self_opt.command()
@click.argument("agent_id")
@click.option("--targets", required=True, help="Comma-separated target metrics (e.g., latency:100ms,cost:0.5)")
@click.option("--priority", default="balanced",
type=click.Choice(["performance", "cost", "balanced"]),
help="Optimization priority")
@click.pass_context
def objectives(ctx, agent_id: str, targets: str, priority: str):
"""Set optimization objectives and targets"""
config = ctx.obj['config']
# Parse targets
target_dict = {}
for target in targets.split(','):
if ':' in target:
key, value = target.split(':', 1)
target_dict[key.strip()] = value.strip()
else:
target_dict[target.strip()] = "optimize"
objectives_data = {
"targets": target_dict,
"priority": priority
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/objectives",
headers={"X-Api-Key": config.api_key or ""},
json=objectives_data
)
if response.status_code == 200:
result = response.json()
success(f"Optimization objectives set for agent {agent_id}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to set objectives: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@self_opt.command()
@click.argument("agent_id")
@click.option("--priority", default="all",
type=click.Choice(["high", "medium", "low", "all"]),
help="Filter recommendations by priority")
@click.option("--category", help="Filter by category (performance, cost, security)")
@click.pass_context
def recommendations(ctx, agent_id: str, priority: str, category: Optional[str]):
"""Get optimization recommendations"""
config = ctx.obj['config']
params = {}
if priority != "all":
params["priority"] = priority
if category:
params["category"] = category
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/recommendations",
headers={"X-Api-Key": config.api_key or ""},
params=params
)
if response.status_code == 200:
recommendations = response.json()
output(recommendations, ctx.obj['output_format'])
else:
error(f"Failed to get recommendations: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@self_opt.command()
@click.argument("agent_id")
@click.option("--recommendation-id", required=True, help="Specific recommendation ID to apply")
@click.option("--confirm", is_flag=True, help="Apply without confirmation prompt")
@click.pass_context
def apply(ctx, agent_id: str, recommendation_id: str, confirm: bool):
"""Apply optimization recommendation"""
config = ctx.obj['config']
if not confirm:
if not click.confirm(f"Apply recommendation {recommendation_id} to agent {agent_id}?"):
click.echo("Operation cancelled")
return
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/apply/{recommendation_id}",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
result = response.json()
success(f"Optimization recommendation applied")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to apply recommendation: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def predict():
"""Predictive operations"""
pass
optimize.add_command(predict)
@predict.command()
@click.argument("agent_id")
@click.option("--horizon", default=24, help="Prediction horizon in hours")
@click.option("--resources", default="gpu,memory", help="Comma-separated resources to predict")
@click.option("--confidence", default=0.8, help="Minimum confidence threshold")
@click.pass_context
def predict(ctx, agent_id: str, horizon: int, resources: str, confidence: float):
"""Predict resource needs and usage patterns"""
config = ctx.obj['config']
prediction_data = {
"horizon_hours": horizon,
"resources": [r.strip() for r in resources.split(',')],
"confidence_threshold": confidence
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/predict/agents/{agent_id}/resources",
headers={"X-Api-Key": config.api_key or ""},
json=prediction_data
)
if response.status_code == 200:
predictions = response.json()
success("Resource prediction completed")
output(predictions, ctx.obj['output_format'])
else:
error(f"Failed to generate predictions: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@predict.command()
@click.argument("agent_id")
@click.option("--policy", default="cost-efficiency",
type=click.Choice(["cost-efficiency", "performance", "availability", "hybrid"]),
help="Auto-scaling policy")
@click.option("--min-instances", default=1, help="Minimum number of instances")
@click.option("--max-instances", default=10, help="Maximum number of instances")
@click.option("--cooldown", default=300, help="Cooldown period in seconds")
@click.pass_context
def autoscale(ctx, agent_id: str, policy: str, min_instances: int, max_instances: int, cooldown: int):
"""Configure auto-scaling based on predictions"""
config = ctx.obj['config']
autoscale_config = {
"policy": policy,
"min_instances": min_instances,
"max_instances": max_instances,
"cooldown_seconds": cooldown
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/predict/agents/{agent_id}/autoscale",
headers={"X-Api-Key": config.api_key or ""},
json=autoscale_config
)
if response.status_code == 200:
result = response.json()
success(f"Auto-scaling configured for agent {agent_id}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to configure auto-scaling: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@predict.command()
@click.argument("agent_id")
@click.option("--metric", required=True, help="Metric to forecast (throughput, latency, cost, etc.)")
@click.option("--period", default=7, help="Forecast period in days")
@click.option("--granularity", default="hour",
type=click.Choice(["minute", "hour", "day", "week"]),
help="Forecast granularity")
@click.pass_context
def forecast(ctx, agent_id: str, metric: str, period: int, granularity: str):
"""Generate performance forecasts"""
config = ctx.obj['config']
forecast_params = {
"metric": metric,
"period_days": period,
"granularity": granularity
}
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/predict/agents/{agent_id}/forecast",
headers={"X-Api-Key": config.api_key or ""},
json=forecast_params
)
if response.status_code == 200:
forecast_data = response.json()
success(f"Forecast generated for {metric}")
output(forecast_data, ctx.obj['output_format'])
else:
error(f"Failed to generate forecast: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@click.group()
def tune():
"""Auto-tuning operations"""
pass
optimize.add_command(tune)
@tune.command()
@click.argument("agent_id")
@click.option("--parameters", help="Comma-separated parameters to tune")
@click.option("--objective", default="performance", help="Optimization objective")
@click.option("--iterations", default=100, help="Number of tuning iterations")
@click.pass_context
def auto(ctx, agent_id: str, parameters: Optional[str], objective: str, iterations: int):
"""Start automatic parameter tuning"""
config = ctx.obj['config']
tuning_data = {
"objective": objective,
"iterations": iterations
}
if parameters:
tuning_data["parameters"] = [p.strip() for p in parameters.split(',')]
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/tune/agents/{agent_id}/auto",
headers={"X-Api-Key": config.api_key or ""},
json=tuning_data
)
if response.status_code == 202:
tuning = response.json()
success(f"Auto-tuning started: {tuning['id']}")
output(tuning, ctx.obj['output_format'])
else:
error(f"Failed to start auto-tuning: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@tune.command()
@click.argument("tuning_id")
@click.option("--watch", is_flag=True, help="Watch tuning progress")
@click.pass_context
def status(ctx, tuning_id: str, watch: bool):
"""Get auto-tuning status"""
config = ctx.obj['config']
def get_status():
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/tune/sessions/{tuning_id}",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
return response.json()
else:
error(f"Failed to get tuning status: {response.status_code}")
return None
except Exception as e:
error(f"Network error: {e}")
return None
if watch:
click.echo(f"Watching tuning session {tuning_id} (Ctrl+C to stop)...")
while True:
status_data = get_status()
if status_data:
click.clear()
click.echo(f"Tuning Status: {status_data.get('status', 'Unknown')}")
click.echo(f"Progress: {status_data.get('progress', 0)}%")
click.echo(f"Iteration: {status_data.get('current_iteration', 0)}/{status_data.get('total_iterations', 0)}")
click.echo(f"Best Score: {status_data.get('best_score', 'N/A')}")
if status_data.get('status') in ['completed', 'failed', 'cancelled']:
break
time.sleep(5)
else:
status_data = get_status()
if status_data:
output(status_data, ctx.obj['output_format'])
@tune.command()
@click.argument("tuning_id")
@click.pass_context
def results(ctx, tuning_id: str):
"""Get auto-tuning results and best parameters"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.get(
f"{config.coordinator_url}/v1/tune/sessions/{tuning_id}/results",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
results = response.json()
output(results, ctx.obj['output_format'])
else:
error(f"Failed to get tuning results: {response.status_code}")
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)
@optimize.command()
@click.argument("agent_id")
@click.pass_context
def disable(ctx, agent_id: str):
"""Disable autonomous optimization for agent"""
config = ctx.obj['config']
try:
with httpx.Client() as client:
response = client.post(
f"{config.coordinator_url}/v1/optimize/agents/{agent_id}/disable",
headers={"X-Api-Key": config.api_key or ""}
)
if response.status_code == 200:
result = response.json()
success(f"Autonomous optimization disabled for agent {agent_id}")
output(result, ctx.obj['output_format'])
else:
error(f"Failed to disable optimization: {response.status_code}")
if response.text:
error(response.text)
ctx.exit(1)
except Exception as e:
error(f"Network error: {e}")
ctx.exit(1)

View 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}/v1/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}/v1/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}/v1/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}/v1/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}/v1/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}/v1/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)

View File

@@ -22,6 +22,12 @@ from .commands.config import config
from .commands.monitor import monitor
from .commands.governance import governance
from .commands.exchange import exchange
from .commands.agent import agent
from .commands.multimodal import multimodal
from .commands.optimize import optimize
from .commands.openclaw import openclaw
from .commands.marketplace_advanced import advanced
from .commands.swarm import swarm
from .plugins import plugin, load_plugins
@@ -100,6 +106,12 @@ cli.add_command(config)
cli.add_command(monitor)
cli.add_command(governance)
cli.add_command(exchange)
cli.add_command(agent)
cli.add_command(multimodal)
cli.add_command(optimize)
cli.add_command(openclaw)
cli.add_command(advanced)
cli.add_command(swarm)
cli.add_command(plugin)
load_plugins(cli)