|
|
|
@ -21,7 +21,7 @@ from core.workflow.enums import SystemVariableKey
|
|
|
|
from core.workflow.nodes import NodeType
|
|
|
|
from core.workflow.nodes import NodeType
|
|
|
|
from core.workflow.nodes.variable_assigner.common.helpers import get_updated_variables
|
|
|
|
from core.workflow.nodes.variable_assigner.common.helpers import get_updated_variables
|
|
|
|
from core.workflow.variable_loader import VariableLoader
|
|
|
|
from core.workflow.variable_loader import VariableLoader
|
|
|
|
from factories.variable_factory import build_segment, segment_to_variable
|
|
|
|
from factories.variable_factory import build_segment, build_segment_with_type, segment_to_variable
|
|
|
|
from models import App, Conversation
|
|
|
|
from models import App, Conversation
|
|
|
|
from models.enums import DraftVariableType
|
|
|
|
from models.enums import DraftVariableType
|
|
|
|
from models.workflow import Workflow, WorkflowDraftVariable, WorkflowNodeExecutionModel, is_system_variable_editable
|
|
|
|
from models.workflow import Workflow, WorkflowDraftVariable, WorkflowNodeExecutionModel, is_system_variable_editable
|
|
|
|
@ -35,7 +35,15 @@ class WorkflowDraftVariableList:
|
|
|
|
total: int | None = None
|
|
|
|
total: int | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableResetError(Exception):
|
|
|
|
class WorkflowDraftVariableError(Exception):
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableResetError(WorkflowDraftVariableError):
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateNotSupportedError(WorkflowDraftVariableError):
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -199,6 +207,8 @@ class WorkflowDraftVariableService:
|
|
|
|
name: str | None = None,
|
|
|
|
name: str | None = None,
|
|
|
|
value: Segment | None = None,
|
|
|
|
value: Segment | None = None,
|
|
|
|
) -> WorkflowDraftVariable:
|
|
|
|
) -> WorkflowDraftVariable:
|
|
|
|
|
|
|
|
if not variable.editable:
|
|
|
|
|
|
|
|
raise UpdateNotSupportedError(f"variable not support updating, id={variable.id}")
|
|
|
|
if name is not None:
|
|
|
|
if name is not None:
|
|
|
|
variable.set_name(name)
|
|
|
|
variable.set_name(name)
|
|
|
|
if value is not None:
|
|
|
|
if value is not None:
|
|
|
|
@ -208,6 +218,9 @@ class WorkflowDraftVariableService:
|
|
|
|
return variable
|
|
|
|
return variable
|
|
|
|
|
|
|
|
|
|
|
|
def _reset_conv_var(self, workflow: Workflow, variable: WorkflowDraftVariable) -> WorkflowDraftVariable | None:
|
|
|
|
def _reset_conv_var(self, workflow: Workflow, variable: WorkflowDraftVariable) -> WorkflowDraftVariable | None:
|
|
|
|
|
|
|
|
# If a variable does not allow updating, it makes no sence to resetting it.
|
|
|
|
|
|
|
|
if not variable.editable:
|
|
|
|
|
|
|
|
return variable
|
|
|
|
conv_var_by_name = {i.name: i for i in workflow.conversation_variables}
|
|
|
|
conv_var_by_name = {i.name: i for i in workflow.conversation_variables}
|
|
|
|
conv_var = conv_var_by_name.get(variable.name)
|
|
|
|
conv_var = conv_var_by_name.get(variable.name)
|
|
|
|
|
|
|
|
|
|
|
|
@ -250,23 +263,29 @@ class WorkflowDraftVariableService:
|
|
|
|
node_config = workflow.get_node_config_by_id(variable.node_id)
|
|
|
|
node_config = workflow.get_node_config_by_id(variable.node_id)
|
|
|
|
node_type = workflow.get_node_type_from_node_config(node_config)
|
|
|
|
node_type = workflow.get_node_type_from_node_config(node_config)
|
|
|
|
|
|
|
|
|
|
|
|
# Extract variable value using unified logic
|
|
|
|
outputs_dict = node_exec.outputs_dict or {}
|
|
|
|
old_value = DraftVariableSaver.extract_variable_value_from_execution_data(
|
|
|
|
|
|
|
|
node_exec,
|
|
|
|
|
|
|
|
variable.name,
|
|
|
|
|
|
|
|
node_type,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if old_value is not None:
|
|
|
|
# Note: Based on the implementation in `_build_from_variable_assigner_mapping`,
|
|
|
|
variable.set_value(old_value)
|
|
|
|
# VariableAssignerNode (both v1 and v2) can only create conversation draft variables.
|
|
|
|
variable.last_edited_at = None # Reset to indicate this is a reset operation
|
|
|
|
# For consistency, we should simply return when processing VARIABLE_ASSIGNER nodes.
|
|
|
|
self._session.flush()
|
|
|
|
#
|
|
|
|
|
|
|
|
# This implementation must remain synchronized with the `_build_from_variable_assigner_mapping`
|
|
|
|
|
|
|
|
# and `save` methods.
|
|
|
|
|
|
|
|
if node_type == NodeType.VARIABLE_ASSIGNER:
|
|
|
|
return variable
|
|
|
|
return variable
|
|
|
|
else:
|
|
|
|
|
|
|
|
|
|
|
|
if variable.name not in outputs_dict:
|
|
|
|
# If variable not found in execution data, delete the variable
|
|
|
|
# If variable not found in execution data, delete the variable
|
|
|
|
self._session.delete(instance=variable)
|
|
|
|
self._session.delete(instance=variable)
|
|
|
|
self._session.flush()
|
|
|
|
self._session.flush()
|
|
|
|
return None
|
|
|
|
return None
|
|
|
|
|
|
|
|
value = outputs_dict[variable.name]
|
|
|
|
|
|
|
|
value_seg = build_segment_with_type(variable.value_type, _rebuild_file_types_from_dict(value))
|
|
|
|
|
|
|
|
# Extract variable value using unified logic
|
|
|
|
|
|
|
|
variable.set_value(value_seg)
|
|
|
|
|
|
|
|
variable.last_edited_at = None # Reset to indicate this is a reset operation
|
|
|
|
|
|
|
|
self._session.flush()
|
|
|
|
|
|
|
|
return variable
|
|
|
|
|
|
|
|
|
|
|
|
def reset_variable(self, workflow: Workflow, variable: WorkflowDraftVariable) -> WorkflowDraftVariable | None:
|
|
|
|
def reset_variable(self, workflow: Workflow, variable: WorkflowDraftVariable) -> WorkflowDraftVariable | None:
|
|
|
|
variable_type = variable.get_variable_type()
|
|
|
|
variable_type = variable.get_variable_type()
|
|
|
|
@ -690,37 +709,6 @@ class DraftVariableSaver:
|
|
|
|
draft_vars = self._build_variables_from_mapping(outputs)
|
|
|
|
draft_vars = self._build_variables_from_mapping(outputs)
|
|
|
|
_batch_upsert_draft_varaible(self._session, draft_vars)
|
|
|
|
_batch_upsert_draft_varaible(self._session, draft_vars)
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
|
|
def extract_variable_value_from_execution_data(
|
|
|
|
|
|
|
|
node_exec: "WorkflowNodeExecutionModel", variable_name: str, node_type: NodeType
|
|
|
|
|
|
|
|
) -> Segment | None:
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
Extract variable value from execution data using unified logic.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
|
|
node_exec: Workflow node execution record
|
|
|
|
|
|
|
|
variable_name: Name of the variable to extract
|
|
|
|
|
|
|
|
node_type: Type of the workflow node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
Raw variable value if found, None otherwise
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
outputs_dict = node_exec.outputs_dict or {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Note: Based on the implementation in `_build_from_variable_assigner_mapping`,
|
|
|
|
|
|
|
|
# VariableAssignerNode (both v1 and v2) can only create conversation draft variables.
|
|
|
|
|
|
|
|
# For consistency, we should simply return when processing VARIABLE_ASSIGNER nodes.
|
|
|
|
|
|
|
|
#
|
|
|
|
|
|
|
|
# This implementation must remain synchronized with the `_build_from_variable_assigner_mapping`
|
|
|
|
|
|
|
|
# and `save` methods.
|
|
|
|
|
|
|
|
if node_type == NodeType.VARIABLE_ASSIGNER:
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if variable_name not in outputs_dict:
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
value = outputs_dict[variable_name]
|
|
|
|
|
|
|
|
return _build_segment_for_value(value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
|
def _should_variable_be_editable(node_id: str, name: str) -> bool:
|
|
|
|
def _should_variable_be_editable(node_id: str, name: str) -> bool:
|
|
|
|
if node_id in (CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID):
|
|
|
|
if node_id in (CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID):
|
|
|
|
|