mirror of
https://github.com/langgenius/dify.git
synced 2025-07-03 23:28:18 +00:00

This pull request introduces a feature aimed at improving the debugging experience during workflow editing. With the addition of variable persistence, the system will automatically retain the output variables from previously executed nodes. These persisted variables can then be reused when debugging subsequent nodes, eliminating the need for repetitive manual input. By streamlining this aspect of the workflow, the feature minimizes user errors and significantly reduces debugging effort, offering a smoother and more efficient experience. Key highlights of this change: - Automatic persistence of output variables for executed nodes. - Reuse of persisted variables to simplify input steps for nodes requiring them (e.g., `code`, `template`, `variable_assigner`). - Enhanced debugging experience with reduced friction. Closes #19735.
184 lines
4.2 KiB
Python
184 lines
4.2 KiB
Python
import json
|
|
import sys
|
|
from collections.abc import Mapping, Sequence
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, ConfigDict, field_validator
|
|
|
|
from core.file import File
|
|
|
|
from .types import SegmentType
|
|
|
|
|
|
class Segment(BaseModel):
|
|
model_config = ConfigDict(frozen=True)
|
|
|
|
value_type: SegmentType
|
|
value: Any
|
|
|
|
@field_validator("value_type")
|
|
@classmethod
|
|
def validate_value_type(cls, value):
|
|
"""
|
|
This validator checks if the provided value is equal to the default value of the 'value_type' field.
|
|
If the value is different, a ValueError is raised.
|
|
"""
|
|
if value != cls.model_fields["value_type"].default:
|
|
raise ValueError("Cannot modify 'value_type'")
|
|
return value
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return str(self.value)
|
|
|
|
@property
|
|
def log(self) -> str:
|
|
return str(self.value)
|
|
|
|
@property
|
|
def markdown(self) -> str:
|
|
return str(self.value)
|
|
|
|
@property
|
|
def size(self) -> int:
|
|
"""
|
|
Return the size of the value in bytes.
|
|
"""
|
|
return sys.getsizeof(self.value)
|
|
|
|
def to_object(self) -> Any:
|
|
return self.value
|
|
|
|
|
|
class NoneSegment(Segment):
|
|
value_type: SegmentType = SegmentType.NONE
|
|
value: None = None
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return ""
|
|
|
|
@property
|
|
def log(self) -> str:
|
|
return ""
|
|
|
|
@property
|
|
def markdown(self) -> str:
|
|
return ""
|
|
|
|
|
|
class StringSegment(Segment):
|
|
value_type: SegmentType = SegmentType.STRING
|
|
value: str
|
|
|
|
|
|
class FloatSegment(Segment):
|
|
value_type: SegmentType = SegmentType.NUMBER
|
|
value: float
|
|
# NOTE(QuantumGhost): seems that the equality for FloatSegment with `NaN` value has some problems.
|
|
# The following tests cannot pass.
|
|
#
|
|
# def test_float_segment_and_nan():
|
|
# nan = float("nan")
|
|
# assert nan != nan
|
|
#
|
|
# f1 = FloatSegment(value=float("nan"))
|
|
# f2 = FloatSegment(value=float("nan"))
|
|
# assert f1 != f2
|
|
#
|
|
# f3 = FloatSegment(value=nan)
|
|
# f4 = FloatSegment(value=nan)
|
|
# assert f3 != f4
|
|
|
|
|
|
class IntegerSegment(Segment):
|
|
value_type: SegmentType = SegmentType.NUMBER
|
|
value: int
|
|
|
|
|
|
class ObjectSegment(Segment):
|
|
value_type: SegmentType = SegmentType.OBJECT
|
|
value: Mapping[str, Any]
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return json.dumps(self.model_dump()["value"], ensure_ascii=False)
|
|
|
|
@property
|
|
def log(self) -> str:
|
|
return json.dumps(self.model_dump()["value"], ensure_ascii=False, indent=2)
|
|
|
|
@property
|
|
def markdown(self) -> str:
|
|
return json.dumps(self.model_dump()["value"], ensure_ascii=False, indent=2)
|
|
|
|
|
|
class ArraySegment(Segment):
|
|
@property
|
|
def markdown(self) -> str:
|
|
items = []
|
|
for item in self.value:
|
|
items.append(str(item))
|
|
return "\n".join(items)
|
|
|
|
|
|
class FileSegment(Segment):
|
|
value_type: SegmentType = SegmentType.FILE
|
|
value: File
|
|
|
|
@property
|
|
def markdown(self) -> str:
|
|
return self.value.markdown
|
|
|
|
@property
|
|
def log(self) -> str:
|
|
return ""
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return ""
|
|
|
|
|
|
class ArrayAnySegment(ArraySegment):
|
|
value_type: SegmentType = SegmentType.ARRAY_ANY
|
|
value: Sequence[Any]
|
|
|
|
|
|
class ArrayStringSegment(ArraySegment):
|
|
value_type: SegmentType = SegmentType.ARRAY_STRING
|
|
value: Sequence[str]
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return json.dumps(self.value, ensure_ascii=False)
|
|
|
|
|
|
class ArrayNumberSegment(ArraySegment):
|
|
value_type: SegmentType = SegmentType.ARRAY_NUMBER
|
|
value: Sequence[float | int]
|
|
|
|
|
|
class ArrayObjectSegment(ArraySegment):
|
|
value_type: SegmentType = SegmentType.ARRAY_OBJECT
|
|
value: Sequence[Mapping[str, Any]]
|
|
|
|
|
|
class ArrayFileSegment(ArraySegment):
|
|
value_type: SegmentType = SegmentType.ARRAY_FILE
|
|
value: Sequence[File]
|
|
|
|
@property
|
|
def markdown(self) -> str:
|
|
items = []
|
|
for item in self.value:
|
|
items.append(item.markdown)
|
|
return "\n".join(items)
|
|
|
|
@property
|
|
def log(self) -> str:
|
|
return ""
|
|
|
|
@property
|
|
def text(self) -> str:
|
|
return ""
|