fix: assemble variable support nested node format

This commit is contained in:
Novice
2026-01-23 17:22:28 +08:00
parent 6551814396
commit e9f4bde18f
3 changed files with 32 additions and 64 deletions

View File

@ -8,31 +8,16 @@ from pydantic_core.core_schema import ValidationInfo
from core.tools.entities.tool_entities import ToolProviderType
from core.workflow.nodes.base.entities import BaseNodeData
# Pattern to match nested_node value format: {{@node.context@}}instruction
# The placeholder {{@node.context@}} must appear at the beginning
# Format: {{@agent_node_id.context@}} where agent_node_id is dynamic, context is fixed
NESTED_NODE_VALUE_PATTERN = re.compile(r"^\{\{@([a-zA-Z0-9_]+)\.context@\}\}(.*)$", re.DOTALL)
# Pattern to match mention format: {{@node.context@}}instruction
MENTION_VALUE_PATTERN = re.compile(r"^\{\{@([a-zA-Z0-9_]+)\.context@\}\}(.*)$", re.DOTALL)
# Pattern to match variable format: {{#node_id.variable#}}
VARIABLE_VALUE_PATTERN = re.compile(r"^\{\{#([a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z_][a-zA-Z0-9_]{0,29}){1,10})#\}\}$")
def parse_nested_node_value(value: str) -> tuple[str, str]:
"""Parse nested_node value into (node_id, instruction).
Args:
value: The nested_node value string like "{{@llm.context@}}extract keywords"
Returns:
Tuple of (node_id, instruction)
Raises:
ValueError: If value format is invalid
"""
match = NESTED_NODE_VALUE_PATTERN.match(value)
if not match:
raise ValueError(
"For nested_node type, value must start with {{@node.context@}} placeholder, "
"e.g., '{{@llm.context@}}extract keywords'"
)
return match.group(1), match.group(2)
def is_variable_format(value: str) -> bool:
"""Check if value is variable format {{#node_id.variable#}}."""
return VARIABLE_VALUE_PATTERN.match(value) is not None
class NestedNodeConfig(BaseModel):
@ -127,12 +112,11 @@ class ToolNodeData(BaseNodeData, ToolEntity):
if not isinstance(value, str):
raise ValueError("value must be a string for nested_node type")
# For nested_node type, value must match format: {{@node.context@}}instruction
# This will raise ValueError if format is invalid
parse_nested_node_value(value)
# nested_node_config is required for nested_node type
if self.nested_node_config is None:
raise ValueError("nested_node_config is required for nested_node type")
# Validate format: must be variable {{#...#}} or mention {{@...@}}
if not is_variable_format(value) and not MENTION_VALUE_PATTERN.match(value):
raise ValueError("value must be variable format {{#node.var#}} or mention format {{@node.context@}}")
return self
tool_parameters: dict[str, ToolInput]

View File

@ -31,7 +31,7 @@ from factories import file_factory
from models import ToolFile
from services.tools.builtin_tools_manage_service import BuiltinToolManageService
from .entities import ToolNodeData
from .entities import ToolNodeData, is_variable_format
from .exc import (
ToolFileError,
ToolNodeError,
@ -213,20 +213,18 @@ class ToolNode(Node[ToolNodeData]):
continue
parameter_value = variable.value
elif tool_input.type == "nested_node":
# Nested node type: get value from extractor node's output
if tool_input.nested_node_config is None:
raise ToolParameterError(
f"nested_node_config is required for nested_node type parameter '{parameter_name}'"
)
nested_node_config = tool_input.nested_node_config.model_dump()
if not isinstance(tool_input.value, str) or tool_input.nested_node_config is None:
raise ToolParameterError(f"Invalid nested_node parameter '{parameter_name}'")
config = tool_input.nested_node_config
# Variable format: use output_selector directly
# Mention format: use extractor_node_id + output_selector
use_extractor = not is_variable_format(tool_input.value)
try:
parameter_value, found = variable_pool.resolve_nested_node(
nested_node_config, parameter_name=parameter_name
config.model_dump(), use_extractor=use_extractor, parameter_name=parameter_name
)
if not found and parameter.required:
raise ToolParameterError(
f"Extractor output not found for required parameter '{parameter_name}'"
)
raise ToolParameterError(f"Value not found for required parameter '{parameter_name}'")
if not found:
continue
except ValueError as e: