#!/usr/bin/env python3 """ Demo script showing the new graph-based DSL for pipeline configuration. This demonstrates how to define pipelines using the graph abstraction, which is more intuitive than the verbose XYZStage naming convention. """ from engine.effects.plugins import discover_plugins from engine.pipeline.graph import Graph, NodeType from engine.pipeline.graph_adapter import graph_to_pipeline, dict_to_pipeline def demo_imperative_api(): """Demo: Imperative Python API for building graphs.""" print("=== Imperative Python API ===") graph = Graph() graph.node("source", NodeType.SOURCE, source="headlines") graph.node("camera", NodeType.CAMERA, mode="scroll", speed=1.0) graph.node("noise", NodeType.EFFECT, effect="noise", intensity=0.3) graph.node("display", NodeType.DISPLAY, backend="null") # Connect nodes in a chain graph.chain("source", "camera", "noise", "display") # Validate the graph errors = graph.validate() if errors: print(f"Validation errors: {errors}") return # Convert to pipeline pipeline = graph_to_pipeline(graph, viewport_width=80, viewport_height=24) print(f"Pipeline created with {len(pipeline._stages)} stages:") for name, stage in pipeline._stages.items(): print(f" - {name}: {stage.__class__.__name__}") return pipeline def demo_dict_api(): """Demo: Dictionary-based API for building graphs.""" print("\n=== Dictionary API ===") data = { "nodes": { "source": "headlines", "camera": {"type": "camera", "mode": "scroll", "speed": 1.0}, "noise": {"type": "effect", "effect": "noise", "intensity": 0.5}, "fade": {"type": "effect", "effect": "fade", "intensity": 0.8}, "display": {"type": "display", "backend": "null"}, }, "connections": ["source -> camera -> noise -> fade -> display"], } pipeline = dict_to_pipeline(data, viewport_width=80, viewport_height=24) print(f"Pipeline created with {len(pipeline._stages)} stages:") for name, stage in pipeline._stages.items(): print(f" - {name}: {stage.__class__.__name__}") return pipeline def demo_graph_validation(): """Demo: Graph validation.""" print("\n=== Graph Validation ===") # Create a graph with a cycle graph = Graph() graph.node("a", NodeType.SOURCE) graph.node("b", NodeType.CAMERA) graph.node("c", NodeType.DISPLAY) graph.connect("a", "b") graph.connect("b", "c") graph.connect("c", "a") # Creates cycle errors = graph.validate() print(f"Cycle detection errors: {errors}") # Create a valid graph graph2 = Graph() graph2.node("source", NodeType.SOURCE, source="headlines") graph2.node("display", NodeType.DISPLAY, backend="null") graph2.connect("source", "display") errors2 = graph2.validate() print(f"Valid graph errors: {errors2}") def demo_node_types(): """Demo: Different node types.""" print("\n=== Node Types ===") graph = Graph() # Source node graph.node("headlines", NodeType.SOURCE, source="headlines") print("✓ Source node created") # Camera node with different modes graph.node("camera_scroll", NodeType.CAMERA, mode="scroll", speed=1.0) graph.node("camera_feed", NodeType.CAMERA, mode="feed", speed=0.5) graph.node("camera_horizontal", NodeType.CAMERA, mode="horizontal", speed=1.0) print("✓ Camera nodes created (scroll, feed, horizontal)") # Effect nodes graph.node("noise", NodeType.EFFECT, effect="noise", intensity=0.3) graph.node("fade", NodeType.EFFECT, effect="fade", intensity=0.8) print("✓ Effect nodes created (noise, fade)") # Positioning node graph.node("position", NodeType.POSITION, mode="mixed") print("✓ Positioning node created") # Display nodes graph.node("terminal", NodeType.DISPLAY, backend="terminal") graph.node("null", NodeType.DISPLAY, backend="null") print("✓ Display nodes created") print(f"\nTotal nodes: {len(graph.nodes)}") if __name__ == "__main__": # Discover effect plugins first discover_plugins() # Run demos demo_imperative_api() demo_dict_api() demo_graph_validation() demo_node_types() print("\n=== Demo Complete ===")