Add auto-generate prerequisite checks script (Priority 3 #1)
Prerequisite Checker Script: - Create generate_prerequisite_checks.py for validating stage dependencies - Checks stage order (depends_on stages must come before current stage) - Checks resource dependencies (resource_depends references valid stages) - Checks wallet_balance placement (must appear before wallet+password operations) - Checks currency fields (operations with amount/price must have currency) - Generates bash prerequisite scripts for individual stages Stage Fixes: - Add currency field to transaction_send in stage1_foundation.json - Remove stage4_marketplace_economics from stage3_ai_operations depends_on (circular dependency) Validation: - All stages (0-9) now pass prerequisite validation - Script can be run: python3 docs/agent-training/generate_prerequisite_checks.py docs/agent-training
This commit is contained in:
265
docs/agent-training/generate_prerequisite_checks.py
Executable file
265
docs/agent-training/generate_prerequisite_checks.py
Executable file
@@ -0,0 +1,265 @@
|
||||
#!/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()
|
||||
@@ -148,6 +148,7 @@
|
||||
"wallet": "training-wallet",
|
||||
"to": "recipient-address",
|
||||
"amount": "10",
|
||||
"currency": "AIT",
|
||||
"password": "training123"
|
||||
},
|
||||
"expected_result": {
|
||||
@@ -156,10 +157,10 @@
|
||||
},
|
||||
"success_criteria": {
|
||||
"status": "success",
|
||||
"response_fields": ["transaction_id", "amount"],
|
||||
"performance": {
|
||||
"max_duration_ms": 10000
|
||||
}
|
||||
"response_fields": ["transaction_id", "amount"]
|
||||
},
|
||||
"performance": {
|
||||
"max_duration_ms": 10000
|
||||
},
|
||||
"verify_on_chain": true,
|
||||
"confirmations_required": 1,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"difficulty": "intermediate",
|
||||
"estimated_time_minutes": 60,
|
||||
"skill_level": "practitioner",
|
||||
"depends_on": ["stage2_operations_mastery", "stage4_marketplace_economics"],
|
||||
"depends_on": ["stage2_operations_mastery"],
|
||||
"skills": [
|
||||
"ai_job_submission",
|
||||
"task_management",
|
||||
|
||||
Reference in New Issue
Block a user