Files
aitbc/cli/aitbc_cli/commands/marketplace_advanced.py
oib 15427c96c0 chore: update file permissions to executable across repository
- Change file mode from 644 to 755 for all project files
- Add chain_id parameter to get_balance RPC endpoint with default "ait-devnet"
- Rename Miner.extra_meta_data to extra_metadata for consistency
2026-03-06 22:17:54 +01:00

655 lines
22 KiB
Python
Executable File

"""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 marketplace_analytics():
"""Marketplace analytics and insights"""
pass
advanced.add_command(marketplace_analytics)
@marketplace_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 get_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)
@marketplace_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)
@marketplace_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)
@marketplace_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)