Files
sideline/engine/pipeline/graph_toml.py
David Gwilliam 6646ed78b3 Add REPL effect detection and input handling to pipeline runner
- Detect REPL effect in pipeline and enable interactive mode
- Enable raw terminal mode for REPL input capture
- Add keyboard input loop for REPL commands (return, up/down arrows, backspace)
- Process commands and handle pipeline mutations from REPL
- Fix lint issues in graph and REPL modules (type annotations, imports)
2026-03-21 21:19:30 -07:00

114 lines
3.0 KiB
Python

"""TOML-based graph configuration loader."""
from pathlib import Path
from typing import Any
import tomllib
from engine.pipeline.graph import Graph, NodeType
from engine.pipeline.graph_adapter import graph_to_pipeline
def load_graph_from_toml(toml_path: str | Path) -> Graph:
"""Load a graph from a TOML file.
Args:
toml_path: Path to the TOML file
Returns:
Graph instance loaded from the TOML file
"""
with open(toml_path, "rb") as f:
data = tomllib.load(f)
return graph_from_dict(data)
def graph_from_dict(data: dict[str, Any]) -> Graph:
"""Create a graph from a dictionary (TOML-compatible structure).
Args:
data: Dictionary with 'nodes' and 'connections' keys
Returns:
Graph instance
"""
graph = Graph()
# Parse nodes
nodes_data = data.get("nodes", {})
for name, node_info in nodes_data.items():
if isinstance(node_info, str):
# Simple format: "source": "headlines"
graph.node(name, NodeType.SOURCE, source=node_info)
elif isinstance(node_info, dict):
# Full format: {"type": "camera", "mode": "scroll"}
node_type = node_info.get("type", "custom")
config = {k: v for k, v in node_info.items() if k != "type"}
graph.node(name, node_type, **config)
# Parse connections
connections_data = data.get("connections", {})
if isinstance(connections_data, dict):
# Format: {"list": ["source -> camera -> display"]}
connections_list = connections_data.get("list", [])
else:
# Format: ["source -> camera -> display"]
connections_list = connections_data
for conn in connections_list:
if isinstance(conn, str):
# Parse "source -> target" format
parts = conn.split("->")
if len(parts) >= 2:
# Connect all nodes in the chain
for i in range(len(parts) - 1):
source = parts[i].strip()
target = parts[i + 1].strip()
graph.connect(source, target)
return graph
def load_pipeline_from_toml(
toml_path: str | Path, viewport_width: int = 80, viewport_height: int = 24
):
"""Load a pipeline from a TOML file.
Args:
toml_path: Path to the TOML file
viewport_width: Terminal width for the pipeline
viewport_height: Terminal height for the pipeline
Returns:
Pipeline instance loaded from the TOML file
"""
graph = load_graph_from_toml(toml_path)
return graph_to_pipeline(graph, viewport_width, viewport_height)
# Example TOML structure:
EXAMPLE_TOML = """
# Graph-based pipeline configuration
[nodes.source]
type = "source"
source = "headlines"
[nodes.camera]
type = "camera"
mode = "scroll"
speed = 1.0
[nodes.noise]
type = "effect"
effect = "noise"
intensity = 0.3
[nodes.display]
type = "display"
backend = "terminal"
[connections]
list = ["source -> camera -> noise -> display"]
"""