feat(workflow): parse and substitute template variables in node configurations
- Implement regex matching for {{xxx}} template variable format.
- Enable recursive parsing of all string template variables within node configurations.
- Resolve and substitute template variables with runtime values during input data extraction.
- Support dynamic parsing and substitution of file selector variables in the document extraction node.
- Make strict template variable mode optional and introduce support for default values.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
@@ -22,6 +23,9 @@ from app.services.multimodal_service import MultimodalService
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 匹配模板变量 {{xxx}} 的正则
|
||||||
|
_TEMPLATE_PATTERN = re.compile(r"\{\{.*?\}\}")
|
||||||
|
|
||||||
|
|
||||||
class NodeExecutionError(Exception):
|
class NodeExecutionError(Exception):
|
||||||
"""节点执行失败异常。
|
"""节点执行失败异常。
|
||||||
@@ -503,10 +507,29 @@ class BaseNode(ABC):
|
|||||||
variable_pool: The variable pool used for reading and writing variables.
|
variable_pool: The variable pool used for reading and writing variables.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A dictionary containing the node's input data.
|
A dictionary containing the node's input data with all template
|
||||||
|
variables resolved to their actual runtime values.
|
||||||
"""
|
"""
|
||||||
# Default implementation returns the node configuration
|
return {"config": self._resolve_config(self.config, variable_pool)}
|
||||||
return {"config": self.config}
|
|
||||||
|
@staticmethod
|
||||||
|
def _resolve_config(config: Any, variable_pool: VariablePool) -> Any:
|
||||||
|
"""递归解析 config 中的模板变量,将 {{xxx}} 替换为实际值。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: 节点的原始配置(可能包含模板变量)。
|
||||||
|
variable_pool: 变量池,用于解析模板变量。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
解析后的配置,所有字符串中的 {{变量}} 已被替换为真实值。
|
||||||
|
"""
|
||||||
|
if isinstance(config, str) and _TEMPLATE_PATTERN.search(config):
|
||||||
|
return BaseNode._render_template(config, variable_pool, strict=False)
|
||||||
|
elif isinstance(config, dict):
|
||||||
|
return {k: BaseNode._resolve_config(v, variable_pool) for k, v in config.items()}
|
||||||
|
elif isinstance(config, list):
|
||||||
|
return [BaseNode._resolve_config(item, variable_pool) for item in config]
|
||||||
|
return config
|
||||||
|
|
||||||
def _extract_output(self, business_result: Any) -> Any:
|
def _extract_output(self, business_result: Any) -> Any:
|
||||||
"""Extracts the actual output from the business result.
|
"""Extracts the actual output from the business result.
|
||||||
|
|||||||
@@ -121,7 +121,10 @@ class DocExtractorNode(BaseNode):
|
|||||||
return business_result
|
return business_result
|
||||||
|
|
||||||
def _extract_input(self, state: WorkflowState, variable_pool: VariablePool) -> dict[str, Any]:
|
def _extract_input(self, state: WorkflowState, variable_pool: VariablePool) -> dict[str, Any]:
|
||||||
return {"file_selector": self.config.get("file_selector")}
|
file_selector = self.config.get("file_selector", "")
|
||||||
|
# 将变量选择器(如 sys.files)解析为实际值
|
||||||
|
resolved = self.get_variable(file_selector, variable_pool, strict=False, default=file_selector)
|
||||||
|
return {"file_selector": resolved}
|
||||||
|
|
||||||
async def execute(self, state: WorkflowState, variable_pool: VariablePool) -> Any:
|
async def execute(self, state: WorkflowState, variable_pool: VariablePool) -> Any:
|
||||||
config = DocExtractorNodeConfig(**self.config)
|
config = DocExtractorNodeConfig(**self.config)
|
||||||
|
|||||||
Reference in New Issue
Block a user