chore(pipeline): improve pipeline architecture
- Add capability-based dependency resolution with prefix matching - Add EffectPluginStage with sensor binding support - Add CameraStage adapter for camera integration - Add DisplayStage adapter for display integration - Add Pipeline metrics collection - Add deprecation notices to legacy modules - Update app.py with pipeline integration
This commit is contained in:
@@ -83,12 +83,60 @@ class Pipeline:
|
||||
|
||||
def build(self) -> "Pipeline":
|
||||
"""Build execution order based on dependencies."""
|
||||
self._capability_map = self._build_capability_map()
|
||||
self._execution_order = self._resolve_dependencies()
|
||||
self._validate_dependencies()
|
||||
self._initialized = True
|
||||
return self
|
||||
|
||||
def _build_capability_map(self) -> dict[str, list[str]]:
|
||||
"""Build a map of capabilities to stage names.
|
||||
|
||||
Returns:
|
||||
Dict mapping capability -> list of stage names that provide it
|
||||
"""
|
||||
capability_map: dict[str, list[str]] = {}
|
||||
for name, stage in self._stages.items():
|
||||
for cap in stage.capabilities:
|
||||
if cap not in capability_map:
|
||||
capability_map[cap] = []
|
||||
capability_map[cap].append(name)
|
||||
return capability_map
|
||||
|
||||
def _find_stage_with_capability(self, capability: str) -> str | None:
|
||||
"""Find a stage that provides the given capability.
|
||||
|
||||
Supports wildcard matching:
|
||||
- "source" matches "source.headlines" (prefix match)
|
||||
- "source.*" matches "source.headlines"
|
||||
- "source.headlines" matches exactly
|
||||
|
||||
Args:
|
||||
capability: The capability to find
|
||||
|
||||
Returns:
|
||||
Stage name that provides the capability, or None if not found
|
||||
"""
|
||||
# Exact match
|
||||
if capability in self._capability_map:
|
||||
return self._capability_map[capability][0]
|
||||
|
||||
# Prefix match (e.g., "source" -> "source.headlines")
|
||||
for cap, stages in self._capability_map.items():
|
||||
if cap.startswith(capability + "."):
|
||||
return stages[0]
|
||||
|
||||
# Wildcard match (e.g., "source.*" -> "source.headlines")
|
||||
if ".*" in capability:
|
||||
prefix = capability[:-2] # Remove ".*"
|
||||
for cap in self._capability_map:
|
||||
if cap.startswith(prefix + "."):
|
||||
return self._capability_map[cap][0]
|
||||
|
||||
return None
|
||||
|
||||
def _resolve_dependencies(self) -> list[str]:
|
||||
"""Resolve stage execution order using topological sort."""
|
||||
"""Resolve stage execution order using topological sort with capability matching."""
|
||||
ordered = []
|
||||
visited = set()
|
||||
temp_mark = set()
|
||||
@@ -103,9 +151,10 @@ class Pipeline:
|
||||
stage = self._stages.get(name)
|
||||
if stage:
|
||||
for dep in stage.dependencies:
|
||||
dep_stage = self._stages.get(dep)
|
||||
if dep_stage:
|
||||
visit(dep)
|
||||
# Find a stage that provides this capability
|
||||
dep_stage_name = self._find_stage_with_capability(dep)
|
||||
if dep_stage_name:
|
||||
visit(dep_stage_name)
|
||||
|
||||
temp_mark.remove(name)
|
||||
visited.add(name)
|
||||
@@ -117,6 +166,25 @@ class Pipeline:
|
||||
|
||||
return ordered
|
||||
|
||||
def _validate_dependencies(self) -> None:
|
||||
"""Validate that all dependencies can be satisfied.
|
||||
|
||||
Raises StageError if any dependency cannot be resolved.
|
||||
"""
|
||||
missing: list[tuple[str, str]] = [] # (stage_name, capability)
|
||||
|
||||
for name, stage in self._stages.items():
|
||||
for dep in stage.dependencies:
|
||||
if not self._find_stage_with_capability(dep):
|
||||
missing.append((name, dep))
|
||||
|
||||
if missing:
|
||||
msgs = [f" - {stage} needs {cap}" for stage, cap in missing]
|
||||
raise StageError(
|
||||
"validation",
|
||||
"Missing capabilities:\n" + "\n".join(msgs),
|
||||
)
|
||||
|
||||
def initialize(self) -> bool:
|
||||
"""Initialize all stages in execution order."""
|
||||
for name in self._execution_order:
|
||||
|
||||
Reference in New Issue
Block a user