chore: import upstream snapshot with attribution
Validate YAML Workflows / Validate YAML Configuration Files (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:51 +08:00
commit d0e4308def
614 changed files with 74458 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
"""Runtime utilities for workflow execution."""
from .runtime_context import RuntimeContext
from .runtime_builder import RuntimeBuilder
from .execution_strategy import (
DagExecutionStrategy,
CycleExecutionStrategy,
MajorityVoteStrategy,
)
from .result_archiver import ResultArchiver
__all__ = [
"RuntimeContext",
"RuntimeBuilder",
"DagExecutionStrategy",
"CycleExecutionStrategy",
"MajorityVoteStrategy",
"ResultArchiver",
]
+149
View File
@@ -0,0 +1,149 @@
"""Execution strategies for different graph topologies."""
from collections import Counter
from typing import Callable, Dict, List, Sequence
from entity.configs import Node
from entity.messages import Message
from utils.log_manager import LogManager
from workflow.executor.dag_executor import DAGExecutor
from workflow.executor.cycle_executor import CycleExecutor
from workflow.executor.parallel_executor import ParallelExecutor
class DagExecutionStrategy:
"""Executes acyclic graphs using the DAGExecutor."""
def __init__(
self,
log_manager: LogManager,
nodes: Dict[str, Node],
layers: List[List[str]],
execute_node_func: Callable[[Node], None],
) -> None:
self.log_manager = log_manager
self.nodes = nodes
self.layers = layers
self.execute_node_func = execute_node_func
def run(self) -> None:
dag_executor = DAGExecutor(
log_manager=self.log_manager,
nodes=self.nodes,
layers=self.layers,
execute_node_func=self.execute_node_func,
)
dag_executor.execute()
class CycleExecutionStrategy:
"""Executes graphs containing cycles via CycleExecutor."""
def __init__(
self,
log_manager: LogManager,
nodes: Dict[str, Node],
cycle_execution_order: List[Dict[str, str]],
cycle_manager,
execute_node_func: Callable[[Node], None],
) -> None:
self.log_manager = log_manager
self.nodes = nodes
self.cycle_execution_order = cycle_execution_order
self.cycle_manager = cycle_manager
self.execute_node_func = execute_node_func
def run(self) -> None:
cycle_executor = CycleExecutor(
log_manager=self.log_manager,
nodes=self.nodes,
cycle_execution_order=self.cycle_execution_order,
cycle_manager=self.cycle_manager,
execute_node_func=self.execute_node_func,
)
cycle_executor.execute()
class MajorityVoteStrategy:
"""Executes graphs configured for majority voting (no edges)."""
def __init__(
self,
log_manager: LogManager,
nodes: Dict[str, Node],
initial_messages: Sequence[Message],
execute_node_func: Callable[[Node], None],
payload_to_text_func: Callable[[object], str],
) -> None:
self.log_manager = log_manager
self.nodes = nodes
self.initial_messages = initial_messages
self.execute_node_func = execute_node_func
self.payload_to_text = payload_to_text_func
def run(self) -> str:
self.log_manager.info("Executing graph with majority voting approach")
all_nodes = list(self.nodes.values())
if not all_nodes:
self.log_manager.error("No nodes to execute in majority voting mode")
return ""
for node in all_nodes:
node.clear_input()
for message in self.initial_messages:
node.append_input(message.clone())
node_ids = [node.id for node in all_nodes]
def _execute(node_id: str) -> None:
self.execute_node_func(self.nodes[node_id])
parallel_executor = ParallelExecutor(self.log_manager, self.nodes)
parallel_executor.execute_nodes_parallel(node_ids, _execute)
return self._collect_majority_result()
def _collect_majority_result(self) -> str:
node_outputs: List[Dict[str, str]] = []
for node_id, node in self.nodes.items():
if node.output:
output_text = self.payload_to_text(node.output[-1])
else:
output_text = ""
node_outputs.append(
{
"node_id": node_id,
"node_type": node.node_type,
"output": output_text,
}
)
output_values = [item["output"] for item in node_outputs]
output_counts = Counter(output_values)
non_empty_outputs = [value for value in output_values if value.strip()]
if non_empty_outputs:
output_counts = Counter(non_empty_outputs)
if not output_counts:
self.log_manager.warning("No outputs available for majority voting")
return ""
majority_output, count = output_counts.most_common(1)[0]
self.log_manager.info(
"Majority output determined",
details={"result": majority_output, "votes": count},
)
self.log_manager.info(
"All node outputs",
details={
"outputs": [
(
item["node_id"],
item["output"][:50] + "..." if len(item["output"]) > 50 else item["output"],
)
for item in node_outputs
]
},
)
return majority_output
+32
View File
@@ -0,0 +1,32 @@
"""Utilities for persisting execution artifacts."""
from utils.log_manager import LogManager
from utils.token_tracker import TokenTracker
from workflow.graph_context import GraphContext
class ResultArchiver:
"""Handles post-execution persistence (tokens, logs, metadata)."""
def __init__(
self,
graph: GraphContext,
log_manager: LogManager,
token_tracker: TokenTracker,
) -> None:
self.graph = graph
self.log_manager = log_manager
self.token_tracker = token_tracker
def export(self, final_result: str) -> None:
token_usage_path = self.graph.directory / f"token_usage_{self.graph.name}.json"
self.token_tracker.export_to_file(str(token_usage_path))
self.log_manager.record_workflow_end(
success=True,
details={
"token_usage": self.token_tracker.get_token_usage(),
"final_result": final_result,
},
)
log_file_path = self.graph.directory / "execution_logs.json"
self.log_manager.save_logs(str(log_file_path))
+58
View File
@@ -0,0 +1,58 @@
"""Builder that assembles the runtime context for workflow execution."""
from dataclasses import dataclass
from typing import Any, Dict, Optional
from runtime.node.agent import ToolManager
from utils.attachments import AttachmentStore
from utils.function_manager import EDGE_FUNCTION_DIR, EDGE_PROCESSOR_FUNCTION_DIR, get_function_manager
from utils.log_manager import LogManager
from utils.logger import WorkflowLogger
from utils.token_tracker import TokenTracker
from workflow.graph_context import GraphContext
from .runtime_context import RuntimeContext
@dataclass
class RuntimeBuilder:
"""Constructs RuntimeContext instances for GraphExecutor."""
graph: GraphContext
def build(self, logger: Optional[WorkflowLogger] = None, *, session_id: Optional[str] = None) -> RuntimeContext:
tool_manager = ToolManager()
function_manager = get_function_manager(EDGE_FUNCTION_DIR)
processor_function_manager = get_function_manager(EDGE_PROCESSOR_FUNCTION_DIR)
logger = logger or WorkflowLogger(self.graph.name, self.graph.log_level)
log_manager = LogManager(logger)
token_tracker = TokenTracker(workflow_id=self.graph.name)
code_workspace = (self.graph.directory / "code_workspace").resolve()
code_workspace.mkdir(parents=True, exist_ok=True)
attachments_dir = code_workspace / "attachments"
attachments_dir.mkdir(parents=True, exist_ok=True)
attachment_store = AttachmentStore(attachments_dir)
global_state: Dict[str, Any] = {
"graph_directory": self.graph.directory,
"vars": self.graph.config.vars,
"python_workspace_root": code_workspace,
"attachment_store": attachment_store,
}
context = RuntimeContext(
tool_manager=tool_manager,
function_manager=function_manager,
edge_processor_function_manager=processor_function_manager,
logger=logger,
log_manager=log_manager,
token_tracker=token_tracker,
attachment_store=attachment_store,
code_workspace=code_workspace,
global_state=global_state,
)
context.session_id = session_id
if session_id:
context.global_state.setdefault("session_id", session_id)
return context
+30
View File
@@ -0,0 +1,30 @@
"""Shared runtime context for workflow execution."""
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, Optional
from runtime.node.agent import ToolManager
from utils.function_manager import FunctionManager
from utils.logger import WorkflowLogger
from utils.log_manager import LogManager
from utils.token_tracker import TokenTracker
from utils.attachments import AttachmentStore
@dataclass
class RuntimeContext:
"""Container for runtime-wide dependencies required by GraphExecutor."""
tool_manager: ToolManager
function_manager: FunctionManager
edge_processor_function_manager: FunctionManager
logger: WorkflowLogger
log_manager: LogManager
token_tracker: TokenTracker
attachment_store: AttachmentStore
code_workspace: Path
global_state: Dict[str, Any] = field(default_factory=dict)
cycle_manager: Optional[Any] = None # Late-bound by GraphManager
session_id: Optional[str] = None
workspace_hook: Optional[Any] = None