* take1 * depreacate stdio and sse mcp components * optionals * rodrigo fixes * session management * update init * mcp component integration test * broken * [autofix.ci] apply automated fixes * fix url input name * upated MCP * Update mcp_component.py * [autofix.ci] apply automated fixes * update to the MCP component * [autofix.ci] apply automated fixes * mostly working * [autofix.ci] apply automated fixes * Update mcp_component.py * [autofix.ci] apply automated fixes * update component * [autofix.ci] apply automated fixes * Update mcp_component.py * rename component because Simon * icon and description for simon * fix integration test * fix test * Update mcp_component.py * update and basic QoL * [autofix.ci] apply automated fixes * refactor clients to util and use flow names not IDs in mcp.py * integration test * take out traces * ✨ (edit-tools.spec.ts): add test for user to be able to edit tools in the frontend application. * session fix * fix content output * ♻️ (util.py): remove redundant constant HTTP_TEMPORARY_REDIRECT and replace its usage with httpx.codes.TEMPORARY_REDIRECT for better code readability and maintainability * [autofix.ci] apply automated fixes * 🐛 (utils.ts): fix potential null pointer error when converting words to title case by adding null check before accessing properties * 🐛 (genericIconComponent/index.tsx): Fix issue with optional chaining in mapping function 🐛 (renderIconComponent/index.tsx): Fix issue with optional chaining in mapping function 🐛 (button.tsx): Fix issue with optional chaining in mapping function 🐛 (utils.ts): Fix issue with optional chaining in mapping functions * 🐛 (language-select.tsx): Fix potential null pointer error when mapping over allLanguages array * ✨ (constants.ts): add support for multiple languages in the application by defining an array of language options ♻️ (audio-settings-dialog.tsx, language-select.tsx): refactor to import the array of all languages from constants.ts instead of duplicating it in each file * ✅ (auto-login-off.spec.ts): add a 2-second delay before continuing the test to ensure proper loading and rendering of elements on the page * ⬆️ (filterEdge-shard-0.spec.ts): reduce wait time for page interactions to improve test performance ⬆️ (playground.spec.ts): optimize wait times for page interactions to enhance test efficiency --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Edwin Jose <edwin.jose@datastax.com> Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
244 lines
9.1 KiB
Python
244 lines
9.1 KiB
Python
from collections.abc import Sequence as SequenceABC
|
|
from types import NoneType
|
|
from typing import Union
|
|
|
|
import pytest
|
|
from langflow.inputs.inputs import BoolInput, DictInput, FloatInput, InputTypes, IntInput, MessageTextInput
|
|
from langflow.io.schema import schema_to_langflow_inputs
|
|
from langflow.schema.data import Data
|
|
from langflow.template import Input, Output
|
|
from langflow.template.field.base import UNDEFINED
|
|
from langflow.type_extraction.type_extraction import post_process_type
|
|
from pydantic import BaseModel, Field, ValidationError
|
|
|
|
|
|
class TestInput:
|
|
def test_field_type_str(self):
|
|
input_obj = Input(field_type="str")
|
|
assert input_obj.field_type == "str"
|
|
|
|
def test_field_type_type(self):
|
|
input_obj = Input(field_type=int)
|
|
assert input_obj.field_type == "int"
|
|
|
|
def test_invalid_field_type(self):
|
|
with pytest.raises(ValidationError):
|
|
Input(field_type=123)
|
|
|
|
def test_serialize_field_type(self):
|
|
input_obj = Input(field_type="str")
|
|
assert input_obj.serialize_field_type("str", None) == "str"
|
|
|
|
def test_validate_type_string(self):
|
|
input_obj = Input(field_type="str")
|
|
assert input_obj.field_type == "str"
|
|
|
|
def test_validate_type_class(self):
|
|
input_obj = Input(field_type=int)
|
|
assert input_obj.field_type == "int"
|
|
|
|
def test_post_process_type_function(self):
|
|
# Basic types
|
|
assert set(post_process_type(int)) == {int}
|
|
assert set(post_process_type(float)) == {float}
|
|
|
|
# List and Sequence types
|
|
assert set(post_process_type(list[int])) == {int}
|
|
assert set(post_process_type(SequenceABC[float])) == {float}
|
|
|
|
# Union types
|
|
assert set(post_process_type(Union[int, str])) == {int, str} # noqa: UP007
|
|
assert set(post_process_type(Union[int, SequenceABC[str]])) == {int, str} # noqa: UP007
|
|
assert set(post_process_type(Union[int, SequenceABC[int]])) == {int} # noqa: UP007
|
|
|
|
# Nested Union with lists
|
|
assert set(post_process_type(Union[list[int], list[str]])) == {int, str} # noqa: UP007
|
|
assert set(post_process_type(Union[int, list[str], list[float]])) == {int, str, float} # noqa: UP007
|
|
|
|
# Custom data types
|
|
assert set(post_process_type(Data)) == {Data}
|
|
assert set(post_process_type(list[Data])) == {Data}
|
|
|
|
# Union with custom types
|
|
assert set(post_process_type(Union[Data, str])) == {Data, str} # noqa: UP007
|
|
assert set(post_process_type(Union[Data, int, list[str]])) == {Data, int, str} # noqa: UP007
|
|
|
|
# Empty lists and edge cases
|
|
assert set(post_process_type(list)) == {list}
|
|
assert set(post_process_type(Union[int, None])) == {int, NoneType} # noqa: UP007
|
|
assert set(post_process_type(Union[list[None], None])) == {None, NoneType} # noqa: UP007
|
|
|
|
# Handling complex nested structures
|
|
assert set(post_process_type(Union[SequenceABC[int | str], list[float]])) == {int, str, float} # noqa: UP007
|
|
assert set(post_process_type(Union[int | list[str] | list[float], str])) == {int, str, float} # noqa: UP007
|
|
|
|
# Non-generic types should return as is
|
|
assert set(post_process_type(dict)) == {dict}
|
|
assert set(post_process_type(tuple)) == {tuple}
|
|
|
|
# Union with custom types
|
|
assert set(post_process_type(Union[Data, str])) == {Data, str} # noqa: UP007
|
|
assert set(post_process_type(Data | str)) == {Data, str}
|
|
assert set(post_process_type(Data | int | list[str])) == {Data, int, str}
|
|
|
|
# More complex combinations with Data
|
|
assert set(post_process_type(Data | list[float])) == {Data, float}
|
|
assert set(post_process_type(Data | Union[int, str])) == {Data, int, str} # noqa: UP007
|
|
assert set(post_process_type(Data | list[int] | None)) == {Data, int, type(None)}
|
|
assert set(post_process_type(Data | Union[float, None])) == {Data, float, type(None)} # noqa: UP007
|
|
|
|
# Multiple Data types combined
|
|
assert set(post_process_type(Union[Data, str | float])) == {Data, str, float} # noqa: UP007
|
|
assert set(post_process_type(Union[Data | float | str, int])) == {Data, int, float, str} # noqa: UP007
|
|
|
|
# Testing with nested unions and lists
|
|
assert set(post_process_type(Union[list[Data], list[int | str]])) == {Data, int, str} # noqa: UP007
|
|
assert set(post_process_type(Data | list[float | str])) == {Data, float, str}
|
|
|
|
def test_input_to_dict(self):
|
|
input_obj = Input(field_type="str")
|
|
assert input_obj.to_dict() == {
|
|
"type": "str",
|
|
"required": False,
|
|
"placeholder": "",
|
|
"list": False,
|
|
"show": True,
|
|
"multiline": False,
|
|
"fileTypes": [],
|
|
"file_path": "",
|
|
"advanced": False,
|
|
"title_case": False,
|
|
"dynamic": False,
|
|
"info": "",
|
|
"input_types": ["Text"],
|
|
"load_from_db": False,
|
|
}
|
|
|
|
|
|
class TestOutput:
|
|
def test_output_default(self):
|
|
output_obj = Output(name="test_output")
|
|
assert output_obj.name == "test_output"
|
|
assert output_obj.value == UNDEFINED
|
|
assert output_obj.cache is True
|
|
|
|
def test_output_add_types(self):
|
|
output_obj = Output(name="test_output")
|
|
output_obj.add_types(["str", "int"])
|
|
assert output_obj.types == ["str", "int"]
|
|
|
|
def test_output_set_selected(self):
|
|
output_obj = Output(name="test_output", types=["str", "int"])
|
|
output_obj.set_selected()
|
|
assert output_obj.selected == "str"
|
|
|
|
def test_output_to_dict(self):
|
|
output_obj = Output(name="test_output")
|
|
assert output_obj.to_dict() == {
|
|
"allows_loop": False,
|
|
"types": [],
|
|
"name": "test_output",
|
|
"display_name": "test_output",
|
|
"cache": True,
|
|
"value": "__UNDEFINED__",
|
|
"tool_mode": True,
|
|
}
|
|
|
|
def test_output_validate_display_name(self):
|
|
output_obj = Output(name="test_output")
|
|
assert output_obj.display_name == "test_output"
|
|
|
|
def test_output_validate_model(self):
|
|
output_obj = Output(name="test_output", value="__UNDEFINED__")
|
|
assert output_obj.validate_model() == output_obj
|
|
|
|
|
|
class TestPostProcessType:
|
|
def test_int_type(self):
|
|
assert post_process_type(int) == [int]
|
|
|
|
def test_list_int_type(self):
|
|
assert post_process_type(list[int]) == [int]
|
|
|
|
def test_union_type(self):
|
|
assert set(post_process_type(Union[int, str])) == {int, str} # noqa: UP007
|
|
|
|
def test_custom_type(self):
|
|
class CustomType:
|
|
pass
|
|
|
|
assert post_process_type(CustomType) == [CustomType]
|
|
|
|
def test_list_custom_type(self):
|
|
class CustomType:
|
|
pass
|
|
|
|
assert post_process_type(list[CustomType]) == [CustomType]
|
|
|
|
def test_union_custom_type(self):
|
|
class CustomType:
|
|
pass
|
|
|
|
assert set(post_process_type(Union[CustomType, int])) == {CustomType, int} # noqa: UP007
|
|
|
|
|
|
def test_schema_to_langflow_inputs():
|
|
# Define a test Pydantic model with various field types
|
|
class TestSchema(BaseModel):
|
|
text_field: str = Field(title="Custom Text Title", description="A text field")
|
|
number_field: int = Field(description="A number field")
|
|
bool_field: bool = Field(description="A boolean field")
|
|
dict_field: dict = Field(description="A dictionary field")
|
|
list_field: list[str] = Field(description="A list of strings")
|
|
|
|
# Convert schema to Langflow inputs
|
|
inputs = schema_to_langflow_inputs(TestSchema)
|
|
|
|
# Verify the number of inputs matches the schema fields
|
|
assert len(inputs) == 5
|
|
|
|
# Helper function to find input by name
|
|
def find_input(name: str) -> InputTypes | None:
|
|
for _input in inputs:
|
|
if _input.name == name:
|
|
return _input
|
|
return None
|
|
|
|
# Test text field
|
|
text_input = find_input("text_field")
|
|
assert text_input.display_name == "Custom Text Title"
|
|
assert text_input.info == "A text field"
|
|
assert isinstance(text_input, MessageTextInput) # Check the instance type instead of field_type
|
|
|
|
# Test number field
|
|
number_input = find_input("number_field")
|
|
assert number_input.display_name == "Number Field"
|
|
assert number_input.info == "A number field"
|
|
assert isinstance(number_input, IntInput | FloatInput)
|
|
|
|
# Test boolean field
|
|
bool_input = find_input("bool_field")
|
|
assert isinstance(bool_input, BoolInput)
|
|
|
|
# Test dictionary field
|
|
dict_input = find_input("dict_field")
|
|
assert isinstance(dict_input, DictInput)
|
|
|
|
# Test list field
|
|
list_input = find_input("list_field")
|
|
assert list_input.is_list is True
|
|
assert isinstance(list_input, MessageTextInput)
|
|
|
|
|
|
def test_schema_to_langflow_inputs_invalid_type():
|
|
# Define a schema with an unsupported type
|
|
class CustomType:
|
|
pass
|
|
|
|
class InvalidSchema(BaseModel):
|
|
model_config = {"arbitrary_types_allowed": True} # Add this line
|
|
invalid_field: CustomType
|
|
|
|
# Test that attempting to convert an unsupported type raises TypeError
|
|
with pytest.raises(TypeError, match="Unsupported field type:"):
|
|
schema_to_langflow_inputs(InvalidSchema)
|