feat(api): preserve variable type while resetting draft variable

pull/20699/head
QuantumGhost 11 months ago
parent 3ec390f6aa
commit 7c7d8ec68a

@ -21,7 +21,7 @@ from core.workflow.enums import SystemVariableKey
from core.workflow.nodes import NodeType
from core.workflow.nodes.variable_assigner.common.helpers import get_updated_variables
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.enums import DraftVariableType
from models.workflow import Workflow, WorkflowDraftVariable, WorkflowNodeExecutionModel, is_system_variable_editable
@ -35,7 +35,15 @@ class WorkflowDraftVariableList:
total: int | None = None
class VariableResetError(Exception):
class WorkflowDraftVariableError(Exception):
pass
class VariableResetError(WorkflowDraftVariableError):
pass
class UpdateNotSupportedError(WorkflowDraftVariableError):
pass
@ -199,6 +207,8 @@ class WorkflowDraftVariableService:
name: str | None = None,
value: Segment | None = None,
) -> WorkflowDraftVariable:
if not variable.editable:
raise UpdateNotSupportedError(f"variable not support updating, id={variable.id}")
if name is not None:
variable.set_name(name)
if value is not None:
@ -208,6 +218,9 @@ class WorkflowDraftVariableService:
return variable
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 = 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_type = workflow.get_node_type_from_node_config(node_config)
# Extract variable value using unified logic
old_value = DraftVariableSaver.extract_variable_value_from_execution_data(
node_exec,
variable.name,
node_type,
)
outputs_dict = node_exec.outputs_dict or {}
if old_value is not None:
variable.set_value(old_value)
variable.last_edited_at = None # Reset to indicate this is a reset operation
self._session.flush()
# 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 variable
else:
if variable.name not in outputs_dict:
# If variable not found in execution data, delete the variable
self._session.delete(instance=variable)
self._session.flush()
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:
variable_type = variable.get_variable_type()
@ -690,37 +709,6 @@ class DraftVariableSaver:
draft_vars = self._build_variables_from_mapping(outputs)
_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
def _should_variable_be_editable(node_id: str, name: str) -> bool:
if node_id in (CONVERSATION_VARIABLE_NODE_ID, ENVIRONMENT_VARIABLE_NODE_ID):

Loading…
Cancel
Save