feat: import Graph without position information (#3203)
* refactor: Add utility functions for getting handle IDs in CustomNodes - Added `getRightHandleId` function to generate the right handle ID for source handles. - Added `getLeftHandleId` function to generate the left handle ID for target handles. - These functions improve code readability and maintainability by encapsulating the logic for generating handle IDs. * refactor: Add type for escaped handle IDs in edges to improve type safety in reactflowUtils * feat: Add function to escape handle IDs in edges, enhancing edge management in reactflowUtils * feat: Add function to check edges without escaped handle IDs, improving edge validation in reactflowUtils * feat: Enhance edge processing in reactflowUtils to handle edges without escaped handle IDs more effectively * feat: Add layoutUtils module for handling node layout using elkjs * feat: update processDataFromFlow to add layout to nodes if needed * Refactor import paths to use 'initialize' module in 'base.py' * feat: Add method to set class source code and integrate it with frontend node input field * refactor: Update sourceHandle dataType to use custom component class name * fix: Raise error for unknown vertex types instead of returning default Vertex class * refactor: Remove redundant call to _import_vertex_types() in VertexTypesDict initialization * refactor: Simplify add_code_field by removing unnecessary field_config parameter from function signature * feat: Add elkjs dependency to package.json and package-lock.json for enhanced functionality in the frontend * refactor: Update fields type in Template class to use InputTypes for improved type safety * refactor: Reorganize imports in __init__.py for better structure and consistency across the inputs module * refactor: Clean up imports in types.py for better organization and consistency in the graph vertex module * refactor: Change vertex type annotations to strings for better compatibility and consistency in the graph module methods * refactor: Update component instantiation to include _code parameter and fix input type annotations for improved type handling * refactor: Remove unused CustomComponent import from __init__.py for cleaner module structure and improved organization * refactor: Modify custom_component instantiation to include _code argument for enhanced functionality and clarity in CodeParser class * refactor: Update CustomComponent import in __init__.py for improved module structure and organization * refactor: Update launch.json to include correct path for backend source files * refactor: Update dependencies in poetry.lock to latest versions and resolve merge conflicts in backend files * refactor: Update dataType assignment in Component class to use component name if available, or fallback to class name * refactor: Correct flow_id reference in MemoryComponent to improve clarity and consistency in memory handling * refactor: Update import path for DefaultPromptField to improve code organization and maintainability in api_utils.py * refactor: Add loading module to __init__.py for improved organization of interface package * refactor: Clean up imports in base.py and enforce edge validation in Graph class for improved maintainability and error handling * refactor: Remove edge component additions in test_base.py to streamline graph tests and emphasize error handling for unprepared graphs * refactor: Mark @clack/prompts is-unicode-supported as extraneous in package-lock.json for better dependency management * refactor: Update dataType assignment in Component class to use component name if available, or fallback to class name * refactor: Fix edge existence check in Graph class to use correct variable, ensuring accurate validation of graph structure * refactor: Add test for graph with edge and improve graph preparation logic * refactor: Set default node type to "genericNode" in getLayoutedNodes for consistent layout structure * create consts for node widht and height * refactor: Catch and log errors when processing flow data in flowsManagerStore and reactflowUtils * [autofix.ci] apply automated fixes * fix: Validate custom components for source and target vertices in Graph edges * test: Add fixture for client and raise TypeError for invalid class parsing in CodeParser tests * test: Add unit test for listing flows as Flow objects in custom component with client * test: Update assertions for memory chatbot component types in unit tests * test: Refactor assertions to use updated component names in vector store RAG unit tests * fix: Change error handling to return default Vertex for unknown node types in graph class * [autofix.ci] apply automated fixes * test: Add pytest fixture for CustomComponent in unit tests to enhance test structure and readability * chore: Update component names in vector store RAG unit tests * test: Refactor imports and make flow name generation unique in database unit tests * chore: Remove unused upload and flow management functions from flowsManagerStore for cleaner codebase * chore: Await processDataFromFlow in useAddFlow hook * chore: Correct NODE_HEIGHT calculation to use NODE_WIDTH constant for consistency in constants file * chore: Remove extraneous flag for is-unicode-supported in package-lock.json for cleaner dependency management --------- Co-authored-by: anovazzi1 <otavio2204@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
a7bfb55e8c
commit
919073a0db
31 changed files with 364 additions and 106 deletions
|
|
@ -21,13 +21,23 @@ async def test_graph_not_prepared():
|
|||
graph = Graph()
|
||||
graph.add_component("chat_input", chat_input)
|
||||
graph.add_component("chat_output", chat_output)
|
||||
graph.add_component_edge("chat_input", (chat_input.outputs[0].name, chat_input.inputs[0].name), "chat_output")
|
||||
with pytest.raises(ValueError):
|
||||
await graph.astep()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_graph():
|
||||
chat_input = ChatInput()
|
||||
chat_output = ChatOutput()
|
||||
graph = Graph()
|
||||
graph.add_component("chat_input", chat_input)
|
||||
graph.add_component("chat_output", chat_output)
|
||||
with pytest.raises(ValueError, match="Graph has vertices but no edges"):
|
||||
graph.prepare()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_graph_with_edge():
|
||||
chat_input = ChatInput()
|
||||
chat_output = ChatOutput()
|
||||
graph = Graph()
|
||||
|
|
|
|||
|
|
@ -94,16 +94,16 @@ def test_memory_chatbot_dump_components_and_edges(memory_chatbot_graph: Graph):
|
|||
assert nodes[0]["data"]["type"] == "ChatInput"
|
||||
assert nodes[0]["id"] == "chat_input"
|
||||
|
||||
assert nodes[1]["data"]["type"] == "MemoryComponent"
|
||||
assert nodes[1]["data"]["type"] == "Memory"
|
||||
assert nodes[1]["id"] == "chat_memory"
|
||||
|
||||
assert nodes[2]["data"]["type"] == "ChatOutput"
|
||||
assert nodes[2]["id"] == "chat_output"
|
||||
|
||||
assert nodes[3]["data"]["type"] == "OpenAIModelComponent"
|
||||
assert nodes[3]["data"]["type"] == "OpenAIModel"
|
||||
assert nodes[3]["id"] == "openai"
|
||||
|
||||
assert nodes[4]["data"]["type"] == "PromptComponent"
|
||||
assert nodes[4]["data"]["type"] == "Prompt"
|
||||
assert nodes[4]["id"] == "prompt"
|
||||
|
||||
# Check edges
|
||||
|
|
|
|||
|
|
@ -134,16 +134,16 @@ def test_vector_store_rag_dump_components_and_edges(ingestion_graph, rag_graph):
|
|||
ingestion_nodes = sorted(ingestion_nodes, key=lambda x: x["id"])
|
||||
|
||||
# Check components in the ingestion graph
|
||||
assert ingestion_nodes[0]["data"]["type"] == "FileComponent"
|
||||
assert ingestion_nodes[0]["data"]["type"] == "File"
|
||||
assert ingestion_nodes[0]["id"] == "file-123"
|
||||
|
||||
assert ingestion_nodes[1]["data"]["type"] == "OpenAIEmbeddingsComponent"
|
||||
assert ingestion_nodes[1]["data"]["type"] == "OpenAIEmbeddings"
|
||||
assert ingestion_nodes[1]["id"] == "openai-embeddings-123"
|
||||
|
||||
assert ingestion_nodes[2]["data"]["type"] == "SplitTextComponent"
|
||||
assert ingestion_nodes[2]["data"]["type"] == "SplitText"
|
||||
assert ingestion_nodes[2]["id"] == "text-splitter-123"
|
||||
|
||||
assert ingestion_nodes[3]["data"]["type"] == "AstraVectorStoreComponent"
|
||||
assert ingestion_nodes[3]["data"]["type"] == "AstraDB"
|
||||
assert ingestion_nodes[3]["id"] == "vector-store-123"
|
||||
|
||||
# Check edges in the ingestion graph
|
||||
|
|
@ -178,19 +178,19 @@ def test_vector_store_rag_dump_components_and_edges(ingestion_graph, rag_graph):
|
|||
assert rag_nodes[1]["data"]["type"] == "ChatOutput"
|
||||
assert rag_nodes[1]["id"] == "chatoutput-123"
|
||||
|
||||
assert rag_nodes[2]["data"]["type"] == "OpenAIModelComponent"
|
||||
assert rag_nodes[2]["data"]["type"] == "OpenAIModel"
|
||||
assert rag_nodes[2]["id"] == "openai-123"
|
||||
|
||||
assert rag_nodes[3]["data"]["type"] == "OpenAIEmbeddingsComponent"
|
||||
assert rag_nodes[3]["data"]["type"] == "OpenAIEmbeddings"
|
||||
assert rag_nodes[3]["id"] == "openai-embeddings-124"
|
||||
|
||||
assert rag_nodes[4]["data"]["type"] == "ParseDataComponent"
|
||||
assert rag_nodes[4]["data"]["type"] == "ParseData"
|
||||
assert rag_nodes[4]["id"] == "parse-data-123"
|
||||
|
||||
assert rag_nodes[5]["data"]["type"] == "PromptComponent"
|
||||
assert rag_nodes[5]["data"]["type"] == "Prompt"
|
||||
assert rag_nodes[5]["id"] == "prompt-123"
|
||||
|
||||
assert rag_nodes[6]["data"]["type"] == "AstraVectorStoreComponent"
|
||||
assert rag_nodes[6]["data"]["type"] == "AstraDB"
|
||||
assert rag_nodes[6]["id"] == "rag-vector-store-123"
|
||||
|
||||
# Check edges in the RAG graph
|
||||
|
|
|
|||
|
|
@ -9,7 +9,12 @@ from langflow.custom import Component, CustomComponent
|
|||
from langflow.custom.code_parser.code_parser import CodeParser, CodeSyntaxError
|
||||
from langflow.custom.custom_component.base_component import BaseComponent, ComponentCodeNullError
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.services.database.models.flow import Flow, FlowCreate
|
||||
from langflow.services.database.models.flow import FlowCreate
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -168,7 +173,7 @@ def test_code_parser_parse_classes():
|
|||
"""
|
||||
Test the parse_classes method of the CodeParser class.
|
||||
"""
|
||||
parser = CodeParser("class Test: pass")
|
||||
parser = CodeParser("from langflow.custom import Component\n\nclass Test(Component): pass")
|
||||
tree = parser.get_tree()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ClassDef):
|
||||
|
|
@ -177,6 +182,18 @@ def test_code_parser_parse_classes():
|
|||
assert parser.data["classes"][0]["name"] == "Test"
|
||||
|
||||
|
||||
def test_code_parser_parse_classes_raises():
|
||||
"""
|
||||
Test the parse_classes method of the CodeParser class.
|
||||
"""
|
||||
parser = CodeParser("class Test: pass")
|
||||
tree = parser.get_tree()
|
||||
with pytest.raises(TypeError):
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ClassDef):
|
||||
parser.parse_classes(node)
|
||||
|
||||
|
||||
def test_code_parser_parse_global_vars():
|
||||
"""
|
||||
Test the parse_global_vars method of the CodeParser class.
|
||||
|
|
@ -487,11 +504,6 @@ def test_list_flows_return_type(component):
|
|||
assert isinstance(flows, list)
|
||||
|
||||
|
||||
def test_list_flows_flow_objects(component):
|
||||
flows = component.list_flows()
|
||||
assert all(isinstance(flow, Flow) for flow in flows)
|
||||
|
||||
|
||||
def test_build_config_return_type(component):
|
||||
config = component.build_config()
|
||||
assert isinstance(config, dict)
|
||||
|
|
|
|||
23
src/backend/tests/unit/test_custom_component_with_client.py
Normal file
23
src/backend/tests/unit/test_custom_component_with_client.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import pytest
|
||||
|
||||
from langflow.custom.custom_component.custom_component import CustomComponent
|
||||
from langflow.services.database.models.flow import Flow
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def component(client, active_user):
|
||||
return CustomComponent(
|
||||
user_id=active_user.id,
|
||||
field_config={
|
||||
"fields": {
|
||||
"llm": {"type": "str"},
|
||||
"url": {"type": "str"},
|
||||
"year": {"type": "int"},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_list_flows_flow_objects(component):
|
||||
flows = component.list_flows()
|
||||
assert all(isinstance(flow, Flow) for flow in flows)
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
import json
|
||||
from collections import namedtuple
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from langflow.graph.utils import log_transaction, log_vertex_build
|
||||
import orjson
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlmodel import Session
|
||||
|
||||
from langflow.api.v1.schemas import FlowListCreate, ResultDataResponse
|
||||
from langflow.initial_setup.setup import load_starter_projects, load_flows_from_directory
|
||||
from langflow.graph.utils import log_transaction, log_vertex_build
|
||||
from langflow.initial_setup.setup import load_flows_from_directory, load_starter_projects
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
from langflow.services.database.models.flow import Flow, FlowCreate, FlowUpdate
|
||||
from langflow.services.database.models.transactions.crud import get_transactions_by_flow_id
|
||||
from langflow.services.database.utils import session_getter, migrate_transactions_from_monitor_service_to_database
|
||||
from langflow.services.database.utils import migrate_transactions_from_monitor_service_to_database, session_getter
|
||||
from langflow.services.deps import get_db_service, get_monitor_service, session_scope
|
||||
from langflow.services.monitor.schema import TransactionModel
|
||||
from langflow.services.monitor.utils import (
|
||||
add_row_to_table,
|
||||
drop_and_create_table_if_schema_mismatch,
|
||||
new_duckdb_locked_connection,
|
||||
add_row_to_table,
|
||||
)
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
|
@ -46,7 +46,7 @@ def test_create_flow(client: TestClient, json_flow: str, active_user, logged_in_
|
|||
assert response.json()["name"] == flow.name
|
||||
assert response.json()["data"] == flow.data
|
||||
# flow is optional so we can create a flow without a flow
|
||||
flow = FlowCreate(name="Test Flow")
|
||||
flow = FlowCreate(name=str(uuid4()))
|
||||
response = client.post("api/v1/flows/", json=flow.model_dump(exclude_unset=True), headers=logged_in_headers)
|
||||
assert response.status_code == 201
|
||||
assert response.json()["name"] == flow.name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue