From 51a7ae74518f6cdeabd90077a9c6d3dc5702a989 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 29 Jul 2024 16:44:09 -0300 Subject: [PATCH 1/3] refactor: improve recursive serialization function Refactor the `recursive_serialize_or_str` function in the `schema.py` file to improve its readability and maintainability. The function now uses a try-except block to handle exceptions and returns a string representation of the object if an exception occurs. This ensures that the function always returns a string, preventing any unexpected errors. Additionally, the function now includes additional checks for different object types, such as dictionaries, lists, and instances of `BaseModel`. These checks ensure that the function correctly serializes complex objects and avoids any potential issues. Overall, this refactoring improves the code quality and reliability of the `recursive_serialize_or_str` function. --- .../custom/custom_component/component.py | 22 +------------ src/backend/base/langflow/schema/schema.py | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 8df5f6c2b8d..2aafd92109c 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -1,5 +1,5 @@ import inspect -from typing import Any, AsyncIterator, Callable, ClassVar, Generator, Iterator, List, Optional, Union +from typing import Any, Callable, ClassVar, List, Optional, Union from uuid import UUID import yaml @@ -15,26 +15,6 @@ from .custom_component import CustomComponent -def recursive_serialize_or_str(obj): - try: - if isinstance(obj, dict): - return {k: recursive_serialize_or_str(v) for k, v in obj.items()} - elif isinstance(obj, list): - return [recursive_serialize_or_str(v) for v in obj] - elif isinstance(obj, BaseModel): - return {k: recursive_serialize_or_str(v) for k, v in obj.model_dump().items()} - elif isinstance(obj, (AsyncIterator, Generator, Iterator)): - # contain memory addresses - # without consuming the iterator - # return list(obj) consumes the iterator - # return f"{obj}" this generates '' - # it is not useful - return "Unconsumed Stream" - return str(obj) - except Exception: - return str(obj) - - class Component(CustomComponent): inputs: List[InputTypes] = [] outputs: List[Output] = [] diff --git a/src/backend/base/langflow/schema/schema.py b/src/backend/base/langflow/schema/schema.py index ee43f014e4c..89f9d0c4bdb 100644 --- a/src/backend/base/langflow/schema/schema.py +++ b/src/backend/base/langflow/schema/schema.py @@ -108,3 +108,35 @@ def build_output_logs(vertex, result) -> dict: outputs |= {name: OutputValue(message=message, type=_type).model_dump()} return outputs + + +def recursive_serialize_or_str(obj): + try: + if isinstance(obj, dict): + return {k: recursive_serialize_or_str(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [recursive_serialize_or_str(v) for v in obj] + elif isinstance(obj, BaseModel): + if hasattr(obj, "model_dump"): + obj_dict = obj.model_dump() + elif hasattr(obj, "dict"): + obj_dict = obj.dict() # type: ignore + return {k: recursive_serialize_or_str(v) for k, v in obj_dict.items()} + + elif isinstance(obj, (AsyncIterator, Generator, Iterator)): + # contain memory addresses + # without consuming the iterator + # return list(obj) consumes the iterator + # return f"{obj}" this generates '' + # it is not useful + return "Unconsumed Stream" + elif hasattr(obj, "dict"): + return {k: recursive_serialize_or_str(v) for k, v in obj.dict().items()} + elif hasattr(obj, "model_dump"): + return {k: recursive_serialize_or_str(v) for k, v in obj.model_dump().items()} + elif issubclass(obj, BaseModel): + # This a type BaseModel and not an instance of it + return repr(obj) + return str(obj) + except Exception: + return str(obj) From c10574d3bbc73657916fdce3c919135f04a1a1b5 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 29 Jul 2024 16:44:33 -0300 Subject: [PATCH 2/3] feat(artifact.py): add support for recursive serialization of items in ARRAY artifact type to ensure consistent data handling --- src/backend/base/langflow/schema/artifact.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/backend/base/langflow/schema/artifact.py b/src/backend/base/langflow/schema/artifact.py index a238838ff24..27eb3c52c0b 100644 --- a/src/backend/base/langflow/schema/artifact.py +++ b/src/backend/base/langflow/schema/artifact.py @@ -6,6 +6,7 @@ from langflow.schema import Data from langflow.schema.message import Message +from langflow.schema.schema import recursive_serialize_or_str class ArtifactType(str, Enum): @@ -52,6 +53,16 @@ def get_artifact_type(value, build_result=None) -> str: def post_process_raw(raw, artifact_type: str): if artifact_type == ArtifactType.STREAM.value: raw = "" + elif artifact_type == ArtifactType.ARRAY.value: + _raw = [] + for item in raw: + if hasattr(item, "dict"): + _raw.append(recursive_serialize_or_str(item)) + elif hasattr(item, "model_dump"): + _raw.append(recursive_serialize_or_str(item)) + else: + _raw.append(str(item)) + raw = _raw elif artifact_type == ArtifactType.UNKNOWN.value and raw is not None: if isinstance(raw, (BaseModel, dict)): try: From 9a9c7a383f9511fd15177457293497a6ccc90760 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 29 Jul 2024 16:45:09 -0300 Subject: [PATCH 3/3] feat(schema.py): add support for serializing arrays in build_output_logs function to handle LogType.ARRAY case --- src/backend/base/langflow/schema/schema.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/base/langflow/schema/schema.py b/src/backend/base/langflow/schema/schema.py index 89f9d0c4bdb..ba8ab9b3949 100644 --- a/src/backend/base/langflow/schema/schema.py +++ b/src/backend/base/langflow/schema/schema.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Generator, Literal, Union +from typing import AsyncIterator, Generator, Iterator, Literal, Union from pydantic import BaseModel from typing_extensions import TypedDict @@ -104,6 +104,9 @@ def build_output_logs(vertex, result) -> dict: case LogType.UNKNOWN: message = "" + + case LogType.ARRAY: + message = [recursive_serialize_or_str(item) for item in message] name = output.get("name", f"output_{index}") outputs |= {name: OutputValue(message=message, type=_type).model_dump()}