diff --git a/tests/test_creators.py b/tests/test_creators.py new file mode 100644 index 000000000..08b2fc9d1 --- /dev/null +++ b/tests/test_creators.py @@ -0,0 +1,49 @@ +from typing import Dict, List +from langflow.interface.base import LangChainTypeCreator +from langflow.interface.agents.base import AgentCreator +import pytest + + +@pytest.fixture +def sample_lang_chain_type_creator() -> LangChainTypeCreator: + class SampleLangChainTypeCreator(LangChainTypeCreator): + type_name: str = "test_type" + + def type_to_loader_dict(self) -> Dict: + return {"test_type": "TestClass"} + + def to_list(self) -> List[str]: + return ["node1", "node2"] + + def get_signature(self, name: str) -> Dict: + return { + "template": {"test_field": {"type": "str"}}, + "description": "test description", + "base_classes": ["base_class1", "base_class2"], + } + + return SampleLangChainTypeCreator() + + +@pytest.fixture +def sample_agent_creator() -> AgentCreator: + return AgentCreator() + + +def test_lang_chain_type_creator_to_dict( + sample_lang_chain_type_creator: LangChainTypeCreator, +): + type_dict = sample_lang_chain_type_creator.to_dict() + assert len(type_dict) == 1 + assert "test_type" in type_dict + assert "node1" in type_dict["test_type"] + assert "node2" in type_dict["test_type"] + assert "template" in type_dict["test_type"]["node1"] + assert "description" in type_dict["test_type"]["node1"] + assert "base_classes" in type_dict["test_type"]["node1"] + + +def test_agent_creator_type_to_loader_dict(sample_agent_creator: AgentCreator): + type_to_loader_dict = sample_agent_creator.type_to_loader_dict + assert len(type_to_loader_dict) > 0 + assert "JsonAgent" diff --git a/tests/test_frontend_nodes.py b/tests/test_frontend_nodes.py new file mode 100644 index 000000000..75b0b741f --- /dev/null +++ b/tests/test_frontend_nodes.py @@ -0,0 +1,60 @@ +import pytest +from typing import Dict, List +from langflow.template.base import TemplateField, FrontendNode, Template +from langflow.interface.base import LangChainTypeCreator +from langflow.interface.agents.base import AgentCreator + + +@pytest.fixture +def sample_template_field() -> TemplateField: + return TemplateField(name="test_field", field_type="str") + + +@pytest.fixture +def sample_template(sample_template_field: TemplateField) -> Template: + return Template(type_name="test_template", fields=[sample_template_field]) + + +@pytest.fixture +def sample_frontend_node(sample_template: Template) -> FrontendNode: + return FrontendNode( + template=sample_template, + description="test description", + base_classes=["base_class1", "base_class2"], + name="test_frontend_node", + ) + + +def test_template_field_defaults(sample_template_field: TemplateField): + assert sample_template_field.field_type == "str" + assert sample_template_field.required == False + assert sample_template_field.placeholder == "" + assert sample_template_field.is_list == False + assert sample_template_field.show == True + assert sample_template_field.multiline == False + assert sample_template_field.value == None + assert sample_template_field.suffixes == [] + assert sample_template_field.file_types == [] + assert sample_template_field.content == None + assert sample_template_field.password == False + assert sample_template_field.name == "test_field" + + +def test_template_to_dict( + sample_template: Template, sample_template_field: TemplateField +): + template_dict = sample_template.to_dict() + assert template_dict["_type"] == "test_template" + assert len(template_dict) == 2 # _type and test_field + assert "test_field" in template_dict + assert "type" in template_dict["test_field"] + assert "required" in template_dict["test_field"] + + +def test_frontend_node_to_dict(sample_frontend_node: FrontendNode): + node_dict = sample_frontend_node.to_dict() + assert len(node_dict) == 1 + assert "test_frontend_node" in node_dict + assert "description" in node_dict["test_frontend_node"] + assert "template" in node_dict["test_frontend_node"] + assert "base_classes" in node_dict["test_frontend_node"] diff --git a/tests/test_template.py b/tests/test_template.py new file mode 100644 index 000000000..102df3b56 --- /dev/null +++ b/tests/test_template.py @@ -0,0 +1,242 @@ +from langflow.utils.constants import CHAT_OPENAI_MODELS, OPENAI_MODELS +from pydantic import BaseModel +import pytest +import re +import importlib +from typing import Dict +from langflow.utils.util import ( + build_template_from_class, + format_dict, + get_base_classes, + get_default_factory, + get_class_doc, +) + + +# Dummy classes for testing purposes +class Parent(BaseModel): + """Parent Class""" + + parent_field: str + + +class Child(Parent): + """Child Class""" + + child_field: int + + +# Test build_template_from_class +def test_build_template_from_class(): + type_to_cls_dict: Dict[str, type] = {"parent": Parent, "child": Child} + + # Test valid input + result = build_template_from_class("Child", type_to_cls_dict) + assert "template" in result + assert "description" in result + assert "base_classes" in result + assert "Child" in result["base_classes"] + assert "Parent" in result["base_classes"] + assert result["description"] == "Child Class" + + # Test invalid input + with pytest.raises(ValueError, match="InvalidClass not found."): + build_template_from_class("InvalidClass", type_to_cls_dict) + + +# Test format_dict +def test_format_dict(): + # Test 1: Optional type removal + input_dict = { + "field1": {"type": "Optional[str]", "required": False}, + } + expected_output = { + "field1": { + "type": "str", + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + }, + } + assert format_dict(input_dict) == expected_output + + # Test 2: List type processing + input_dict = { + "field1": {"type": "List[str]", "required": False}, + } + expected_output = { + "field1": { + "type": "str", + "required": False, + "list": True, + "show": False, + "password": False, + "multiline": False, + }, + } + assert format_dict(input_dict) == expected_output + + # Test 3: Mapping type replacement + input_dict = { + "field1": {"type": "Mapping[str, int]", "required": False}, + } + expected_output = { + "field1": { + "type": "code", # Mapping type is replaced with dict which is replaced with code + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + }, + } + assert format_dict(input_dict) == expected_output + + # Test 4: Replace default value with actual value + input_dict = { + "field1": {"type": "str", "required": False, "default": "test"}, + } + expected_output = { + "field1": { + "type": "str", + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + "value": "test", + }, + } + assert format_dict(input_dict) == expected_output + + # Test 5: Add password field + input_dict = { + "field1": {"type": "str", "required": False}, + "api_key": {"type": "str", "required": False}, + } + expected_output = { + "field1": { + "type": "str", + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + }, + "api_key": { + "type": "str", + "required": False, + "list": False, + "show": True, + "password": True, + "multiline": False, + }, + } + assert format_dict(input_dict) == expected_output + + # Test 6: Add multiline + input_dict = { + "field1": {"type": "str", "required": False}, + "prefix": {"type": "str", "required": False}, + } + expected_output = { + "field1": { + "type": "str", + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + }, + "prefix": { + "type": "str", + "required": False, + "list": False, + "show": True, + "password": False, + "multiline": True, + }, + } + assert format_dict(input_dict) == expected_output + + # Test 7: Check class name-specific cases (OpenAI, OpenAIChat) + input_dict = { + "model_name": {"type": "str", "required": False}, + } + expected_output_openai = { + "model_name": { + "type": "str", + "required": False, + "list": True, + "show": True, + "password": False, + "multiline": False, + "options": OPENAI_MODELS, + }, + } + expected_output_openai_chat = { + "model_name": { + "type": "str", + "required": False, + "list": True, + "show": True, + "password": False, + "multiline": False, + "options": CHAT_OPENAI_MODELS, + }, + } + assert format_dict(input_dict, "OpenAI") == expected_output_openai + assert format_dict(input_dict, "OpenAIChat") == expected_output_openai_chat + + # Test 8: Replace dict type with str + input_dict = { + "field1": {"type": "Dict[str, int]", "required": False}, + } + expected_output = { + "field1": { + "type": "code", + "required": False, + "list": False, + "show": False, + "password": False, + "multiline": False, + }, + } + assert format_dict(input_dict) == expected_output + + +# Test get_base_classes +def test_get_base_classes(): + base_classes_parent = get_base_classes(Parent) + base_classes_child = get_base_classes(Child) + + assert "Parent" in base_classes_parent + assert "Child" in base_classes_child + assert "Parent" in base_classes_child + + +# Test get_default_factory +def test_get_default_factory(): + module_name = "langflow.utils.util" + function_repr = "" + + def dummy_function(): + return "default_value" + + # Add dummy_function to your_module + setattr(importlib.import_module(module_name), "dummy_function", dummy_function) + + default_value = get_default_factory(module_name, function_repr) + + assert default_value == "default_value" + + +# Test get_class_doc +def test_get_class_doc(): + class_doc_parent = get_class_doc(Parent) + class_doc_child = get_class_doc(Child) + + assert class_doc_parent["Description"] == "Parent Class" + assert class_doc_child["Description"] == "Child Class"