Move validation scripts from docs/agent-training to scripts/training
- Move validate_stage_dependencies.py to scripts/training/ - Move generate_prerequisite_checks.py to scripts/training/ - Scripts/training is the standard location for training-related scripts
This commit is contained in:
@@ -1,265 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Prerequisite Check Generator
|
||||
|
||||
Generates prerequisite validation checks for training stages.
|
||||
Validates that all dependencies (stage and resource) are satisfied before running a stage.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
STAGE_ORDER = [
|
||||
"stage0_environment_setup",
|
||||
"stage1_foundation",
|
||||
"stage2_operations_mastery",
|
||||
"stage3_ai_operations",
|
||||
"stage4_marketplace_economics",
|
||||
"stage5_expert_operations",
|
||||
"stage6_agent_identity_sdk",
|
||||
"stage7_cross_node_training",
|
||||
"stage8_advanced_agent_specialization",
|
||||
"stage9_multi_chain_architecture",
|
||||
]
|
||||
|
||||
|
||||
class PrerequisiteChecker:
|
||||
def __init__(self, stages_dir: Path):
|
||||
self.stages_dir = stages_dir
|
||||
self.stages: Dict[str, dict] = {}
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
|
||||
def load_stages(self):
|
||||
"""Load all stage JSON files."""
|
||||
for stage_name in STAGE_ORDER:
|
||||
stage_file = self.stages_dir / f"{stage_name}.json"
|
||||
if stage_file.exists():
|
||||
self.stages[stage_name] = json.loads(stage_file.read_text())
|
||||
else:
|
||||
self.warnings.append(f"Stage file not found: {stage_file}")
|
||||
|
||||
def check_stage_order(self, stage_name: str) -> bool:
|
||||
"""Check that depends_on stages come before this stage in order."""
|
||||
stage_data = self.stages.get(stage_name)
|
||||
if not stage_data:
|
||||
return False
|
||||
|
||||
depends_on = stage_data.get("depends_on", [])
|
||||
stage_index = STAGE_ORDER.index(stage_name)
|
||||
|
||||
for dep_stage in depends_on:
|
||||
if dep_stage not in STAGE_ORDER:
|
||||
self.errors.append(f"{stage_name}: depends_on references unknown stage '{dep_stage}'")
|
||||
continue
|
||||
|
||||
dep_index = STAGE_ORDER.index(dep_stage)
|
||||
if dep_index >= stage_index:
|
||||
self.errors.append(
|
||||
f"{stage_name}: depends_on '{dep_stage}' comes after it in stage order "
|
||||
f"(index {dep_index} >= {stage_index})"
|
||||
)
|
||||
|
||||
return len(self.errors) == 0
|
||||
|
||||
def check_resource_dependencies(self, stage_name: str) -> bool:
|
||||
"""Check that resource_depends references valid stages."""
|
||||
stage_data = self.stages.get(stage_name)
|
||||
if not stage_data:
|
||||
return False
|
||||
|
||||
resource_depends = stage_data.get("resource_depends", [])
|
||||
depends_on = stage_data.get("depends_on", [])
|
||||
|
||||
for resource_dep in resource_depends:
|
||||
provider_stage = resource_dep.get("stage")
|
||||
if provider_stage:
|
||||
if provider_stage not in STAGE_ORDER:
|
||||
self.errors.append(
|
||||
f"{stage_name}: resource_depends references unknown stage '{provider_stage}'"
|
||||
)
|
||||
elif provider_stage not in depends_on and provider_stage != stage_name:
|
||||
self.warnings.append(
|
||||
f"{stage_name}: resource_depends stage '{provider_stage}' "
|
||||
f"not in depends_on (should be added)"
|
||||
)
|
||||
|
||||
return len(self.errors) == 0
|
||||
|
||||
def check_wallet_balance_placement(self, stage_name: str) -> bool:
|
||||
"""Check that wallet_balance appears before operations with wallet+password."""
|
||||
stage_data = self.stages.get(stage_name)
|
||||
if not stage_data:
|
||||
return False
|
||||
|
||||
operations = stage_data.get("training_data", {}).get("operations", [])
|
||||
wallet_balance_found = False
|
||||
|
||||
for i, op in enumerate(operations):
|
||||
op_name = op.get("operation", "")
|
||||
params = op.get("parameters", {})
|
||||
|
||||
if op_name == "wallet_balance":
|
||||
wallet_balance_found = True
|
||||
continue
|
||||
|
||||
has_wallet = "wallet" in params
|
||||
has_password = "password" in params
|
||||
|
||||
if has_wallet and has_password and not wallet_balance_found:
|
||||
self.errors.append(
|
||||
f"{stage_name}: operation '{op_name}' (index {i}) has wallet+password "
|
||||
f"but no wallet_balance check before it"
|
||||
)
|
||||
|
||||
return len(self.errors) == 0
|
||||
|
||||
def check_currency_fields(self, stage_name: str) -> bool:
|
||||
"""Check that operations with amount/price have currency field."""
|
||||
stage_data = self.stages.get(stage_name)
|
||||
if not stage_data:
|
||||
return False
|
||||
|
||||
operations = stage_data.get("training_data", {}).get("operations", [])
|
||||
currency_required_params = {"amount", "price", "bounty_amount", "stake_amount", "spread"}
|
||||
|
||||
for i, op in enumerate(operations):
|
||||
op_name = op.get("operation", "")
|
||||
params = op.get("parameters", {})
|
||||
|
||||
requires_currency = any(param in currency_required_params for param in params.keys())
|
||||
if requires_currency and "currency" not in params:
|
||||
self.errors.append(
|
||||
f"{stage_name}: operation '{op_name}' (index {i}) has "
|
||||
f"{currency_required_params} parameter(s) but missing 'currency' field"
|
||||
)
|
||||
|
||||
return len(self.errors) == 0
|
||||
|
||||
def validate_stage(self, stage_name: str) -> bool:
|
||||
"""Run all prerequisite checks for a stage."""
|
||||
self.check_stage_order(stage_name)
|
||||
self.check_resource_dependencies(stage_name)
|
||||
self.check_wallet_balance_placement(stage_name)
|
||||
self.check_currency_fields(stage_name)
|
||||
return len(self.errors) == 0
|
||||
|
||||
def validate_all(self) -> bool:
|
||||
"""Validate all stages."""
|
||||
for stage_name in STAGE_ORDER:
|
||||
if stage_name in self.stages:
|
||||
self.validate_stage(stage_name)
|
||||
return len(self.errors) == 0
|
||||
|
||||
def generate_prerequisite_script(self, stage_name: str) -> str:
|
||||
"""Generate a bash script to check prerequisites for a stage."""
|
||||
stage_data = self.stages.get(stage_name)
|
||||
if not stage_data:
|
||||
return f"# Stage {stage_name} not found"
|
||||
|
||||
depends_on = stage_data.get("depends_on", [])
|
||||
resource_depends = stage_data.get("resource_depends", [])
|
||||
|
||||
script_lines = [
|
||||
f"#!/bin/bash",
|
||||
f"# Prerequisite checks for {stage_name}",
|
||||
f"#",
|
||||
f"# This script validates that all prerequisites are satisfied",
|
||||
f"# before starting the training stage.",
|
||||
f"",
|
||||
f"set -e",
|
||||
f"",
|
||||
f'echo "Checking prerequisites for {stage_name}..."',
|
||||
]
|
||||
|
||||
# Check stage dependencies
|
||||
if depends_on:
|
||||
script_lines.append(f"# Stage dependencies")
|
||||
for dep_stage in depends_on:
|
||||
script_lines.extend([
|
||||
f"if [ ! -f \"$(dirname \"$0\")/{dep_stage}.json\" ]; then",
|
||||
f' echo "❌ Missing prerequisite stage: {dep_stage}"',
|
||||
f" exit 1",
|
||||
f"fi",
|
||||
f'echo "✅ Prerequisite stage found: {dep_stage}"',
|
||||
f"",
|
||||
])
|
||||
|
||||
# Check resource dependencies
|
||||
if resource_depends:
|
||||
script_lines.append(f"# Resource dependencies")
|
||||
for res_dep in resource_depends:
|
||||
resource_type = res_dep.get("resource", "")
|
||||
condition = res_dep.get("condition", "")
|
||||
script_lines.extend([
|
||||
f"# Check {resource_type}: {condition}",
|
||||
f"# TODO: Implement resource check for {resource_type}",
|
||||
f'echo "⚠️ Resource check not implemented: {resource_type}"',
|
||||
f"",
|
||||
])
|
||||
|
||||
script_lines.extend([
|
||||
f'echo "✅ All prerequisites satisfied for {stage_name}"',
|
||||
f"exit 0",
|
||||
])
|
||||
|
||||
return "\n".join(script_lines)
|
||||
|
||||
def report(self):
|
||||
"""Print validation report."""
|
||||
print("Prerequisite Validation Report")
|
||||
print("=" * 50)
|
||||
|
||||
if self.errors:
|
||||
print(f"❌ {len(self.errors)} error(s) found:")
|
||||
for error in self.errors:
|
||||
print(f" - {error}")
|
||||
else:
|
||||
print("✅ No errors found")
|
||||
|
||||
if self.warnings:
|
||||
print(f"\n⚠️ {len(self.warnings)} warning(s):")
|
||||
for warning in self.warnings:
|
||||
print(f" - {warning}")
|
||||
|
||||
print("\nStage Status:")
|
||||
for stage_name in STAGE_ORDER:
|
||||
if stage_name in self.stages:
|
||||
print(f" ✅ {stage_name}")
|
||||
else:
|
||||
print(f" ❌ {stage_name} (file not found)")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: generate_prerequisite_checks.py <stages_dir> [stage_name]")
|
||||
print(" stages_dir: Path to directory containing stage JSON files")
|
||||
print(" stage_name: (optional) Generate prerequisite script for specific stage")
|
||||
sys.exit(1)
|
||||
|
||||
stages_dir = Path(sys.argv[1])
|
||||
if not stages_dir.exists():
|
||||
print(f"❌ Stages directory not found: {stages_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
checker = PrerequisiteChecker(stages_dir)
|
||||
checker.load_stages()
|
||||
|
||||
# If specific stage requested, generate prerequisite script
|
||||
if len(sys.argv) >= 3:
|
||||
stage_name = sys.argv[2]
|
||||
script = checker.generate_prerequisite_script(stage_name)
|
||||
print(script)
|
||||
sys.exit(0)
|
||||
|
||||
# Otherwise, validate all stages
|
||||
checker.validate_all()
|
||||
checker.report()
|
||||
|
||||
sys.exit(0 if len(checker.errors) == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,199 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Stage Dependency Validator
|
||||
|
||||
Validates that stage JSON files follow dependency rules:
|
||||
1. Operations with wallet+password parameters should have wallet_balance check before them
|
||||
2. Operations with amount/price parameters should have currency field
|
||||
3. Operations that require resources (agent_id, dispute_id, etc.) should have corresponding create operations
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
# Parameters that indicate wallet+password operations
|
||||
WALLET_PASSWORD_PARAMS = {"wallet", "password"}
|
||||
|
||||
# Parameters that indicate currency requirement
|
||||
CURRENCY_REQUIRED_PARAMS = {"amount", "price", "bounty_amount", "stake_amount", "spread"}
|
||||
|
||||
# Resource ID patterns
|
||||
RESOURCE_PATTERNS = {
|
||||
"agent_id": "agent_*",
|
||||
"dispute_id": "dispute_*",
|
||||
"enterprise_id": "enterprise_*",
|
||||
"proposal_id": "proposal_*",
|
||||
"island_id": "island_*",
|
||||
"workflow_id": "workflow_*",
|
||||
"gpu_id": "gpu_*",
|
||||
"listing_id": "listing_*",
|
||||
"job_id": "job_*",
|
||||
"bounty_id": "bounty_*",
|
||||
"mm_id": "mm_*",
|
||||
"fl_id": "fl_*",
|
||||
"swarm_id": "swarm_*",
|
||||
"validator_id": "validator_*",
|
||||
"bridge_tx_id": "bridge_tx_*",
|
||||
"transfer_id": "transfer_*",
|
||||
"deployment_id": "deployment_*",
|
||||
"pubsub_config_id": "pubsub_config_*",
|
||||
"sync_config_id": "sync_config_*",
|
||||
}
|
||||
|
||||
# Operations that create resources (can create multiple resources)
|
||||
RESOURCE_CREATORS = {
|
||||
"agent_create": ["agent_id"],
|
||||
"dispute_create": ["dispute_id"],
|
||||
"enterprise_create": ["enterprise_id"],
|
||||
"governance_propose": ["proposal_id"],
|
||||
"island_create": ["island_id"],
|
||||
"multi_chain_island_setup": ["island_id"],
|
||||
"workflow_create": ["workflow_id"],
|
||||
"market_gpu_register": ["gpu_id", "listing_id"],
|
||||
"market_sell": ["listing_id"],
|
||||
"ai_submit": ["job_id"],
|
||||
"bounty_system": ["bounty_id"],
|
||||
"cross_chain_market_maker": ["mm_id"],
|
||||
"federated_learning_coordinator": ["fl_id"],
|
||||
"agent_swarm_create": ["swarm_id"],
|
||||
"staking_validator_agent": ["validator_id"],
|
||||
"cross_chain_bridge": ["bridge_tx_id"],
|
||||
"cross_chain_transfer": ["transfer_id"],
|
||||
"production_deploy": ["deployment_id"],
|
||||
"redis_pubsub_config": ["pubsub_config_id"],
|
||||
"gossip_sync_config": ["sync_config_id"],
|
||||
}
|
||||
|
||||
|
||||
class StageValidator:
|
||||
def __init__(self, stage_file: Path):
|
||||
self.stage_file = stage_file
|
||||
self.stage_data = json.loads(stage_file.read_text())
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
|
||||
def validate(self) -> bool:
|
||||
"""Run all validation checks."""
|
||||
operations = self.stage_data.get("training_data", {}).get("operations", [])
|
||||
|
||||
self._validate_wallet_balance_checks(operations)
|
||||
self._validate_currency_fields(operations)
|
||||
self._validate_resource_dependencies(operations)
|
||||
|
||||
return len(self.errors) == 0
|
||||
|
||||
def _validate_wallet_balance_checks(self, operations: List[Dict]):
|
||||
"""Ensure operations with wallet+password have wallet_balance check before them."""
|
||||
wallet_balance_found = False
|
||||
|
||||
for i, op in enumerate(operations):
|
||||
op_name = op.get("operation", "")
|
||||
params = op.get("parameters", {})
|
||||
|
||||
if op_name == "wallet_balance":
|
||||
wallet_balance_found = True
|
||||
continue
|
||||
|
||||
# Check if operation requires wallet+password
|
||||
has_wallet = "wallet" in params
|
||||
has_password = "password" in params
|
||||
|
||||
if has_wallet and has_password:
|
||||
if not wallet_balance_found:
|
||||
self.errors.append(
|
||||
f"Operation '{op_name}' (index {i}) has wallet+password parameters "
|
||||
f"but no wallet_balance check before it"
|
||||
)
|
||||
|
||||
def _validate_currency_fields(self, operations: List[Dict]):
|
||||
"""Ensure operations with amount/price parameters have currency field."""
|
||||
for i, op in enumerate(operations):
|
||||
op_name = op.get("operation", "")
|
||||
params = op.get("parameters", {})
|
||||
|
||||
# Check if operation requires currency
|
||||
requires_currency = any(
|
||||
param in CURRENCY_REQUIRED_PARAMS for param in params.keys()
|
||||
)
|
||||
|
||||
if requires_currency and "currency" not in params:
|
||||
self.errors.append(
|
||||
f"Operation '{op_name}' (index {i}) has {CURRENCY_REQUIRED_PARAMS} parameter(s) "
|
||||
f"but missing 'currency' field"
|
||||
)
|
||||
|
||||
def _validate_resource_dependencies(self, operations: List[Dict]):
|
||||
"""Ensure operations that require resources have corresponding create operations."""
|
||||
created_resources: Dict[str, Set[str]] = {} # resource_type -> set of patterns
|
||||
required_resources: List[tuple] = [] # (index, op_name, resource_type, pattern)
|
||||
|
||||
for i, op in enumerate(operations):
|
||||
op_name = op.get("operation", "")
|
||||
params = op.get("parameters", {})
|
||||
|
||||
# Track resources created
|
||||
if op_name in RESOURCE_CREATORS:
|
||||
resource_types = RESOURCE_CREATORS[op_name]
|
||||
if isinstance(resource_types, str):
|
||||
resource_types = [resource_types]
|
||||
for resource_type in resource_types:
|
||||
if resource_type not in created_resources:
|
||||
created_resources[resource_type] = set()
|
||||
created_resources[resource_type].add(RESOURCE_PATTERNS.get(resource_type, f"{resource_type}_*"))
|
||||
|
||||
# Track resources required
|
||||
for param_name, param_value in params.items():
|
||||
for resource_type, pattern in RESOURCE_PATTERNS.items():
|
||||
if param_name == resource_type or (isinstance(param_value, str) and pattern in param_value):
|
||||
required_resources.append((i, op_name, resource_type, pattern))
|
||||
|
||||
# Validate that required resources have corresponding create operations
|
||||
for index, op_name, resource_type, pattern in required_resources:
|
||||
if resource_type not in created_resources:
|
||||
self.errors.append(
|
||||
f"Operation '{op_name}' (index {index}) requires '{resource_type}' "
|
||||
f"but no '{resource_type}' create operation found"
|
||||
)
|
||||
|
||||
def report(self):
|
||||
"""Print validation report."""
|
||||
stage_name = self.stage_data.get("stage", self.stage_file.name)
|
||||
|
||||
if self.errors:
|
||||
print(f"❌ {stage_name}: {len(self.errors)} error(s)")
|
||||
for error in self.errors:
|
||||
print(f" - {error}")
|
||||
else:
|
||||
print(f"✅ {stage_name}: No errors")
|
||||
|
||||
if self.warnings:
|
||||
print(f"⚠️ {stage_name}: {len(self.warnings)} warning(s)")
|
||||
for warning in self.warnings:
|
||||
print(f" - {warning}")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: validate_stage_dependencies.py <stage_file.json> [stage_file2.json ...]")
|
||||
sys.exit(1)
|
||||
|
||||
all_valid = True
|
||||
for stage_path in sys.argv[1:]:
|
||||
stage_file = Path(stage_path)
|
||||
if not stage_file.exists():
|
||||
print(f"❌ File not found: {stage_file}")
|
||||
all_valid = False
|
||||
continue
|
||||
|
||||
validator = StageValidator(stage_file)
|
||||
if not validator.validate():
|
||||
all_valid = False
|
||||
validator.report()
|
||||
|
||||
sys.exit(0 if all_valid else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user