Files
TinyTorch/tito/commands/milestone.py
Vijay Janapa Reddi 308d6f2049 Add transformer quickdemo with live learning progression dashboard
New milestone 05 demo that shows students the model learning to "talk":
- Live dashboard with epoch-by-epoch response progression
- Systems stats panel (tokens/sec, batch time, memory)
- 3 test prompts with full history displayed
- Smaller model (110K params) for ~2 minute training time

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-22 15:55:12 -05:00

1251 lines
51 KiB
Python

"""
Milestone command group for TinyTorch CLI: capability-based learning progression.
The milestone system transforms module completion into meaningful capability achievements.
Instead of just finishing modules, students unlock epic milestones that represent
real-world ML engineering skills.
"""
from argparse import ArgumentParser, Namespace
from rich.panel import Panel
from rich import box
from rich.progress import Progress, BarColumn, TextColumn, SpinnerColumn, TimeElapsedColumn
from rich.console import Console
from rich.align import Align
from rich.text import Text
from rich.layout import Layout
from rich.tree import Tree
from rich.columns import Columns
import sys
import json
import time
import subprocess
import yaml
from datetime import datetime
from pathlib import Path
from .base import BaseCommand
from .checkpoint import CheckpointSystem
from ..core.console import print_ascii_logo
from ..core.console import get_console
# Milestone-to-script mapping for tito milestone run command
MILESTONE_SCRIPTS = {
"01": {
"id": "01",
"name": "Perceptron (1957)",
"year": 1957,
"title": "Frank Rosenblatt's First Neural Network",
"script": "milestones/01_1957_perceptron/02_rosenblatt_trained.py",
"required_modules": [1],
"description": "Build the first trainable neural network",
"historical_context": "Rosenblatt's perceptron proved machines could learn",
"emoji": "🧠"
},
"02": {
"id": "02",
"name": "XOR Crisis (1969)",
"year": 1969,
"title": "Solving the Problem That Stalled AI",
"script": "milestones/02_1969_xor/02_xor_solved.py",
"required_modules": [1, 2],
"description": "Solve XOR with multi-layer networks",
"historical_context": "Minsky & Papert showed single-layer limits",
"emoji": "🔀"
},
"03": {
"id": "03",
"name": "MLP Revival (1986)",
"year": 1986,
"title": "Backpropagation Breakthrough",
"script": "milestones/03_1986_mlp/01_rumelhart_tinydigits.py",
"required_modules": [1, 2, 3, 4, 5, 6, 7],
"description": "Train deep networks on TinyDigits",
"historical_context": "Rumelhart, Hinton & Williams (Nature, 1986)",
"emoji": "🎓"
},
"04": {
"id": "04",
"name": "CNN Revolution (1998)",
"year": 1998,
"title": "LeNet - Computer Vision Breakthrough",
"script": "milestones/04_1998_cnn/01_lecun_tinydigits.py",
"required_modules": [1, 2, 3, 4, 5, 6, 7, 8, 9],
"description": "Build LeNet for digit recognition",
"historical_context": "Yann LeCun's convolutional networks",
"emoji": "👁️"
},
"05": {
"id": "05",
"name": "Transformer Era (2017)",
"year": 2017,
"title": "Attention is All You Need",
"script": "milestones/05_2017_transformer/03_quickdemo.py",
"required_modules": list(range(1, 14)),
"description": "Build transformer with self-attention",
"historical_context": "Vaswani et al. revolutionized NLP",
"emoji": "🤖"
},
"06": {
"id": "06",
"name": "MLPerf Benchmarks (2018)",
"year": 2018,
"title": "Production ML Systems",
"script": "milestones/06_2018_mlperf/02_compression.py",
"required_modules": list(range(1, 20)),
"description": "Optimize for production deployment",
"historical_context": "MLPerf standardized ML benchmarks",
"emoji": "🏆"
}
}
class MilestoneSystem:
"""Core milestone tracking and management system."""
def __init__(self, config):
self.config = config
self.console = get_console()
# Load milestones from configuration file
self.MILESTONES = self._load_milestones_config()
def _load_milestones_config(self) -> dict:
"""Load milestone configuration from YAML files (main and era-specific)."""
config_path = Path("milestones") / "milestones.yml"
milestones = {}
# Try to load main milestones.yml first
if config_path.exists():
try:
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
# Convert to expected format
for milestone_id, milestone_data in config['milestones'].items():
milestone_data['id'] = str(milestone_id)
milestones[str(milestone_id)] = milestone_data
except Exception as e:
self.console.print(f"[yellow]Warning: Could not load main milestone config: {e}[/yellow]")
# Also try to load era-specific configurations
era_paths = [
Path("milestones") / "foundation" / "milestone.yml",
Path("milestones") / "revolution" / "milestone.yml",
Path("milestones") / "generation" / "milestone.yml"
]
for era_path in era_paths:
if era_path.exists():
try:
with open(era_path, 'r') as f:
era_config = yaml.safe_load(f)
if 'milestone' in era_config:
milestone_data = era_config['milestone']
milestone_id = milestone_data['id']
milestones[str(milestone_id)] = milestone_data
except Exception as e:
self.console.print(f"[yellow]Warning: Could not load era config {era_path}: {e}[/yellow]")
# If no milestones loaded, return fallback
if not milestones:
return {
"1": {
"id": "1",
"name": "Machines Can See",
"title": "I taught a computer to recognize digits!",
"trigger_module": "05_dense",
"required_checkpoints": ["00", "01", "02", "03", "04"],
"victory_condition": "85%+ MNIST accuracy with MLP",
"capability": "I can create neural networks that recognize images!",
"real_world_impact": "Foundation for any computer vision system",
"emoji": "👁️",
"test_file": "milestone_01_machines_can_see.py"
}
}
return milestones
def get_milestone_status(self) -> dict:
"""Get current milestone progress status."""
checkpoint_system = CheckpointSystem(self.config)
milestone_data = self._get_milestone_progress_data()
status = {
"milestones": {},
"overall_progress": 0,
"total_unlocked": 0,
"next_milestone": None
}
total_milestones = len(self.MILESTONES)
unlocked_count = 0
for milestone_id, milestone in self.MILESTONES.items():
# Check if all required checkpoints are complete
required_complete = all(
checkpoint_system.get_checkpoint_status(cp_id).get("is_complete", False)
for cp_id in milestone["required_checkpoints"]
)
# Check if milestone is unlocked
is_unlocked = milestone_id in milestone_data.get("unlocked_milestones", [])
# Check if trigger module is completed
trigger_complete = self._is_module_completed(milestone["trigger_module"])
milestone_status = {
"id": milestone_id,
"name": milestone["name"],
"title": milestone["title"],
"emoji": milestone["emoji"],
"trigger_module": milestone["trigger_module"],
"required_checkpoints": milestone["required_checkpoints"],
"victory_condition": milestone["victory_condition"],
"capability": milestone["capability"],
"real_world_impact": milestone["real_world_impact"],
"required_complete": required_complete,
"trigger_complete": trigger_complete,
"is_unlocked": is_unlocked,
"can_unlock": required_complete and trigger_complete and not is_unlocked,
"unlock_date": milestone_data.get("unlock_dates", {}).get(milestone_id)
}
status["milestones"][milestone_id] = milestone_status
if is_unlocked:
unlocked_count += 1
elif milestone_status["can_unlock"] and not status["next_milestone"]:
status["next_milestone"] = milestone_id
status["total_unlocked"] = unlocked_count
status["overall_progress"] = (unlocked_count / total_milestones) * 100
return status
def check_milestone_unlock(self, completed_module: str) -> dict:
"""Check if completing a module unlocks a milestone."""
result = {
"milestone_unlocked": False,
"milestone_id": None,
"milestone_data": None,
"celebration_needed": False
}
# Find milestone triggered by this module
for milestone_id, milestone in self.MILESTONES.items():
if milestone["trigger_module"] == completed_module:
status = self.get_milestone_status()
milestone_status = status["milestones"][milestone_id]
if milestone_status["can_unlock"]:
# Unlock the milestone!
self._unlock_milestone(milestone_id)
result.update({
"milestone_unlocked": True,
"milestone_id": milestone_id,
"milestone_data": milestone,
"celebration_needed": True
})
break
return result
def run_milestone_test(self, milestone_id: str) -> dict:
"""Run tests to validate milestone achievement."""
if milestone_id not in self.MILESTONES:
return {"success": False, "error": f"Milestone {milestone_id} not found"}
milestone = self.MILESTONES[milestone_id]
# Check all required checkpoints
checkpoint_system = CheckpointSystem(self.config)
failed_checkpoints = []
for cp_id in milestone["required_checkpoints"]:
if not checkpoint_system.get_checkpoint_status(cp_id).get("is_complete", False):
failed_checkpoints.append(cp_id)
if failed_checkpoints:
return {
"success": False,
"error": f"Required checkpoints not completed: {', '.join(failed_checkpoints)}",
"milestone_name": milestone["name"]
}
# Check trigger module completion
if not self._is_module_completed(milestone["trigger_module"]):
return {
"success": False,
"error": f"Trigger module {milestone['trigger_module']} not completed",
"milestone_name": milestone["name"]
}
# All tests passed
return {
"success": True,
"milestone_id": milestone_id,
"milestone_name": milestone["name"],
"title": milestone["title"],
"capability": milestone["capability"],
"victory_condition": milestone["victory_condition"]
}
def _unlock_milestone(self, milestone_id: str) -> None:
"""Record milestone unlock in progress tracking."""
milestone_data = self._get_milestone_progress_data()
if milestone_id not in milestone_data["unlocked_milestones"]:
milestone_data["unlocked_milestones"].append(milestone_id)
milestone_data["unlock_dates"][milestone_id] = datetime.now().isoformat()
milestone_data["total_unlocked"] = len(milestone_data["unlocked_milestones"])
self._save_milestone_progress_data(milestone_data)
def _is_module_completed(self, module_name: str) -> bool:
"""Check if a module has been completed."""
# Check module progress file
progress_file = Path(".tito") / "progress.json"
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
progress_data = json.load(f)
return module_name in progress_data.get("completed_modules", [])
except (json.JSONDecodeError, IOError):
pass
return False
def _get_milestone_progress_data(self) -> dict:
"""Get or create milestone progress data."""
progress_dir = Path(".tito")
progress_file = progress_dir / "milestones.json"
progress_dir.mkdir(exist_ok=True)
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
pass
return {
"unlocked_milestones": [],
"unlock_dates": {},
"total_unlocked": 0,
"achievements": []
}
def _save_milestone_progress_data(self, milestone_data: dict) -> None:
"""Save milestone progress data."""
progress_dir = Path(".tito")
progress_file = progress_dir / "milestones.json"
progress_dir.mkdir(exist_ok=True)
try:
with open(progress_file, 'w') as f:
json.dump(milestone_data, f, indent=2)
except IOError:
pass
class MilestoneCommand(BaseCommand):
@property
def name(self) -> str:
return "milestone"
@property
def description(self) -> str:
return "Milestone achievement and capability unlock commands"
def add_arguments(self, parser: ArgumentParser) -> None:
subparsers = parser.add_subparsers(
dest='milestone_command',
help='Milestone subcommands',
metavar='SUBCOMMAND'
)
# List subcommand (NEW)
list_parser = subparsers.add_parser(
'list',
help='List available milestones and their status'
)
list_parser.add_argument(
'--simple',
action='store_true',
help='Show simple list (less detail)'
)
# Run subcommand (NEW)
run_parser = subparsers.add_parser(
'run',
help='Run a milestone with prerequisite checking'
)
run_parser.add_argument(
'milestone_id',
help='Milestone ID to run (01-06)'
)
run_parser.add_argument(
'--skip-checks',
action='store_true',
help='Skip prerequisite checks (not recommended)'
)
# Info subcommand (NEW)
info_parser = subparsers.add_parser(
'info',
help='Show detailed information about a milestone'
)
info_parser.add_argument(
'milestone_id',
help='Milestone ID to show info for (01-06)'
)
# Status subcommand
status_parser = subparsers.add_parser(
'status',
help='View milestone progress and achievements'
)
status_parser.add_argument(
'--detailed',
action='store_true',
help='Show detailed milestone information'
)
# Timeline subcommand
timeline_parser = subparsers.add_parser(
'timeline',
help='View milestone timeline and progression'
)
timeline_parser.add_argument(
'--horizontal',
action='store_true',
help='Show horizontal progress bar instead of tree'
)
# Test subcommand
test_parser = subparsers.add_parser(
'test',
help='Test milestone achievement requirements'
)
test_parser.add_argument(
'milestone_id',
nargs='?',
help='Milestone ID to test (1-5), or test next available'
)
# Demo subcommand
demo_parser = subparsers.add_parser(
'demo',
help='Run milestone capability demonstration'
)
demo_parser.add_argument(
'milestone_id',
help='Milestone ID to demonstrate (1-5)'
)
def run(self, args: Namespace) -> int:
console = self.console
if not hasattr(args, 'milestone_command') or not args.milestone_command:
console.print(Panel(
"[bold cyan]Milestone Commands[/bold cyan]\n\n"
"Recreate ML history and achieve epic capabilities!\n\n"
"Available subcommands:\n"
" • [bold]list[/bold] - List available milestones\n"
" • [bold]run[/bold] - Run a milestone (with prereq checks)\n"
" • [bold]info[/bold] - Show detailed milestone information\n"
" • [bold]status[/bold] - View progress and achievements\n"
" • [bold]timeline[/bold] - View milestone timeline\n"
" • [bold]test[/bold] - Test milestone requirements\n"
" • [bold]demo[/bold] - Run capability demonstration\n\n"
"[dim]Examples:[/dim]\n"
"[dim] tito milestone list[/dim]\n"
"[dim] tito milestone run 03[/dim]\n"
"[dim] tito milestone info 03[/dim]\n"
"[dim] tito milestone status --detailed[/dim]",
title="🏆 Milestone System",
border_style="bright_cyan"
))
return 0
# Execute the appropriate subcommand
if args.milestone_command == 'list':
return self._handle_list_command(args)
elif args.milestone_command == 'run':
return self._handle_run_command(args)
elif args.milestone_command == 'info':
return self._handle_info_command(args)
elif args.milestone_command == 'status':
return self._handle_status_command(args)
elif args.milestone_command == 'timeline':
return self._handle_timeline_command(args)
elif args.milestone_command == 'test':
return self._handle_test_command(args)
elif args.milestone_command == 'demo':
return self._handle_demo_command(args)
else:
console.print(Panel(
f"[red]Unknown milestone subcommand: {args.milestone_command}[/red]",
title="Error",
border_style="red"
))
return 1
def _handle_status_command(self, args: Namespace) -> int:
"""Handle milestone status command."""
console = self.console
milestone_system = MilestoneSystem(self.config)
status = milestone_system.get_milestone_status()
# Show header with overall progress
total_milestones = len(milestone_system.MILESTONES)
console.print(Panel(
f"[bold cyan]🎮 TinyTorch Milestone Progress[/bold cyan]\n\n"
f"[bold]Capabilities Unlocked:[/bold] {status['total_unlocked']}/{total_milestones} milestones\n"
f"[bold]Overall Progress:[/bold] {status['overall_progress']:.0f}%\n\n"
f"[dim]Transform from student to ML Systems Engineer![/dim]",
title="🚀 Your Epic Journey",
border_style="bright_blue"
))
# Show milestone status
for milestone_id in sorted(milestone_system.MILESTONES.keys()):
milestone = status["milestones"][milestone_id]
self._show_milestone_status(milestone, args.detailed)
# Show next steps
if status["next_milestone"]:
next_milestone = status["milestones"][status["next_milestone"]]
console.print(Panel(
f"[bold cyan]🎯 Next Achievement[/bold cyan]\n\n"
f"[bold yellow]{next_milestone['emoji']} {next_milestone['title']}[/bold yellow]\n"
f"[dim]{next_milestone['victory_condition']}[/dim]\n\n"
f"[green]Ready to unlock![/green] Complete: {next_milestone['trigger_module']}\n"
f"[dim]tito module complete {next_milestone['trigger_module']}[/dim]",
title="Next Milestone",
border_style="bright_green"
))
elif status["total_unlocked"] == 5:
console.print(Panel(
f"[bold green]🏆 QUEST COMPLETE! 🏆[/bold green]\n\n"
f"[green]You've unlocked all 5 epic milestones![/green]\n"
f"[bold white]You are now an ML Systems Engineer![/bold white]\n\n"
f"[cyan]Share your achievement and inspire others![/cyan]",
title="🌟 FULL MASTERY ACHIEVED",
border_style="bright_green"
))
return 0
def _show_milestone_status(self, milestone: dict, detailed: bool = False) -> None:
"""Show status for a single milestone."""
console = self.console
# Status indicator
if milestone["is_unlocked"]:
status_icon = "🔓"
status_color = "green"
status_text = "UNLOCKED"
elif milestone["can_unlock"]:
status_icon = ""
status_color = "yellow"
status_text = "READY TO UNLOCK"
elif milestone["required_complete"] and not milestone["trigger_complete"]:
status_icon = "🔒"
status_color = "cyan"
status_text = f"COMPLETE: {milestone['trigger_module']}"
else:
status_icon = "🔒"
status_color = "dim"
status_text = "LOCKED"
# Basic display
milestone_content = (
f"[{status_color}]{status_icon} {milestone['emoji']} {milestone['title']}[/{status_color}]\n"
f"[dim]{milestone['victory_condition']}[/dim]"
)
# Add detailed information if requested
if detailed:
req_status = "" if milestone["required_complete"] else ""
trigger_status = "" if milestone["trigger_complete"] else ""
milestone_content += (
f"\n\n[bold]Requirements:[/bold]\n"
f" {req_status} Checkpoints: {', '.join(milestone['required_checkpoints'])}\n"
f" {trigger_status} Module: {milestone['trigger_module']}\n"
f"[bold]Capability:[/bold] {milestone['capability']}\n"
f"[bold]Impact:[/bold] {milestone['real_world_impact']}"
)
if milestone["is_unlocked"] and milestone.get("unlock_date"):
unlock_date = datetime.fromisoformat(milestone["unlock_date"]).strftime("%Y-%m-%d")
milestone_content += f"\n[dim]Unlocked: {unlock_date}[/dim]"
console.print(Panel(
milestone_content,
title=f"Milestone {milestone['id']}",
border_style=status_color
))
def _handle_timeline_command(self, args: Namespace) -> int:
"""Handle milestone timeline command."""
console = self.console
milestone_system = MilestoneSystem(self.config)
status = milestone_system.get_milestone_status()
if args.horizontal:
self._show_horizontal_timeline(status, milestone_system)
else:
self._show_tree_timeline(status, milestone_system)
return 0
def _show_horizontal_timeline(self, status: dict, milestone_system: MilestoneSystem) -> None:
"""Show horizontal progress bar timeline."""
console = self.console
total_milestones = len(milestone_system.MILESTONES)
console.print(Panel(
f"[bold cyan]🎮 Milestone Timeline[/bold cyan]\n\n"
f"[bold]Progress:[/bold] {status['total_unlocked']}/{total_milestones} milestones unlocked",
title="Your Epic Journey",
border_style="bright_blue"
))
# Create progress bar
progress_width = 50
total_milestones = len(milestone_system.MILESTONES)
unlocked_width = int((status["total_unlocked"] / total_milestones) * progress_width)
# Create milestone markers
timeline = []
for milestone_id in sorted(milestone_system.MILESTONES.keys()):
milestone = status["milestones"][milestone_id]
if milestone["is_unlocked"]:
marker = f"[green]{milestone['emoji']}[/green]"
elif milestone["can_unlock"]:
marker = f"[yellow blink]{milestone['emoji']}[/yellow blink]"
else:
marker = f"[dim]{milestone['emoji']}[/dim]"
timeline.append(marker)
# Show timeline
console.print(f"\n{' '.join(timeline)}")
# Progress bar
filled = "" * unlocked_width
empty = "" * (progress_width - unlocked_width)
console.print(f"\n[green]{filled}[/green][dim]{empty}[/dim]")
console.print(f"[dim]{status['overall_progress']:.0f}% complete[/dim]\n")
def _show_tree_timeline(self, status: dict, milestone_system: MilestoneSystem) -> None:
"""Show tree-style milestone timeline."""
console = self.console
console.print(Panel(
f"[bold cyan]🎮 Milestone Progression Tree[/bold cyan]\n\n"
f"[bold]Your journey from student to ML Systems Engineer[/bold]",
title="Epic Timeline",
border_style="bright_blue"
))
# Create tree structure
tree = Tree("🚀 [bold]TinyTorch Mastery Journey[/bold]")
for milestone_id in sorted(milestone_system.MILESTONES.keys()):
milestone = status["milestones"][milestone_id]
if milestone["is_unlocked"]:
node_style = "green"
icon = ""
elif milestone["can_unlock"]:
node_style = "yellow"
icon = ""
else:
node_style = "dim"
icon = "🔒"
branch = tree.add(
f"[{node_style}]{icon} {milestone['emoji']} {milestone['title']}[/{node_style}]"
)
# Add capability description
branch.add(f"[dim]{milestone['capability']}[/dim]")
# Add trigger module info
if milestone["trigger_complete"]:
branch.add(f"[green]✅ {milestone['trigger_module']} completed[/green]")
else:
branch.add(f"[dim]🎯 Complete: {milestone['trigger_module']}[/dim]")
console.print(tree)
console.print()
def _handle_test_command(self, args: Namespace) -> int:
"""Handle milestone test command."""
console = self.console
milestone_system = MilestoneSystem(self.config)
# Determine which milestone to test
if args.milestone_id:
milestone_id = args.milestone_id
else:
# Test next available milestone
status = milestone_system.get_milestone_status()
if status["next_milestone"]:
milestone_id = status["next_milestone"]
else:
console.print(Panel(
"[yellow]No milestone available to test.[/yellow]\n\n"
"Either all milestones are unlocked or none are ready.\n"
"Use [dim]tito milestone status[/dim] to see your progress.",
title="No Test Available",
border_style="yellow"
))
return 0
# Validate milestone ID
if milestone_id not in milestone_system.MILESTONES:
console.print(Panel(
f"[red]Invalid milestone ID: {milestone_id}[/red]\n\n"
f"Valid milestone IDs: 1, 2, 3, 4, 5",
title="Invalid Milestone",
border_style="red"
))
return 1
milestone = milestone_system.MILESTONES[milestone_id]
console.print(Panel(
f"[bold cyan]🧪 Testing Milestone {milestone_id}[/bold cyan]\n\n"
f"[bold]{milestone['emoji']} {milestone['title']}[/bold]\n"
f"[dim]{milestone['victory_condition']}[/dim]",
title="Milestone Test",
border_style="bright_cyan"
))
# Run the test with progress animation
with console.status(f"[bold green]Testing milestone requirements...", spinner="dots"):
result = milestone_system.run_milestone_test(milestone_id)
# Show results
if result["success"]:
console.print(Panel(
f"[bold green]✅ Milestone Test Passed![/bold green]\n\n"
f"[green]All requirements met for {result['milestone_name']}[/green]\n"
f"[cyan]Capability: {result['capability']}[/cyan]\n\n"
f"[bold yellow]Complete the trigger module to unlock:[/bold yellow]\n"
f"[dim]tito module complete {milestone['trigger_module']}[/dim]",
title="🎉 Ready to Unlock!",
border_style="green"
))
else:
console.print(Panel(
f"[bold yellow]⚠️ Milestone Requirements Not Met[/bold yellow]\n\n"
f"[yellow]Milestone: {result.get('milestone_name', 'Unknown')}[/yellow]\n"
f"[red]Issue: {result.get('error', 'Unknown error')}[/red]\n\n"
f"[cyan]Complete the required modules and try again.[/cyan]",
title="Requirements Missing",
border_style="yellow"
))
return 0
def _handle_demo_command(self, args: Namespace) -> int:
"""Handle milestone demo command."""
console = self.console
milestone_system = MilestoneSystem(self.config)
milestone_id = args.milestone_id
# Validate milestone ID
if milestone_id not in milestone_system.MILESTONES:
console.print(Panel(
f"[red]Invalid milestone ID: {milestone_id}[/red]\n\n"
f"Valid milestone IDs: 1, 2, 3, 4, 5",
title="Invalid Milestone",
border_style="red"
))
return 1
milestone = milestone_system.MILESTONES[milestone_id]
status = milestone_system.get_milestone_status()
milestone_status = status["milestones"][milestone_id]
# Check if milestone is unlocked
if not milestone_status["is_unlocked"]:
console.print(Panel(
f"[yellow]Milestone {milestone_id} not yet unlocked.[/yellow]\n\n"
f"[bold]{milestone['emoji']} {milestone['title']}[/bold]\n"
f"[dim]{milestone['victory_condition']}[/dim]\n\n"
f"[cyan]Complete the requirements first:[/cyan]\n"
f"[dim]tito milestone test {milestone_id}[/dim]",
title="Milestone Locked",
border_style="yellow"
))
return 0
# Check if demo file exists
demo_path = Path("capabilities") / milestone["demo_file"]
if not demo_path.exists():
console.print(Panel(
f"[yellow]Demo not available for Milestone {milestone_id}[/yellow]\n\n"
f"Demo file not found: {milestone['demo_file']}\n"
f"[dim]This demo may be coming in a future update.[/dim]",
title="Demo Unavailable",
border_style="yellow"
))
return 0
# Run the demo
console.print(Panel(
f"[bold cyan]🎬 Launching Milestone {milestone_id} Demo[/bold cyan]\n\n"
f"[bold]{milestone['emoji']} {milestone['title']}[/bold]\n"
f"[yellow]Watch your capability in action![/yellow]\n\n"
f"[cyan]Demonstrating: {milestone['capability']}[/cyan]\n"
f"[dim]Running: {milestone['demo_file']}[/dim]",
title="Capability Demo",
border_style="bright_cyan"
))
try:
result = subprocess.run(
[sys.executable, str(demo_path)],
capture_output=False,
text=True
)
if result.returncode == 0:
console.print(Panel(
f"[bold green]✅ Demo completed successfully![/bold green]\n\n"
f"[yellow]You've seen your {milestone['title']} capability in action![/yellow]\n"
f"[cyan]Real-world impact: {milestone['real_world_impact']}[/cyan]",
title="🎉 Demo Complete",
border_style="green"
))
else:
console.print(f"[yellow]⚠️ Demo completed with status: {result.returncode}[/yellow]")
except Exception as e:
console.print(Panel(
f"[red]❌ Error running demo: {e}[/red]\n\n"
f"[dim]You can manually run: python capabilities/{milestone['demo_file']}[/dim]",
title="Demo Error",
border_style="red"
))
return 1
return 0
def _handle_list_command(self, args: Namespace) -> int:
"""Handle milestone list command - show available milestones."""
console = self.console
console.print(Panel(
"[bold cyan]🏆 TinyTorch Milestones[/bold cyan]\n\n"
"[dim]Recreate ML history from 1957 to 2018[/dim]",
title="Available Milestones",
border_style="bright_cyan"
))
# Check module completion status
progress_file = Path(".tito") / "progress.json"
completed_modules = []
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
progress_data = json.load(f)
completed_modules = progress_data.get("completed_modules", [])
except (json.JSONDecodeError, IOError):
pass
# Check milestone completion
milestone_progress = self._get_milestone_progress_data()
completed_milestones = milestone_progress.get("completed_milestones", [])
for milestone_id in sorted(MILESTONE_SCRIPTS.keys()):
milestone = MILESTONE_SCRIPTS[milestone_id]
# Check if prerequisites met
prereqs_met = all(mod in completed_modules for mod in milestone["required_modules"])
is_complete = milestone_id in completed_milestones
# Status indicator
if is_complete:
status_icon = ""
status_color = "green"
status_text = "COMPLETE"
elif prereqs_met:
status_icon = "🎯"
status_color = "yellow"
status_text = "READY TO RUN"
else:
status_icon = "🔒"
status_color = "dim"
status_text = "LOCKED"
# Build display
if args.simple:
console.print(f"[{status_color}]{status_icon} {milestone['id']} - {milestone['name']}[/{status_color}]")
else:
milestone_display = (
f"[{status_color}]{status_icon} {milestone['emoji']} {milestone['name']}[/{status_color}]\n"
f"[bold]{milestone['title']}[/bold]\n"
f"[dim]{milestone['description']}[/dim]\n"
f"[dim]Historical: {milestone['historical_context']}[/dim]\n\n"
)
if prereqs_met and not is_complete:
milestone_display += f"[bold yellow]▶ Run now:[/bold yellow] [cyan]tito milestone run {milestone_id}[/cyan]\n"
elif not prereqs_met:
missing = [str(m) for m in milestone["required_modules"] if m not in completed_modules]
milestone_display += f"[dim]Required: Complete modules {', '.join(missing)}[/dim]\n"
console.print(Panel(
milestone_display.strip(),
title=f"Milestone {milestone['id']} ({milestone['year']})",
border_style=status_color
))
return 0
def _handle_run_command(self, args: Namespace) -> int:
"""Handle milestone run command - run a milestone with checks."""
console = self.console
milestone_id = args.milestone_id
# Validate milestone ID
if milestone_id not in MILESTONE_SCRIPTS:
console.print(Panel(
f"[red]Invalid milestone ID: {milestone_id}[/red]\n\n"
f"Valid milestone IDs: {', '.join(sorted(MILESTONE_SCRIPTS.keys()))}",
title="Invalid Milestone",
border_style="red"
))
return 1
milestone = MILESTONE_SCRIPTS[milestone_id]
# Check if script exists
script_path = Path(milestone["script"])
if not script_path.exists():
console.print(Panel(
f"[red]Milestone script not found![/red]\n\n"
f"Expected: {milestone['script']}\n"
f"[dim]This milestone may not be implemented yet.[/dim]",
title="Script Not Found",
border_style="red"
))
return 1
# Check prerequisites and validate exports/tests (unless skipped)
if not args.skip_checks:
console.print(f"\n[bold cyan]🔍 Checking prerequisites for Milestone {milestone_id}...[/bold cyan]\n")
# Use unified progress tracker
from ..core.progress_tracker import ProgressTracker
from ..core.milestone_validator import MilestoneValidator
from .export import ExportCommand
from .test import TestCommand
tracker = ProgressTracker(self.config.project_root)
validator = MilestoneValidator(self.config.project_root, console)
export_cmd = ExportCommand(self.config)
test_cmd = TestCommand(self.config)
# Check if modules are completed
all_complete, missing_modules = validator.check_prerequisites(milestone_id, tracker)
if not all_complete:
console.print(Panel(
f"[bold yellow]❌ Missing Required Modules[/bold yellow]\n\n"
f"[yellow]Milestone {milestone_id} requires modules: {', '.join(f'{m:02d}' for m in milestone['required_modules'])}[/yellow]\n"
f"[red]Missing: {', '.join(f'{m:02d}' for m in missing_modules)}[/red]\n\n"
f"[cyan]Complete the missing modules first:[/cyan]\n" +
"\n".join(f"[dim] tito module complete {m:02d}[/dim]" for m in missing_modules[:3]),
title="Prerequisites Not Met",
border_style="yellow"
))
return 1
console.print(f"[green]✅ All required modules completed![/green]\n")
# Validate that all modules are exported and tested
console.print(f"[bold cyan]🔧 Validating exports and tests...[/bold cyan]\n")
success, failed = validator.validate_and_export_modules(milestone_id, export_cmd, test_cmd)
if not success:
console.print(Panel(
f"[bold red]❌ Validation Failed[/bold red]\n\n"
f"[yellow]Some required modules failed export or tests:[/yellow]\n"
f"[red]{', '.join(failed)}[/red]\n\n"
f"[cyan]Fix the issues and try again:[/cyan]\n"
f"[dim] tito module complete XX[/dim] - Re-export and test modules",
title="Validation Failed",
border_style="red"
))
return 1
console.print(f"\n[green]✅ All modules exported and tested! Ready to run milestone.[/green]\n")
# Test imports work
console.print("[bold cyan]🧪 Testing YOUR implementations...[/bold cyan]\n")
# Try importing key components (basic check)
try:
import sys as _sys
_sys.path.insert(0, str(Path.cwd()))
if 1 in milestone["required_modules"]:
from tinytorch import Tensor
console.print(" [green]✓[/green] Tensor import successful")
if 2 in milestone["required_modules"]:
from tinytorch import ReLU
console.print(" [green]✓[/green] Activations import successful")
if 3 in milestone["required_modules"]:
from tinytorch import Linear
console.print(" [green]✓[/green] Layers import successful")
console.print(f"\n[green]✅ YOUR TinyTorch is ready![/green]\n")
except ImportError as e:
console.print(Panel(
f"[red]Import Error![/red]\n\n"
f"[yellow]Error: {e}[/yellow]\n\n"
f"[dim]Your modules may not be exported correctly.[/dim]\n"
f"[dim]Try re-exporting: tito module complete XX[/dim]",
title="Import Test Failed",
border_style="red"
))
return 1
# Show milestone banner
console.print(Panel(
f"[bold magenta]╔════════════════════════════════════════════════╗[/bold magenta]\n"
f"[bold magenta]║[/bold magenta] {milestone['emoji']} Milestone {milestone_id}: {milestone['name']:<30} [bold magenta]║[/bold magenta]\n"
f"[bold magenta]║[/bold magenta] {milestone['title']:<44} [bold magenta]║[/bold magenta]\n"
f"[bold magenta]╚════════════════════════════════════════════════╝[/bold magenta]\n\n"
f"[bold]📚 Historical Context:[/bold]\n"
f"{milestone['historical_context']}\n\n"
f"[bold]🎯 What You'll Do:[/bold]\n"
f"{milestone['description']}\n\n"
f"[bold]📂 Running:[/bold] {milestone['script']}\n\n"
f"[dim]All code uses YOUR TinyTorch implementations![/dim]",
title=f"🏆 Milestone {milestone_id} ({milestone['year']})",
border_style="bright_magenta",
padding=(1, 2)
))
try:
input("\n[yellow]Press Enter to begin...[/yellow] ")
except EOFError:
# Non-interactive mode, proceed automatically
pass
# Run the milestone script
console.print(f"\n[bold green]🚀 Starting Milestone {milestone_id}...[/bold green]\n")
console.print("" * 80 + "\n")
try:
result = subprocess.run(
[sys.executable, str(script_path)],
capture_output=False,
text=True
)
console.print("\n" + "" * 80)
if result.returncode == 0:
# Success! Mark milestone as complete
self._mark_milestone_complete(milestone_id)
# Also update unified progress tracker
try:
from ..core.progress_tracker import ProgressTracker
tracker = ProgressTracker(self.config.project_root)
tracker.mark_milestone_completed(milestone_id)
except Exception:
pass # Non-critical
console.print(Panel(
f"[bold green]🏆 MILESTONE ACHIEVED![/bold green]\n\n"
f"[green]You completed Milestone {milestone_id}: {milestone['name']}[/green]\n"
f"[yellow]{milestone['title']}[/yellow]\n\n"
f"[bold]What makes this special:[/bold]\n"
f"• Every line of code: YOUR implementations\n"
f"• Every tensor operation: YOUR Tensor class\n"
f"• Every gradient: YOUR autograd\n\n"
f"[cyan]Achievement saved to your progress![/cyan]",
title="✨ Achievement Unlocked ✨",
border_style="bright_green",
padding=(1, 2)
))
# Show next steps
next_id = str(int(milestone_id) + 1).zfill(2)
if next_id in MILESTONE_SCRIPTS:
next_milestone = MILESTONE_SCRIPTS[next_id]
console.print(f"\n[bold yellow]🎯 What's Next:[/bold yellow]")
console.print(f"[dim]Milestone {next_id}: {next_milestone['name']} ({next_milestone['year']})[/dim]")
# Get completed modules for checking next milestone
progress_file = Path(".tito") / "progress.json"
completed_modules = []
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
progress_data = json.load(f)
for mod in progress_data.get("completed_modules", []):
try:
completed_modules.append(int(mod.split("_")[0]))
except (ValueError, IndexError):
pass
except (json.JSONDecodeError, IOError):
pass
# Check if unlocked
missing = [m for m in next_milestone["required_modules"] if m not in completed_modules]
if missing:
console.print(f"[dim]Unlock by completing modules: {', '.join(f'{m:02d}' for m in missing[:3])}[/dim]")
else:
console.print(f"[green]Ready to run: tito milestone run {next_id}[/green]")
return 0
else:
console.print(f"[yellow]⚠️ Milestone completed with errors (exit code: {result.returncode})[/yellow]")
return result.returncode
except KeyboardInterrupt:
console.print(f"\n\n[yellow]⚠️ Milestone interrupted by user[/yellow]")
return 130
except Exception as e:
console.print(Panel(
f"[red]❌ Error running milestone: {e}[/red]\n\n"
f"[dim]You can try running manually:[/dim]\n"
f"[dim]python {milestone['script']}[/dim]",
title="Execution Error",
border_style="red"
))
return 1
def _handle_info_command(self, args: Namespace) -> int:
"""Handle milestone info command - show detailed information."""
console = self.console
milestone_id = args.milestone_id
if milestone_id not in MILESTONE_SCRIPTS:
console.print(Panel(
f"[red]Invalid milestone ID: {milestone_id}[/red]\n\n"
f"Valid milestone IDs: {', '.join(sorted(MILESTONE_SCRIPTS.keys()))}",
title="Invalid Milestone",
border_style="red"
))
return 1
milestone = MILESTONE_SCRIPTS[milestone_id]
# Check status
progress_file = Path(".tito") / "progress.json"
completed_modules = []
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
progress_data = json.load(f)
completed_modules = progress_data.get("completed_modules", [])
except:
pass
prereqs_met = all(m in completed_modules for m in milestone["required_modules"])
# Display detailed info
info_text = (
f"[bold cyan]{milestone['emoji']} {milestone['name']} ({milestone['year']})[/bold cyan]\n\n"
f"[bold]{milestone['title']}[/bold]\n\n"
f"[yellow]📚 Historical Context:[/yellow]\n"
f"{milestone['historical_context']}\n\n"
f"[yellow]🎯 Description:[/yellow]\n"
f"{milestone['description']}\n\n"
f"[yellow]📋 Required Modules:[/yellow]\n"
)
for mod in milestone["required_modules"]:
if mod in completed_modules:
info_text += f" [green]✓[/green] Module {mod:02d}\n"
else:
info_text += f" [red]✗[/red] Module {mod:02d}\n"
info_text += f"\n[yellow]📂 Script:[/yellow] {milestone['script']}\n"
if prereqs_met:
info_text += f"\n[bold green]✅ Ready to run![/bold green]\n[cyan]tito milestone run {milestone_id}[/cyan]"
else:
missing = [m for m in milestone["required_modules"] if m not in completed_modules]
info_text += f"\n[bold yellow]🔒 Locked[/bold yellow]\nComplete modules: {', '.join(f'{m:02d}' for m in missing)}"
console.print(Panel(
info_text,
title=f"Milestone {milestone_id} Information",
border_style="bright_cyan",
padding=(1, 2)
))
return 0
def _mark_milestone_complete(self, milestone_id: str) -> None:
"""Mark a milestone as complete in progress tracking."""
progress = self._get_milestone_progress_data()
if milestone_id not in progress.get("completed_milestones", []):
if "completed_milestones" not in progress:
progress["completed_milestones"] = []
progress["completed_milestones"].append(milestone_id)
progress["completion_dates"] = progress.get("completion_dates", {})
progress["completion_dates"][milestone_id] = datetime.now().isoformat()
self._save_milestone_progress_data(progress)
def _get_milestone_progress_data(self) -> dict:
"""Get or create milestone progress data."""
progress_dir = Path(".tito")
progress_file = progress_dir / "milestones.json"
progress_dir.mkdir(exist_ok=True)
if progress_file.exists():
try:
with open(progress_file, 'r') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
pass
return {
"completed_milestones": [],
"completion_dates": {},
"unlocked_milestones": [],
"unlock_dates": {},
"total_unlocked": 0,
"achievements": []
}
def _save_milestone_progress_data(self, milestone_data: dict) -> None:
"""Save milestone progress data."""
progress_dir = Path(".tito")
progress_file = progress_dir / "milestones.json"
progress_dir.mkdir(exist_ok=True)
try:
with open(progress_file, 'w') as f:
json.dump(milestone_data, f, indent=2)
except IOError:
pass