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:
Gabriel Luiz Freitas Almeida 2024-08-07 11:04:48 -03:00 • committed by GitHub
commit 919073a0db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 364 additions and 106 deletions

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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)

View 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)

View file

@ -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