From cac85c62b4aa5e2775d0342c6cb3819d30c9d892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dtalo=20Johnny?= Date: Mon, 31 Mar 2025 11:46:40 -0300 Subject: [PATCH] feat: expose serialization truncation constants in /config route (#7316) * test: validate truncation logic and response structure * feat: expose serialization truncation constants in /config route * chore: tidy up redundant import from module --- src/backend/base/langflow/api/v1/endpoints.py | 2 - src/backend/base/langflow/api/v1/schemas.py | 3 + src/backend/tests/unit/api/v1/test_schemas.py | 82 +++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/backend/tests/unit/api/v1/test_schemas.py diff --git a/src/backend/base/langflow/api/v1/endpoints.py b/src/backend/base/langflow/api/v1/endpoints.py index ece229a25..533f2d64f 100644 --- a/src/backend/base/langflow/api/v1/endpoints.py +++ b/src/backend/base/langflow/api/v1/endpoints.py @@ -748,8 +748,6 @@ async def custom_component_update( @router.get("/config", response_model=ConfigResponse) async def get_config(): try: - from langflow.services.deps import get_settings_service - settings_service: SettingsService = get_settings_service() return { diff --git a/src/backend/base/langflow/api/v1/schemas.py b/src/backend/base/langflow/api/v1/schemas.py index 08e0ade10..69f0e1456 100644 --- a/src/backend/base/langflow/api/v1/schemas.py +++ b/src/backend/base/langflow/api/v1/schemas.py @@ -17,6 +17,7 @@ from langflow.graph.schema import RunOutputs from langflow.schema import dotdict from langflow.schema.graph import Tweaks from langflow.schema.schema import InputType, OutputType, OutputValue +from langflow.serialization import constants as serialization_constants from langflow.serialization.constants import MAX_ITEMS_LENGTH, MAX_TEXT_LENGTH from langflow.serialization.serialization import serialize from langflow.services.database.models.api_key.model import ApiKeyRead @@ -378,6 +379,8 @@ class FlowDataRequest(BaseModel): class ConfigResponse(BaseModel): feature_flags: FeatureFlags + serialization_max_items_lenght: int = serialization_constants.MAX_ITEMS_LENGTH + serialization_max_text_length: int = serialization_constants.MAX_TEXT_LENGTH frontend_timeout: int auto_saving: bool auto_saving_interval: int diff --git a/src/backend/tests/unit/api/v1/test_schemas.py b/src/backend/tests/unit/api/v1/test_schemas.py new file mode 100644 index 000000000..5021e43b7 --- /dev/null +++ b/src/backend/tests/unit/api/v1/test_schemas.py @@ -0,0 +1,82 @@ +import pytest +from langflow.api.v1.schemas import VertexBuildResponse +from langflow.serialization.constants import MAX_ITEMS_LENGTH + +expected_keys_vertex_build_response = { + "id", + "inactivated_vertices", + "next_vertices_ids", + "top_level_vertices", + "valid", + "params", + "data", + "timestamp", +} +expected_keys_data = { + "results", + "outputs", + "logs", + "message", + "artifacts", + "timedelta", + "duration", + "used_frozen_result", +} +expected_keys_outputs = {"message", "type"} + + +def assert_vertex_response_structure(result): + assert set(result.keys()).issuperset(expected_keys_vertex_build_response) + assert set(result["data"].keys()).issuperset(expected_keys_data) + assert set(result["data"]["outputs"]["dataframe"].keys()).issuperset(expected_keys_outputs) + + +def test_vertex_response_structure_without_truncate(): + message = [{"key": 1, "value": 1}] + output_value = {"message": message, "type": "bar"} + data = { + "data": {"outputs": {"dataframe": output_value}, "type": "foo"}, + "valid": True, + } + + result = VertexBuildResponse(**data).model_dump() + + assert_vertex_response_structure(result) + assert len(result["data"]["outputs"]["dataframe"]["message"]) == len(message) + + +def test_vertex_response_structure_when_truncate_applies(): + message = [{"key": i, "value": i} for i in range(MAX_ITEMS_LENGTH + 5000)] + output_value = {"message": message, "type": "bar"} + data = { + "data": {"outputs": {"dataframe": output_value}, "type": "foo"}, + "valid": True, + } + + result = VertexBuildResponse(**data).model_dump() + + assert_vertex_response_structure(result) + assert len(result["data"]["outputs"]["dataframe"]["message"]) == MAX_ITEMS_LENGTH + 1 + + +@pytest.mark.parametrize( + ("size", "expected"), + [ + (0, 0), + (42, 42), + (MAX_ITEMS_LENGTH, MAX_ITEMS_LENGTH), + (MAX_ITEMS_LENGTH + 1000, MAX_ITEMS_LENGTH + 1), + (MAX_ITEMS_LENGTH + 2000, MAX_ITEMS_LENGTH + 1), + (MAX_ITEMS_LENGTH + 3000, MAX_ITEMS_LENGTH + 1), + ], +) +def test_vertex_response_truncation_behavior(size, expected): + message = [{"key": i, "value": i} for i in range(size)] + output_value = {"message": message, "type": "bar"} + data = { + "data": {"outputs": {"dataframe": output_value}, "type": "foo"}, + "valid": True, + } + + result = VertexBuildResponse(**data).model_dump() + assert len(result["data"]["outputs"]["dataframe"]["message"]) == expected