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
+1
View File
@@ -0,0 +1 @@
from .conditions import *
+10
View File
@@ -0,0 +1,10 @@
"""Edge condition registry utilities."""
from .base import EdgeConditionManager, ConditionFactoryContext
from .registry import build_edge_condition_manager
__all__ = [
"ConditionFactoryContext",
"EdgeConditionManager",
"build_edge_condition_manager",
]
+233
View File
@@ -0,0 +1,233 @@
"""EdgeConditionManager abstraction that unifies edge condition compilation."""
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, Callable, Generic, TypeVar
from entity.messages import Message, MessageRole
from runtime.node.executor import ExecutionContext
from utils.log_manager import LogManager
from utils.structured_logger import get_server_logger
from entity.configs.node.node import EdgeLink, Node
from entity.configs.edge.edge_condition import EdgeConditionTypeConfig
from utils.function_manager import FunctionManager
ConditionEvaluator = Callable[[str], bool]
TConfig = TypeVar("TConfig", bound="EdgeConditionTypeConfig")
@dataclass(slots=True)
class ConditionFactoryContext:
"""Context passed to managers at initialization time."""
function_manager: "FunctionManager | None" = None
log_manager: "LogManager | None" = None
class EdgeConditionManager(Generic[TConfig], ABC):
"""An abstract base class is required, and all edge condition types must implement the process logic."""
def __init__(self, config: TConfig, ctx: ConditionFactoryContext, execution_context: ExecutionContext) -> None:
self.config = config
self.ctx = ctx
self.execution_context = execution_context
@abstractmethod
def process(
self,
edge_link: "EdgeLink",
source_result: Message,
from_node: "Node",
log_manager: LogManager,
) -> None:
"""The execution logic is implemented by the subclasses."""
def transform_payload(
self,
payload: Message,
*,
source_result: Message,
from_node: "Node",
edge_link: "EdgeLink",
log_manager: LogManager,
) -> Message | None:
processor = edge_link.payload_processor
if not processor:
return payload
try:
return processor.transform(
payload,
source_result=source_result,
from_node=from_node,
edge_link=edge_link,
log_manager=log_manager,
context=self.execution_context,
)
except Exception as exc: # pragma: no cover
error_msg = (
f"Edge payload processor failed for {from_node.id}->{edge_link.target.id}: {exc}"
)
if self.ctx and self.ctx.log_manager:
self.ctx.log_manager.error(
error_msg,
details={
"processor_type": getattr(edge_link, "process_type", None),
"processor_metadata": getattr(edge_link, "process_metadata", {}),
},
)
server_logger = get_server_logger()
server_logger.log_exception(
exc,
error_msg,
processor_type=getattr(edge_link, "process_type", None),
processor_metadata=getattr(edge_link, "process_metadata", {}),
)
return payload
# ------------------------------------------------------------------
# internal helpers
# ------------------------------------------------------------------
def _process_with_condition(
self,
evaluator: ConditionEvaluator,
*,
label: str,
metadata: dict[str, Any],
edge_link: "EdgeLink",
source_result: Message,
from_node: "Node",
log_manager: LogManager,
) -> None:
target_node = edge_link.target
log_manager.record_edge_process(from_node.id, target_node.id, edge_link.config)
serialized_source = self._payload_to_text(source_result)
try:
condition_met = bool(evaluator(serialized_source))
except Exception as exc: # pragma: no cover
error_msg = f"Error calling condition '{label}': {exc}"
log_manager.error(
error_msg,
details={
"condition_type": edge_link.condition_type,
"condition_metadata": metadata,
},
)
server_logger = get_server_logger()
server_logger.log_exception(
exc,
error_msg,
condition_type=edge_link.condition_type,
condition_metadata=metadata,
)
condition_met = True
if not condition_met:
log_manager.debug(
f"Edge condition not met for {from_node.id} -> {target_node.id}, skipping edge processing"
)
return
log_manager.info(f"Edge condition met for {from_node.id} -> {target_node.id}")
self._clear_target_context(
target_node,
drop_non_keep=getattr(edge_link, "clear_context", False),
drop_keep=getattr(edge_link, "clear_kept_context", False),
from_node=from_node,
log_manager=log_manager,
)
if edge_link.carry_data:
payload = self._prepare_payload_for_target(source_result, from_node, target_node, edge_link.keep_message)
payload = self.transform_payload(
payload,
source_result=source_result,
from_node=from_node,
edge_link=edge_link,
log_manager=log_manager,
)
if payload is None:
log_manager.debug(
f"Payload processor dropped message for edge {from_node.id} -> {target_node.id}"
)
return
# Tag message with dynamic edge info for later processing
if edge_link.dynamic_config is not None:
metadata = dict(payload.metadata)
metadata["_from_dynamic_edge"] = True
metadata["_dynamic_edge_source"] = from_node.id
payload.metadata = metadata
target_node.append_input(payload)
log_manager.debug(
f"Data passed from {from_node.id} to {target_node.id}'s input queue "
f"(type: {self._describe_payload(payload)})"
)
else:
log_manager.debug(
f"Edge {from_node.id} -> {target_node.id} does not carry data, skipping data transfer"
)
if edge_link.trigger:
edge_link.triggered = True
log_manager.debug(f"Edge {from_node.id} -> {target_node.id} triggered")
def _payload_to_text(self, payload: Any) -> str:
if isinstance(payload, Message):
return payload.text_content()
if payload is None:
return ""
return str(payload)
def _prepare_payload_for_target(
self,
payload: Any,
from_node: Node,
target_node: Node,
keep: bool = False,
) -> Message:
if not isinstance(payload, Message):
payload = Message(role=MessageRole.ASSISTANT, content=str(payload))
cloned = payload.clone()
if not cloned.preserve_role:
cloned_role = MessageRole.ASSISTANT if from_node.id == target_node.id else MessageRole.USER
cloned.role = cloned_role
metadata = dict(cloned.metadata)
metadata["source"] = from_node.id
cloned.metadata = metadata
if keep:
cloned.keep = True
return cloned
def _clear_target_context(
self,
target_node: Node,
*,
drop_non_keep: bool,
drop_keep: bool,
from_node: Node,
log_manager: LogManager,
) -> None:
if not drop_non_keep and not drop_keep:
return
removed_non_keep, removed_keep = target_node.clear_inputs_by_flag(
drop_non_keep=drop_non_keep,
drop_keep=drop_keep,
)
if removed_non_keep or removed_keep:
log_manager.debug(
f"Cleared target context for edge {from_node.id} -> {target_node.id}",
details={
"removed_non_keep": removed_non_keep,
"removed_keep": removed_keep,
"drop_non_keep": drop_non_keep,
"drop_keep": drop_keep,
},
)
def _describe_payload(self, payload: Any) -> str:
if isinstance(payload, Message):
return f"message:{payload.role.value}"
return type(payload).__name__
+21
View File
@@ -0,0 +1,21 @@
"""Register built-in edge condition manager implementations."""
from entity.configs.edge.edge_condition import (
FunctionEdgeConditionConfig,
KeywordEdgeConditionConfig,
)
from runtime.edge.conditions.registry import register_edge_condition
from runtime.edge.conditions.function_manager import FunctionEdgeConditionManager
from runtime.edge.conditions.keyword_manager import KeywordEdgeConditionManager
register_edge_condition(
"function",
manager_cls=FunctionEdgeConditionManager,
summary="Calls registered Python functions from functions/edge (default 'true').",
config_cls=FunctionEdgeConditionConfig
)
register_edge_condition(
"keyword",
manager_cls=KeywordEdgeConditionManager,
summary="Declarative conditions based on include/exclude keywords or regex matching",
config_cls=KeywordEdgeConditionConfig
)
+67
View File
@@ -0,0 +1,67 @@
"""FunctionEdgeManager centralizes function-based edge conditions."""
from typing import Callable, Optional
from entity.configs.edge.edge_condition import FunctionEdgeConditionConfig
from utils.structured_logger import get_server_logger
from .base import ConditionFactoryContext
from .base import EdgeConditionManager
from utils.function_manager import FunctionManager
from ...node.executor import ExecutionContext
class FunctionEdgeConditionManager(EdgeConditionManager[FunctionEdgeConditionConfig]):
def __init__(self, config: FunctionEdgeConditionConfig, ctx: ConditionFactoryContext, execution_context: ExecutionContext) -> None:
super().__init__(config, ctx, execution_context)
self._name = config.name or "true"
self.label = self._name or "true"
self.metadata = {"function": self._name}
self._evaluator = self._build_evaluator()
def _build_evaluator(self) -> Callable[[str], bool]:
if self._name == "true":
return lambda _: True
function_obj = self._resolve_function(self._name, self.ctx.function_manager)
if function_obj is None:
logger = get_server_logger()
logger.warning(f"Edge condition function '{self._name}' not found; defaulting to true")
self.metadata = {"function": self._name, "missing": True}
def missing(_: str) -> bool:
if self.ctx.log_manager:
self.ctx.log_manager.warning(
f"Condition function '{self._name}' not found, defaulting to true"
)
return True
return missing
def evaluator(data: str) -> bool:
return bool(function_obj(data))
return evaluator
def process(
self,
edge_link,
source_result,
from_node,
log_manager,
) -> None:
self._process_with_condition(
self._evaluator,
label=self.label,
metadata=self.metadata,
edge_link=edge_link,
source_result=source_result,
from_node=from_node,
log_manager=log_manager,
)
def _resolve_function(
self, name: str, function_manager: Optional["FunctionManager"]
) -> Optional[Callable[[str], bool]]:
if not function_manager:
return None
return function_manager.get_function(name)
+66
View File
@@ -0,0 +1,66 @@
"""KeywordEdgeManager implements declarative keyword conditions."""
import re
from entity.configs.edge.edge_condition import KeywordEdgeConditionConfig
from .base import ConditionFactoryContext
from .base import EdgeConditionManager
from ...node.executor import ExecutionContext
class KeywordEdgeConditionManager(EdgeConditionManager[KeywordEdgeConditionConfig]):
def __init__(self, config: KeywordEdgeConditionConfig, ctx: ConditionFactoryContext, execution_context: ExecutionContext) -> None:
super().__init__(config, ctx, execution_context)
self.any_keywords = list(config.any_keywords)
self.none_keywords = list(config.none_keywords)
self.regex_patterns = list(config.regex_patterns)
self.case_sensitive = bool(config.case_sensitive)
self.default_value = bool(config.default)
lowered_regex_flags = 0 if self.case_sensitive else re.IGNORECASE
if self.case_sensitive:
self.processed_any = self.any_keywords
self.processed_none = self.none_keywords
else:
self.processed_any = [kw.lower() for kw in self.any_keywords]
self.processed_none = [kw.lower() for kw in self.none_keywords]
self.compiled_regex = [re.compile(pattern, lowered_regex_flags) for pattern in self.regex_patterns]
self.label = f"keyword(any={len(self.any_keywords)}, none={len(self.none_keywords)}, regex={len(self.regex_patterns)})"
self.metadata = {
"any": self.any_keywords,
"none": self.none_keywords,
"regex": self.regex_patterns,
"case_sensitive": self.case_sensitive,
"default": self.default_value,
}
def _evaluate(self, data: str) -> bool:
haystack = data if self.case_sensitive else data.lower()
for keyword in self.processed_none:
if keyword and keyword in haystack:
return False
for keyword in self.processed_any:
if keyword and keyword in haystack:
return True
for pattern in self.compiled_regex:
if pattern.search(data):
return True
if self.any_keywords or self.regex_patterns:
return False
return True
def process(
self,
edge_link,
source_result,
from_node,
log_manager,
) -> None:
self._process_with_condition(
self._evaluate,
label=self.label,
metadata=self.metadata,
edge_link=edge_link,
source_result=source_result,
from_node=from_node,
log_manager=log_manager,
)
+92
View File
@@ -0,0 +1,92 @@
"""Registry helpers for pluggable edge condition managers."""
from dataclasses import dataclass
from importlib import import_module
from typing import Any, Dict, Type
from schema_registry import register_edge_condition_schema
from utils.registry import Registry, RegistryEntry, RegistryError
from entity.configs.edge.edge_condition import EdgeConditionConfig, EdgeConditionTypeConfig
from .base import EdgeConditionManager, ConditionFactoryContext
from ...node.executor import ExecutionContext
@dataclass(slots=True)
class EdgeConditionRegistration:
"""Registry entry describing a pluggable condition type."""
name: str
config_cls: Type["EdgeConditionTypeConfig"]
manager_cls: Type["EdgeConditionManager[Any]"]
summary: str | None = None
edge_condition_registry = Registry("edge_condition_type")
_BUILTINS_LOADED = False
def _ensure_builtins_loaded() -> None:
global _BUILTINS_LOADED
if not _BUILTINS_LOADED:
import_module("runtime.edge.conditions.builtin_types")
_BUILTINS_LOADED = True
def register_edge_condition(
name: str,
*,
config_cls: Type["EdgeConditionTypeConfig"],
manager_cls: Type["EdgeConditionManager[Any]"],
summary: str | None = None,
) -> None:
"""Register a manager class that encapsulates edge processing logic."""
if name in edge_condition_registry.names():
raise RegistryError(f"Edge condition type '{name}' already registered")
entry = EdgeConditionRegistration(
name=name,
config_cls=config_cls,
manager_cls=manager_cls,
summary=summary,
)
edge_condition_registry.register(name, target=entry)
register_edge_condition_schema(name, config_cls=config_cls, summary=summary)
def get_edge_condition_registration(name: str) -> EdgeConditionRegistration:
"""Retrieve a registered condition type."""
_ensure_builtins_loaded()
entry: RegistryEntry = edge_condition_registry.get(name)
registration = entry.load()
if not isinstance(registration, EdgeConditionRegistration):
raise RegistryError(f"Entry '{name}' is not an EdgeConditionRegistration")
return registration
def iter_edge_condition_registrations() -> Dict[str, EdgeConditionRegistration]:
"""Iterate over registered condition types."""
_ensure_builtins_loaded()
return {name: entry.load() for name, entry in edge_condition_registry.items()}
def build_edge_condition_manager(
condition: "EdgeConditionConfig",
context: ConditionFactoryContext,
execution_context: ExecutionContext
) -> "EdgeConditionManager[Any]":
"""Instantiate the manager responsible for a specific edge."""
registration = get_edge_condition_registration(condition.type)
manager_cls = registration.manager_cls
if not manager_cls:
raise RegistryError(
f"Edge condition type '{condition.type}' does not provide a manager implementation"
)
return manager_cls(condition.config, context, execution_context)
__all__ = [
"EdgeConditionRegistration",
"register_edge_condition",
"get_edge_condition_registration",
"iter_edge_condition_registrations",
"build_edge_condition_manager",
]
+10
View File
@@ -0,0 +1,10 @@
"""Public helpers for edge payload processors."""
from .base import EdgePayloadProcessor, ProcessorFactoryContext
from .registry import build_edge_processor
__all__ = [
"EdgePayloadProcessor",
"ProcessorFactoryContext",
"build_edge_processor",
]
+50
View File
@@ -0,0 +1,50 @@
"""Base classes for payload processors applied on edges."""
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, Generic, TypeVar
from entity.messages import Message
from runtime.node.executor import ExecutionContext
from utils.log_manager import LogManager
from utils.function_manager import FunctionManager
TConfig = TypeVar("TConfig")
@dataclass(slots=True)
class ProcessorFactoryContext:
"""Context passed to processor implementations."""
function_manager: "FunctionManager | None" = None
log_manager: "LogManager | None" = None
class EdgePayloadProcessor(Generic[TConfig], ABC):
"""Base payload processor API."""
label: str | None = None
metadata: dict[str, Any] | None = None
def __init__(self, config: TConfig, ctx: ProcessorFactoryContext) -> None:
self.config = config
self.ctx = ctx
@abstractmethod
def transform(
self,
payload: Message,
*,
source_result: Message,
from_node: Any,
edge_link: Any,
log_manager: LogManager,
context: ExecutionContext,
) -> Message | None:
"""Return transformed payload or None to drop the message."""
def _clone(self, payload: Message) -> Message:
return payload.clone()
def _text(self, payload: Message) -> str:
return payload.text_content()
+23
View File
@@ -0,0 +1,23 @@
"""Register built-in edge payload processors."""
from entity.configs.edge.edge_processor import (
RegexEdgeProcessorConfig,
FunctionEdgeProcessorConfig,
)
from .registry import register_edge_processor
from .regex_processor import RegexEdgePayloadProcessor
from .function_processor import FunctionEdgePayloadProcessor
register_edge_processor(
"regex_extract",
processor_cls=RegexEdgePayloadProcessor,
summary="Extract payload fragments via Python regular expressions.",
config_cls=RegexEdgeProcessorConfig,
)
register_edge_processor(
"function",
processor_cls=FunctionEdgePayloadProcessor,
summary="Delegate message transformation to Python functions in functions/edge_processor.",
config_cls=FunctionEdgeProcessorConfig,
)
+76
View File
@@ -0,0 +1,76 @@
"""Payload processor that delegates to Python functions."""
from typing import Any, Callable, Mapping
from entity.messages import Message
from utils.log_manager import LogManager
from utils.structured_logger import get_server_logger
from .base import EdgePayloadProcessor, ProcessorFactoryContext
from entity.configs.edge.edge_processor import FunctionEdgeProcessorConfig
from runtime.node.executor import ExecutionContext
class FunctionEdgePayloadProcessor(EdgePayloadProcessor[FunctionEdgeProcessorConfig]):
def __init__(self, config: FunctionEdgeProcessorConfig, ctx: ProcessorFactoryContext) -> None:
super().__init__(config, ctx)
self._name = config.name
self.label = f"function({self._name})"
self.metadata = {"function": self._name}
self._callable = self._resolve()
def transform(
self,
payload: Message,
*,
source_result: Message,
from_node,
edge_link,
log_manager: LogManager,
context: ExecutionContext,
) -> Message | None:
if self._callable is None:
log_manager.warning(
f"Processor function '{self._name}' not found. Falling back to passthrough."
)
return payload
try:
result = self._callable(payload.text_content(), context.global_state)
except Exception as exc: # pragma: no cover - defensive logging
error_msg = f"Processor function '{self._name}' failed: {exc}"
log_manager.error(error_msg)
server_logger = get_server_logger()
server_logger.log_exception(exc, error_msg, processor_name=self._name)
return payload
if result is None:
return None
return self._coerce_result(payload, result)
def _resolve(self) -> Callable[[str, dict[str, Any]], Any] | None:
manager = self.ctx.function_manager if self.ctx else None
if not manager:
return None
func = manager.get_function(self._name)
if func is None:
return None
return func
def _coerce_result(self, payload: Message, result: Any) -> Message | None:
if isinstance(result, Message):
return result
cloned = payload.clone()
if isinstance(result, str):
cloned.content = result
return cloned
if isinstance(result, Mapping):
if result.get("drop"):
return None
if "content" in result:
cloned.content = result["content"]
metadata = dict(cloned.metadata)
metadata.update(result.get("metadata") or {})
cloned.metadata = metadata
return cloned
cloned.content = str(result)
return cloned
+92
View File
@@ -0,0 +1,92 @@
"""Regex-based payload processor."""
import re
from typing import List
from entity.messages import Message
from utils.log_manager import LogManager
from .base import EdgePayloadProcessor, ProcessorFactoryContext
from entity.configs.edge.edge_processor import RegexEdgeProcessorConfig
from runtime.node.executor import ExecutionContext
class RegexEdgePayloadProcessor(EdgePayloadProcessor[RegexEdgeProcessorConfig]):
def __init__(self, config: RegexEdgeProcessorConfig, ctx: ProcessorFactoryContext) -> None:
super().__init__(config, ctx)
flags = 0
if not config.case_sensitive:
flags |= re.IGNORECASE
if config.multiline:
flags |= re.MULTILINE
if config.dotall:
flags |= re.DOTALL
self._pattern = re.compile(config.pattern, flags)
self.label = f"regex({config.pattern})"
self.metadata = {
"pattern": config.pattern,
"group": config.group,
"multiple": config.multiple,
}
def transform(
self,
payload: Message,
*,
source_result: Message,
from_node,
edge_link,
log_manager: LogManager,
context: ExecutionContext,
) -> Message | None:
matches = list(self._pattern.finditer(self._text(payload)))
if not matches:
return self._handle_no_match(payload, log_manager, from_node.id, edge_link.target.id)
extracted = self._extract_values(matches)
if not extracted:
return self._handle_no_match(payload, log_manager, from_node.id, edge_link.target.id)
cloned = payload.clone()
if self.config.multiple:
cloned.content = "\n".join(extracted)
else:
cloned.content = extracted[0]
return cloned
def _extract_values(self, matches: List[re.Match]) -> List[str]:
values: List[str] = []
for match in matches:
group = self.config.group
if group is None:
value = match.group(0)
else:
value = match.group(group)
if value is None:
continue
if self.config.template:
value = self.config.template.replace("{match}", value)
values.append(value)
if not self.config.multiple:
break
return values
def _handle_no_match(
self,
payload: Message,
log_manager: LogManager,
from_node_id: str,
to_node_id: str,
) -> Message | None:
behavior = self.config.on_no_match
if behavior == "drop":
log_manager.debug(
f"Regex processor dropped payload for {from_node_id}->{to_node_id} (no match)"
)
return None
if behavior == "default":
default_value = self.config.default_value or ""
cloned = payload.clone()
cloned.content = default_value
return cloned
return payload
+81
View File
@@ -0,0 +1,81 @@
"""Registry helpers for edge payload processors."""
from dataclasses import dataclass
from importlib import import_module
from typing import Any, Dict, Type
from schema_registry import register_edge_processor_schema
from utils.registry import Registry, RegistryEntry, RegistryError
from entity.configs.edge.edge_processor import EdgeProcessorConfig, EdgeProcessorTypeConfig
from .base import EdgePayloadProcessor, ProcessorFactoryContext
@dataclass(slots=True)
class EdgeProcessorRegistration:
name: str
config_cls: Type["EdgeProcessorTypeConfig"]
processor_cls: Type["EdgePayloadProcessor[Any]"]
summary: str | None = None
edge_processor_registry = Registry("edge_processor_type")
_BUILTINS_LOADED = False
def _ensure_builtins_loaded() -> None:
global _BUILTINS_LOADED
if not _BUILTINS_LOADED:
import_module("runtime.edge.processors.builtin_types")
_BUILTINS_LOADED = True
def register_edge_processor(
name: str,
*,
config_cls: Type["EdgeProcessorTypeConfig"],
processor_cls: Type["EdgePayloadProcessor[Any]"],
summary: str | None = None,
) -> None:
if name in edge_processor_registry.names():
raise RegistryError(f"Edge processor type '{name}' already registered")
entry = EdgeProcessorRegistration(
name=name,
config_cls=config_cls,
processor_cls=processor_cls,
summary=summary,
)
edge_processor_registry.register(name, target=entry)
register_edge_processor_schema(name, config_cls=config_cls, summary=summary)
def get_edge_processor_registration(name: str) -> EdgeProcessorRegistration:
_ensure_builtins_loaded()
entry: RegistryEntry = edge_processor_registry.get(name)
registration = entry.load()
if not isinstance(registration, EdgeProcessorRegistration):
raise RegistryError(f"Entry '{name}' is not an EdgeProcessorRegistration")
return registration
def iter_edge_processor_registrations() -> Dict[str, EdgeProcessorRegistration]:
_ensure_builtins_loaded()
return {name: entry.load() for name, entry in edge_processor_registry.items()}
def build_edge_processor(
processor_config: "EdgeProcessorConfig",
context: ProcessorFactoryContext,
) -> "EdgePayloadProcessor[Any]":
registration = get_edge_processor_registration(processor_config.type)
processor_cls = registration.processor_cls
if not processor_cls:
raise RegistryError(f"Edge processor type '{processor_config.type}' does not provide an implementation")
return processor_cls(processor_config.config, context)
__all__ = [
"register_edge_processor",
"get_edge_processor_registration",
"iter_edge_processor_registrations",
"build_edge_processor",
]