Adds CLI tool for project setup and management

Introduces a command-line interface (CLI) named 'tito'
to streamline project setup, testing, and information retrieval.

Includes a setup script to automate virtual environment
creation and dependency installation.

Improves the user experience by providing clear instructions
and status indicators within the CLI.
This commit is contained in:
Vijay Janapa Reddi
2025-07-09 17:03:44 -04:00
parent cbeaf866e9
commit 5cf210268e
5 changed files with 564 additions and 134 deletions

29
bin/activate-tinytorch.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# TinyTorch Environment Activation & Setup
# Check if virtual environment exists, create if not
if [ ! -d "tinytorch-env" ]; then
echo "🆕 First time setup - creating environment..."
python3 -m venv tinytorch-env || {
echo "❌ Failed to create virtual environment"
exit 1
}
echo "📦 Installing dependencies..."
tinytorch-env/bin/pip install -r requirements.txt || {
echo "❌ Failed to install dependencies"
exit 1
}
echo "✅ Environment created!"
fi
echo "🔥 Activating TinyTorch environment..."
source tinytorch-env/bin/activate
# Create tito alias for convenience
alias tito="python3 bin/tito.py"
echo "✅ Ready to build ML systems!"
echo "💡 Quick commands:"
echo " tito info - Check system status"
echo " tito test - Run tests"
echo " tito doctor - Diagnose issues"

View File

