from __future__ import annotations import functools from typing import Any, Callable from ..nodes import deployment_node, discovery_node, generation_node, validation_node from ..states.ingestion_state import IngestionState class IngestionWorkflow: """LangGraph StateGraph workflow runner for Content Ingestion Agent. Executes the 4-phase contract: Discovery -> Validation -> Generation -> Deployment. """ def __init__( self, scm: Any = None, cms: Any = None, allowed_sources: set[str] | None = None, publish_enabled: bool = False, require_governance_approval: bool = True, ): self.nodes: dict[str, Callable[[IngestionState], dict[str, Any]]] = { "discovery": discovery_node, "validation": functools.partial(validation_node, allowed_sources=allowed_sources), "generation": functools.partial(generation_node, scm=scm), "deployment": functools.partial( deployment_node, cms=cms, publish_enabled=publish_enabled, require_governance_approval=require_governance_approval, ), } def invoke(self, state: IngestionState) -> IngestionState: """Run the workflow sequentially through the 4 phases.""" current_state: IngestionState = dict(state) # type: ignore current_state.setdefault("messages", []) current_state.setdefault("errors", []) current_state.setdefault("has_errors", False) current_state.setdefault("current_step", "discovery") current_state.setdefault("next_step", "discovery") current_node_name = "discovery" while current_node_name and current_node_name != "END": node_fn = self.nodes.get(current_node_name) if not node_fn: break update = node_fn(current_state) current_state.update(update) # type: ignore # Route based on next_step contract next_step = current_state.get("next_step") if not next_step or next_step == "END" or current_state.get("has_errors"): break current_node_name = next_step return current_state def create_ingestion_workflow( scm: Any = None, cms: Any = None, allowed_sources: set[str] | None = None, publish_enabled: bool = False, require_governance_approval: bool = True, ) -> IngestionWorkflow: return IngestionWorkflow( scm=scm, cms=cms, allowed_sources=allowed_sources, publish_enabled=publish_enabled, require_governance_approval=require_governance_approval, )