@@ -12,119 +12,517 @@ import sys
import os
import argparse
from pathlib import Path
import numpy as np
# Rich imports for beautiful terminal output
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from rich.tree import Tree
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
# Add the project root to Python path
sys.path.insert(0, str(Path(__file__).parent.parent))
# Create Rich console instance
console = Console()
def print_banner():
"""Print the TinyTorch banner."""
print("🔥 Tiny🔥Torch: Build ML Systems from Scratch 🔥")
print("=" * 50)
"""Print the TinyTorch banner using Rich."""
banner_text = Text("Tiny🔥Torch: Build ML Systems from Scratch", style="bold red")
console.print(Panel(banner_text, style="bright_blue", padding=(1, 2)))
def check_setup_status():
"""Check if setup project is complete."""
try:
from tinytorch.core.utils import hello_tinytorch
return "✅ Implemented"
except ImportError:
return "❌ Not Implemented"
def check_tensor_status():
"""Check if tensor project is complete."""
try:
from tinytorch.core.tensor import Tensor
# Test actual functionality, not just import
t1 = Tensor([1, 2, 3])
t2 = Tensor([4, 5, 6])
result = t1 + t2 # Should work if implemented
return "✅ Implemented"
except (ImportError, NotImplementedError):
return "⏳ Not Started"
def check_mlp_status():
"""Check if MLP project is complete."""
try:
from tinytorch.core.modules import MLP
# Test actual functionality
mlp = MLP(input_size=10, hidden_size=5, output_size=2)
from tinytorch.core.tensor import Tensor
x = Tensor([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]])
output = mlp(x) # Should work if implemented
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_cnn_status():
"""Check if CNN project is complete."""
try:
from tinytorch.core.modules import Conv2d
# Test actual functionality
conv = Conv2d(in_channels=3, out_channels=16, kernel_size=3)
from tinytorch.core.tensor import Tensor
x = Tensor(np.random.randn(1, 3, 32, 32))
output = conv(x) # Should work if implemented
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_data_status():
"""Check if data project is complete."""
try:
from tinytorch.core.dataloader import DataLoader
# Test actual functionality
data = [(np.random.randn(3, 32, 32), 0) for _ in range(10)]
loader = DataLoader(data, batch_size=2, shuffle=True)
batch = next(iter(loader)) # Should work if implemented
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError, StopIteration):
return "⏳ Not Started"
def check_training_status():
"""Check if training project is complete."""
try:
from tinytorch.core.optimizer import SGD
from tinytorch.core.tensor import Tensor
# Test actual functionality
t = Tensor([1.0, 2.0, 3.0], requires_grad=True)
optimizer = SGD([t], lr=0.01)
t.backward() # Should work if autograd is implemented
optimizer.step() # Should work if optimizer is implemented
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_profiling_status():
"""Check if profiling project is complete."""
try:
from tinytorch.core.profiler import Profiler
# Test basic functionality
profiler = Profiler()
profiler.start("test")
profiler.end("test")
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_compression_status():
"""Check if compression project is complete."""
try:
from tinytorch.core.compression import Pruner
# Test basic functionality
pruner = Pruner(sparsity=0.5)
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_kernels_status():
"""Check if kernels project is complete."""
try:
from tinytorch.core.kernels import optimized_matmul
# Test basic functionality
a = np.random.randn(3, 3)
b = np.random.randn(3, 3)
result = optimized_matmul(a, b)
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_benchmarking_status():
"""Check if benchmarking project is complete."""
try:
from tinytorch.core.benchmark import Benchmark
# Test basic functionality
benchmark = Benchmark()
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError):
return "⏳ Not Started"
def check_mlops_status():
"""Check if MLOps project is complete."""
try:
from tinytorch.core.mlops import ModelMonitor
from tinytorch.core.tensor import Tensor
# Test that the methods are actually implemented, not just placeholders
monitor = ModelMonitor(model=None, baseline_metrics={})
# Try to use a method that should be implemented
test_inputs = Tensor([1.0, 2.0, 3.0])
test_predictions = Tensor([0.5, 0.8, 0.2])
monitor.log_prediction(test_inputs, test_predictions)
return "✅ Implemented"
except (ImportError, NotImplementedError, AttributeError, TypeError):
return "⏳ Not Started"
def validate_environment():
"""Validate environment setup before running commands."""
issues = []
# Check virtual environment
in_venv = (hasattr(sys, 'real_prefix') or
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))
if not in_venv:
issues.append("Virtual environment not activated. Run: source tinytorch-env/bin/activate")
# Check Python version
if sys.version_info < (3, 8):
issues.append(f"Python 3.8+ required, found {sys.version_info.major}.{sys.version_info.minor}")
# Check core dependencies
try:
import numpy
import pytest
except ImportError as e:
issues.append(f"Missing dependency: {e.name}. Run: pip install -r requirements.txt")
# Check TinyTorch package structure
tinytorch_path = Path(__file__).parent.parent / "tinytorch"
if not tinytorch_path.exists():
issues.append("TinyTorch package not found. Check project structure.")
if issues:
issue_text = Text()
issue_text.append("❌ Environment Issues Detected\n\n", style="bold red")
for issue in issues:
issue_text.append(f"{issue}\n", style="yellow")
issue_text.append("\nRun 'python3 bin/tito.py doctor' for detailed diagnosis", style="dim")
console.print(Panel(issue_text, title="Environment Problems", border_style="red"))
return False
return True
def cmd_version(args):
"""Show TinyTorch version."""
print("Tiny🔥Torch v0.1.0")
print("Machine Learning Systems Course")
version_text = Text()
version_text.append("Tiny🔥Torch v0.1.0\n", style="bold red")
version_text.append("Machine Learning Systems Course", style="cyan")
console.print(Panel(version_text, title="Version Info", border_style="bright_blue"))
def cmd_info(args):
"""Show system information and status."""
print_banner()
print()
console.print()
# Python environment info
print("📋 System Information")
print("-" * 30)
print(f"Python: {sys.version.split()[0]}")
print(f"Platform: {sys.platform}")
print(f"Working Directory: {os.getcwd()}")
# System Information Panel
info_text = Text()
info_text.append(f"Python: {sys.version.split()[0]}\n", style="cyan")
info_text.append(f"Platform: {sys.platform}\n", style="cyan")
info_text.append(f"Working Directory: {os.getcwd()}\n", style="cyan")
# Virtual environment check
in_venv = (hasattr(sys, 'real_prefix') or
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix))
venv_status = "✅ Active" if in_venv else "❌ Not Active"
print(f"Virtual Environment: {venv_status}")
# Virtual environment check - use same robust detection as doctor
venv_path = Path("tinytorch-env")
venv_exists = venv_path.exists()
in_venv = (
# Method 1: Check VIRTUAL_ENV environment variable (most reliable for activation)
os.environ.get('VIRTUAL_ENV') is not None or
# Method 2: Check sys.prefix vs sys.base_prefix (works for running Python in venv)
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix) or
# Method 3: Check for sys.real_prefix (older Python versions)
hasattr(sys, 'real_prefix')
)
print()
if venv_exists and in_venv:
venv_style = "green"
venv_icon = ""
venv_status = "Ready & Active"
elif venv_exists:
venv_style = "yellow"
venv_icon = ""
venv_status = "Ready (Not Active)"
else:
venv_style = "red"
venv_icon = ""
venv_status = "Not Found"
# Course navigation
print("📋 Course Navigation")
print("-" * 30)
print("📖 Course Overview: README.md")
print("🎯 Detailed Guide: COURSE_GUIDE.md")
print("🚀 Start Here: projects/setup/README.md")
info_text.append(f"Virtual Environment: {venv_icon} ", style=venv_style)
info_text.append(venv_status, style=f"bold {venv_style}")
print()
console.print(Panel(info_text, title="📋 System Information", border_style="bright_blue"))
console.print()
# Course Navigation Panel
nav_text = Text()
nav_text.append("📖 Course Overview: ", style="dim")
nav_text.append("README.md\n", style="cyan underline")
nav_text.append("🎯 Detailed Guide: ", style="dim")
nav_text.append("COURSE_GUIDE.md\n", style="cyan underline")
nav_text.append("🚀 Start Here: ", style="dim")
nav_text.append("projects/setup/README.md", style="cyan underline")
console.print(Panel(nav_text, title="📋 Course Navigation", border_style="bright_green"))
console.print()
# Implementation status
print("🚀 Implementation Status")
print("-" * 30)
projects = [
("Setup", "hello_tinytorch function", check_setup_status),
("Tensor", "basic tensor operations", check_tensor_status),
("MLP", "multi-layer perceptron (manual)", check_mlp_status),
("CNN", "convolutional networks (basic)", check_cnn_status),
("Data", "data loading pipeline", check_data_status),
("Training", "autograd engine & optimization", check_training_status),
("Profiling", "performance profiling", check_profiling_status),
("Compression", "model compression", check_compression_status),
("Kernels", "custom compute kernels", check_kernels_status),
("Benchmarking", "performance benchmarking", check_benchmarking_status),
("MLOps", "production monitoring", check_mlops_status),
]
# Check if hello function exists
try:
from tinytorch.core.utils import hello_tinytorch
hello_status = "✅ Implemented"
if args.hello:
print(f"Hello Message: {hello_tinytorch()}")
except ImportError:
hello_status = "❌ Not Implemented"
# Project Status Table
status_table = Table(title="🚀 Project Implementation Status", show_header=True, header_style="bold blue")
status_table.add_column("ID", style="dim", width=3, justify="center")
status_table.add_column("Project", style="bold cyan", width=12)
status_table.add_column("Status", width=18, justify="center")
status_table.add_column("Description", style="dim", width=40)
print(f"hello_tinytorch(): {hello_status}")
for i, (name, desc, check_func) in enumerate(projects):
status_text = check_func()
if "" in status_text:
status_style = "[green]✅ Implemented[/green]"
elif "" in status_text:
status_style = "[red]❌ Not Implemented[/red]"
else:
status_style = "[yellow]⏳ Not Started[/yellow]"
status_table.add_row(str(i), name, status_style, desc)
# TODO: Add checks for other components as they're implemented
print("tensor operations: ⏳ Coming in Project 1")
print("autograd engine: ⏳ Coming in Project 4")
print("neural networks: ⏳ Coming in Project 2")
print("training system: ⏳ Coming in Project 6")
console.print(status_table)
if args.hello and check_setup_status() == "✅ Implemented":
try:
from tinytorch.core.utils import hello_tinytorch
hello_text = Text(hello_tinytorch(), style="bold red")
console.print()
console.print(Panel(hello_text, style="bright_red", padding=(1, 2)))
except ImportError:
pass
if args.show_architecture:
print()
print("🏗️ System Architecture")
print("-" * 30)
print("""
┌─────────────────────────────────────────────────────────────┐
TinyTorch System │
├─────────────────────────────────────────────────────────────┤
│ CLI Interface (bin/tito.py) │
├─────────────────────────────────────────────────────────────┤
│ Training Orchestration (trainer.py) │
├─────────────────────────────────────────────────────────────┤
│ Model Definition │ Data Pipeline │ Optimization │
│ (modules.py) │ (dataloader.py) │ (optimizer.py) │
├─────────────────────────────────────────────────────────────┤
│ Automatic Differentiation Engine (autograd) │
├─────────────────────────────────────────────────────────────┤
│ Tensor Operations & Storage (tensor.py) │
├─────────────────────────────────────────────────────────────┤
│ Profiling & MLOps (profiler.py, mlops.py) │
└─────────────────────────────────────────────────────────────┘
""")
console.print()
# Create architecture tree
arch_tree = Tree("🏗️ TinyTorch System Architecture", style="bold blue")
cli_branch = arch_tree.add("CLI Interface", style="cyan")
cli_branch.add("bin/tito.py - Command line tools", style="dim")
training_branch = arch_tree.add("Training Orchestration", style="cyan")
training_branch.add("trainer.py - Training loop management", style="dim")
core_branch = arch_tree.add("Core Components", style="cyan")
model_sub = core_branch.add("Model Definition", style="yellow")
model_sub.add("modules.py - Neural network layers", style="dim")
data_sub = core_branch.add("Data Pipeline", style="yellow")
data_sub.add("dataloader.py - Efficient data loading", style="dim")
opt_sub = core_branch.add("Optimization", style="yellow")
opt_sub.add("optimizer.py - SGD, Adam, etc.", style="dim")
autograd_branch = arch_tree.add("Automatic Differentiation Engine", style="cyan")
autograd_branch.add("autograd.py - Gradient computation", style="dim")
tensor_branch = arch_tree.add("Tensor Operations & Storage", style="cyan")
tensor_branch.add("tensor.py - Core tensor implementation", style="dim")
system_branch = arch_tree.add("System Tools", style="cyan")
system_branch.add("profiler.py - Performance measurement", style="dim")
system_branch.add("mlops.py - Production monitoring", style="dim")
console.print(Panel(arch_tree, title="🏗️ System Architecture", border_style="bright_blue"))
def cmd_test(args):
"""Run tests for a specific project."""
print(f"🧪 Running tests for project: {args.project}")
valid_projects = ["setup", "tensor", "mlp", "cnn", "data", "training",
"profiling", "compression", "kernels", "benchmarking", "mlops"]
if args.project == "setup":
# Run setup tests
if args.all:
# Run all tests with progress bar
import subprocess
test_file = "projects/setup/test_setup.py"
failed_projects = []
# Count existing test files
existing_tests = [p for p in valid_projects if Path(f"projects/{p}/test_{p}.py").exists()]
console.print(Panel(f"🧪 Running tests for {len(existing_tests)} projects",
title="Test Suite", border_style="bright_cyan"))
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
BarColumn(),
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
console=console
) as progress:
task = progress.add_task("Running tests...", total=len(existing_tests))
for project in existing_tests:
progress.update(task, description=f"Testing {project}...")
test_file = f"projects/{project}/test_{project}.py"
result = subprocess.run([sys.executable, "-m", "pytest", test_file, "-v"],
capture_output=True, text=True)
if result.returncode != 0:
failed_projects.append(project)
console.print(f"[red]❌ {project} tests failed[/red]")
else:
console.print(f"[green]✅ {project} tests passed[/green]")
progress.advance(task)
# Results summary
if failed_projects:
console.print(Panel(f"[red]❌ Failed projects: {', '.join(failed_projects)}[/red]",
title="Test Results", border_style="red"))
return 1
else:
console.print(Panel("[green]✅ All tests passed![/green]",
title="Test Results", border_style="green"))
return 0
elif args.project in valid_projects:
# Run specific project tests
import subprocess
test_file = f"projects/{args.project}/test_{args.project}.py"
console.print(Panel(f"🧪 Running tests for project: [bold cyan]{args.project}[/bold cyan]",
title="Single Project Test", border_style="bright_cyan"))
if not Path(test_file).exists():
console.print(Panel(f"[yellow]⏳ Test file not found: {test_file}\n"
f"Project '{args.project}' may not be implemented yet.[/yellow]",
title="Test Not Found", border_style="yellow"))
return 1
console.print(f"[dim]Running: pytest {test_file} -v[/dim]")
console.print()
result = subprocess.run([sys.executable, "-m", "pytest", test_file, "-v"],
capture_output=False)
# Show result summary
if result.returncode == 0:
console.print(Panel(f"[green]✅ All tests passed for {args.project}![/green]",
title="Test Results", border_style="green"))
else:
console.print(Panel(f"[red]❌ Some tests failed for {args.project}[/red]",
title="Test Results", border_style="red"))
return result.returncode
else:
print(f"Tests for project '{args.project}' not yet implemented.")
console.print(Panel(f"[red]❌ Unknown project: {args.project}[/red]\n"
f"Valid projects: {', '.join(valid_projects)}",
title="Invalid Project", border_style="red"))
return 1
def cmd_submit(args):
"""Submit project for grading."""
print(f"📤 Submitting project: {args.project}")
print("🚧 Submission system not yet implemented.")
print("For now, make sure all tests pass with:")
print(f" python -m pytest projects/{args.project}/test_*.py -v")
submit_text = Text()
submit_text.append(f"📤 Submitting project: {args.project}\n\n", style="bold cyan")
submit_text.append("🚧 Submission system not yet implemented.\n\n", style="yellow")
submit_text.append("For now, make sure all tests pass with:\n", style="dim")
submit_text.append(f" python -m pytest projects/{args.project}/test_*.py -v", style="bold white")
console.print(Panel(submit_text, title="Project Submission", border_style="bright_yellow"))
def cmd_status(args):
"""Check project status."""
print(f"📊 Status for project: {args.project}")
print("🚧 Status system not yet implemented.")
status_text = Text()
status_text.append(f"📊 Status for project: {args.project}\n\n", style="bold cyan")
status_text.append("🚧 Status system not yet implemented.", style="yellow")
console.print(Panel(status_text, title="Project Status", border_style="bright_yellow"))
def cmd_doctor(args):
"""Run comprehensive environment diagnosis."""
console.print(Panel("🔬 TinyTorch Environment Diagnosis",
title="System Doctor", border_style="bright_magenta"))
console.print()
# Environment checks table
env_table = Table(title="Environment Check", show_header=True, header_style="bold blue")
env_table.add_column("Component", style="cyan", width=20)
env_table.add_column("Status", justify="left")
env_table.add_column("Details", style="dim", width=30)
# Python environment
env_table.add_row("Python", "[green]✅ OK[/green]", f"{sys.version.split()[0]} ({sys.platform})")
# Virtual environment - check if it exists and if we're using it
venv_path = Path("tinytorch-env")
venv_exists = venv_path.exists()
in_venv = (
# Method 1: Check VIRTUAL_ENV environment variable (most reliable for activation)
os.environ.get('VIRTUAL_ENV') is not None or
# Method 2: Check sys.prefix vs sys.base_prefix (works for running Python in venv)
(hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix) or
# Method 3: Check for sys.real_prefix (older Python versions)
hasattr(sys, 'real_prefix')
)
if venv_exists and in_venv:
venv_status = "[green]✅ Ready & Active[/green]"
elif venv_exists:
venv_status = "[yellow]✅ Ready (Not Active)[/yellow]"
else:
venv_status = "[red]❌ Not Found[/red]"
env_table.add_row("Virtual Environment", venv_status, "tinytorch-env")
# Dependencies
dependencies = ['numpy', 'matplotlib', 'pytest', 'yaml', 'black', 'rich']
for dep in dependencies:
try:
module = __import__(dep)
version = getattr(module, '__version__', 'unknown')
env_table.add_row(dep.title(), "[green]✅ OK[/green]", f"v{version}")
except ImportError:
env_table.add_row(dep.title(), "[red]❌ Missing[/red]", "Not installed")
console.print(env_table)
console.print()
# Project structure table
struct_table = Table(title="Project Structure", show_header=True, header_style="bold magenta")
struct_table.add_column("Path", style="cyan", width=25)
struct_table.add_column("Status", justify="left")
struct_table.add_column("Type", style="dim", width=25)
required_paths = [
('tinytorch/', 'Package directory'),
('tinytorch/core/', 'Core module directory'),
('projects/', 'Project directory'),
('bin/tito.py', 'CLI script'),
('requirements.txt', 'Dependencies file')
]
for path, desc in required_paths:
if Path(path).exists():
struct_table.add_row(path, "[green]✅ Found[/green]", desc)
else:
struct_table.add_row(path, "[red]❌ Missing[/red]", desc)
console.print(struct_table)
console.print()
# Project implementations
console.print(Panel("📋 Implementation Status",
title="Project Status", border_style="bright_blue"))
cmd_info(argparse.Namespace(hello=False, show_architecture=False))
def main():
"""Main CLI entry point."""
@@ -136,6 +534,8 @@ def main():
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# Info command
info_parser = subparsers.add_parser("info", help="Show system information")
info_parser.add_argument("--hello", action="store_true", help="Show hello message")
@@ -143,7 +543,8 @@ def main():
# Test command
test_parser = subparsers.add_parser("test", help="Run project tests")
test_parser.add_argument("--project", required=True, help="Project to test")
test_parser.add_argument("--project", help="Project to test")
test_parser.add_argument("--all", action="store_true", help="Run all project tests")
# Submit command
submit_parser = subparsers.add_parser("submit", help="Submit project")
@@ -153,6 +554,9 @@ def main():
status_parser = subparsers.add_parser("status", help="Check project status")
status_parser.add_argument("--project", required=True, help="Project to check")
# Doctor command
doctor_parser = subparsers.add_parser("doctor", help="Run environment diagnosis")
args = parser.parse_args()
# Handle version flag
@@ -160,6 +564,19 @@ def main():
cmd_version(args)
return 0
# Environment validation (skip for doctor and info commands)
if args.command not in ["doctor", "info"] and not validate_environment():
return 1
# Validate test command arguments
if args.command == "test":
if not args.all and not args.project:
error_text = Text()
error_text.append("❌ Error: Must specify either --project or --all\n\n", style="bold red")
error_text.append("Usage: python bin/tito.py test --project <name> | --all", style="cyan")
console.print(Panel(error_text, title="Invalid Arguments", border_style="red"))
return 1
# Handle commands
if args.command == "info":
cmd_info(args)
@@ -169,6 +586,8 @@ def main():
cmd_submit(args)
elif args.command == "status":
cmd_status(args)
elif args.command == "doctor":
cmd_doctor(args)
else:
parser.print_help()
return 1