Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add code generation from Graphs generated in Python #3205

Draft
wants to merge 91 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
3927bf5
feat(utils.py): add escape_json_dump function to escape JSON strings …
ogabrielluiz Aug 5, 2024
6c40f7b
refactor(Output): streamline add_types method to prevent duplicate en…
ogabrielluiz Aug 5, 2024
a744489
feat(data.py): add classmethod decorator to validate_data for enhance…
ogabrielluiz Aug 5, 2024
8254275
feat(setup.py): implement retry logic for loading starter projects to…
ogabrielluiz Aug 5, 2024
fd770f2
fix(input_mixin.py): improve model_config formatting and update field…
ogabrielluiz Aug 5, 2024
22fd048
feat(types.py): refactor vertex constructors to use NodeData and add …
ogabrielluiz Aug 5, 2024
e75d06e
feat(schema.py): add NodeData and Position TypedDicts for improved ty…
ogabrielluiz Aug 5, 2024
7e65921
feat(base.py): update Vertex to use NodeData type and add to_data met…
ogabrielluiz Aug 5, 2024
862ae9c
refactor(schema.py): update TargetHandle and SourceHandle models to i…
ogabrielluiz Aug 5, 2024
ac5e541
Add TypedDict classes for graph schema serialization in `schema.py`
ogabrielluiz Aug 5, 2024
3006402
Refactor `Edge` class to improve handle validation and data handling
ogabrielluiz Aug 5, 2024
cf69f83
Refactor `Edge` class to improve handle validation and data handling
ogabrielluiz Aug 5, 2024
6ea2715
Refactor: Standardize attribute naming and add `to_data` method in Ed…
ogabrielluiz Aug 5, 2024
c6f31f8
Refactor: Update Edge class to consistently use snake_case for attrib…
ogabrielluiz Aug 5, 2024
8983260
Refactor: Change node argument type in add_node and _create_vertex me…
ogabrielluiz Aug 5, 2024
5f24db8
Refactor: Implement JSON serialization for graph data with `dumps` an…
ogabrielluiz Aug 5, 2024
4239ab6
Refactor: Add pytest fixtures for ingestion and RAG graphs, enhance t…
ogabrielluiz Aug 5, 2024
46fec80
Refactor: Add pytest fixtures for memory_chatbot_graph tests and impr…
ogabrielluiz Aug 5, 2024
78ff654
Refactor: Remove unused methods in ComponentVertex class to streamlin…
ogabrielluiz Aug 5, 2024
cc3fc12
Refactor: Remove unnecessary line in ComponentVertex class to enhance…
ogabrielluiz Aug 5, 2024
8d0140f
refactor: Add utility functions for getting handle IDs in CustomNodes
ogabrielluiz Aug 5, 2024
5f763d5
refactor: Add type for escaped handle IDs in edges to improve type sa…
ogabrielluiz Aug 5, 2024
26961a0
feat: Add function to escape handle IDs in edges, enhancing edge mana…
ogabrielluiz Aug 5, 2024
d5d0ea1
feat: Add function to check edges without escaped handle IDs, improvi…
ogabrielluiz Aug 5, 2024
2b264da
feat: Enhance edge processing in reactflowUtils to handle edges witho…
ogabrielluiz Aug 5, 2024
0590d26
feat: Add layoutUtils module for handling node layout using elkjs
ogabrielluiz Aug 5, 2024
711f258
feat: update processDataFromFlow to add layout to nodes if needed
ogabrielluiz Aug 5, 2024
d21843e
feat: Update flowsManagerStore to parse flow data from file before pr…
ogabrielluiz Aug 5, 2024
c9a6c34
Refactor import paths to use 'initialize' module in 'base.py'
ogabrielluiz Aug 5, 2024
db0a021
feat: Add method to set class source code and integrate it with front…
ogabrielluiz Aug 5, 2024
d088421
refactor: Update sourceHandle dataType to use custom component class …
ogabrielluiz Aug 5, 2024
685cc8c
fix: Raise error for unknown vertex types instead of returning defaul…
ogabrielluiz Aug 5, 2024
e216287
refactor: Remove redundant call to _import_vertex_types() in VertexTy…
ogabrielluiz Aug 5, 2024
afc9a3f
refactor: Simplify add_code_field by removing unnecessary field_confi…
ogabrielluiz Aug 5, 2024
bb88027
feat: Add elkjs dependency to package.json and package-lock.json for …
ogabrielluiz Aug 5, 2024
80a2b05
refactor: Update fields type in Template class to use InputTypes for …
ogabrielluiz Aug 5, 2024
a7820f3
refactor: Update import path for DefaultPromptField to improve code o…
ogabrielluiz Aug 5, 2024
ac27030
refactor: Reorganize imports in __init__.py for better structure and …
ogabrielluiz Aug 5, 2024
dff6f5e
refactor: Clean up imports in types.py for better organization and co…
ogabrielluiz Aug 5, 2024
cf5d719
refactor: Change vertex type annotations to strings for better compat…
ogabrielluiz Aug 5, 2024
5128701
refactor: Update component instantiation to include _code parameter a…
ogabrielluiz Aug 5, 2024
fe33a50
refactor: Remove unused CustomComponent import from __init__.py for c…
ogabrielluiz Aug 5, 2024
9aadcc3
refactor: Modify custom_component instantiation to include _code argu…
ogabrielluiz Aug 5, 2024
220a1c1
refactor: Update CustomComponent import in __init__.py for improved m…
ogabrielluiz Aug 5, 2024
beaf5e2
refactor: Update launch.json to include correct path for backend sour…
ogabrielluiz Aug 5, 2024
13e1cfb
refactor: Update dependencies in poetry.lock to latest versions and r…
ogabrielluiz Aug 5, 2024
e6f06b6
Merge branch 'main' into feat/elkjslayout
ogabrielluiz Aug 6, 2024
c689330
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 6, 2024
121c9c0
refactor: Remove unnecessary line in test_memory_chatbot.py
ogabrielluiz Aug 6, 2024
6e09dc3
refactor: Update dataType assignment in Component class to use compon…
ogabrielluiz Aug 6, 2024
a05a7bc
refactor: Correct flow_id reference in MemoryComponent to improve cla…
ogabrielluiz Aug 6, 2024
8e7c9ad
refactor: Update import path for DefaultPromptField to improve code o…
ogabrielluiz Aug 6, 2024
3c76f7b
refactor: Add loading module to __init__.py for improved organization…
ogabrielluiz Aug 6, 2024
73613e1
refactor: Clean up imports in base.py and enforce edge validation in …
ogabrielluiz Aug 6, 2024
ab8cdec
refactor: Remove edge component additions in test_base.py to streamli…
ogabrielluiz Aug 6, 2024
f21ff76
refactor: Mark @clack/prompts is-unicode-supported as extraneous in p…
ogabrielluiz Aug 6, 2024
13a5383
refactor: Update dataType assignment in Component class to use compon…
ogabrielluiz Aug 6, 2024
de11978
refactor: Fix edge existence check in Graph class to use correct vari…
ogabrielluiz Aug 6, 2024
7a0b10e
refactor: Add test for graph with edge and improve graph preparation …
ogabrielluiz Aug 6, 2024
c9e46ac
refactor: Set default node type to "genericNode" in getLayoutedNodes …
ogabrielluiz Aug 6, 2024
a1aa7cf
refactor: Add _call_inputs attribute to Component class
ogabrielluiz Jul 28, 2024
f3acfe6
refactor: Update test_base.py with code generation functions
ogabrielluiz Jul 28, 2024
00d6f86
refactor: Sort vertices in the graph
ogabrielluiz Jul 28, 2024
83fd9ec
feat(base.py): add support for flattening vertices layers in Graph class
ogabrielluiz Jul 28, 2024
9112f99
refactor: Rename test_generate_code to test_generate_import_statement…
ogabrielluiz Jul 28, 2024
d8403c5
feat(base.py): add support for flattening vertices layers in Graph class
ogabrielluiz Jul 28, 2024
f04d5ce
refactor: Sort components in the graph
ogabrielluiz Jul 28, 2024
da89cea
refactor: Add test_gerenate_script_from_graph to test_base.py
ogabrielluiz Jul 28, 2024
b8d987f
refactor: Add code generation functions to test_base.py
ogabrielluiz Jul 28, 2024
af2a3c2
feat(tests): add unit tests for component code generation and graph c…
ogabrielluiz Jul 28, 2024
12e4139
refactor(test_component_codegen.py): update import statement to impro…
ogabrielluiz Jul 28, 2024
a1aed06
refactor(test_component_codegen.py): update import statement to match…
ogabrielluiz Jul 28, 2024
080943b
fix(component.py): update call string generation to use .set method a…
ogabrielluiz Aug 6, 2024
87137f8
fix(component.py): add set call inputs in Component class
ogabrielluiz Aug 6, 2024
4b812fd
refactor(graph.py): rename start/end to entry/exit and update related…
ogabrielluiz Aug 6, 2024
8acb248
refactor(graph.py): update generate_script_from_graph to use entry/ex…
ogabrielluiz Aug 6, 2024
e287a85
refactor(component.py): update generate_script to include graph insta…
ogabrielluiz Aug 6, 2024
7ff77cb
test(tests): add pytest fixture for client in test_component_codegen.…
ogabrielluiz Aug 6, 2024
8493b58
test(tests): refactor test_generate_script to update TextOutput compo…
ogabrielluiz Aug 6, 2024
84f8219
Refactor test to use `set` method for setting input values in `test_g…
ogabrielluiz Aug 6, 2024
e6b3861
refactor(test_memory_chatbot.py): add test for generating script from…
ogabrielluiz Aug 6, 2024
870933b
Merge branch 'main' into feat/generategraphscript
ogabrielluiz Aug 7, 2024
c0c7331
refactor: enhance Vertex class with _build_instance and get_component…
ogabrielluiz Aug 7, 2024
461bf54
refactor: add Graph import statement and tidy up import handling in g…
ogabrielluiz Aug 7, 2024
8644d8f
refactor: add validation to prevent script generation from JSON in ge…
ogabrielluiz Aug 7, 2024
394ad1b
refactor: implement _find_entry method to improve graph entry compone…
ogabrielluiz Aug 7, 2024
13a490c
refactor: implement _find_exit method to enhance exit component resol…
ogabrielluiz Aug 7, 2024
5209ff6
refactor: update component retrieval to use get_component_instance me…
ogabrielluiz Aug 7, 2024
b9d6bfc
refactor: update graph script generation to access entry and exit pro…
ogabrielluiz Aug 7, 2024
93ddf5d
Merge branch 'main' into feat/generategraphscript
ogabrielluiz Sep 23, 2024
fdaf96f
[autofix.ci] apply automated fixes
autofix-ci[bot] Sep 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
52 changes: 52 additions & 0 deletions src/backend/base/langflow/code_gen/component.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from langflow.code_gen.generic import generate_import_statement, get_variable_name


def generate_call_string(instance):
variable_name = get_variable_name(instance)
if hasattr(instance, "_call_inputs"):
args = ", ".join(
f"{key}={get_variable_name(value.__self__)}.{value.__name__}" if callable(value) else f"{key}={repr(value)}"
for key, value in instance._call_inputs.items()
)
if args:
return f"{variable_name}.set({args})"


def generate_instantiation_string(instance):
if isinstance(instance, tuple):
raise ValueError(
"An instance of Component was expected, but a tuple was provided. You might be trying to call the component instead of calling the `set` method."
)
class_name = instance.__class__.__name__
instance_id = instance._id
variable_name = get_variable_name(instance)
return f"{variable_name} = {class_name}(_id='{instance_id}')"


def generate_graph_instantiation_string(entry, _exit):
return f"graph = Graph(entry={get_variable_name(entry)}, exit={get_variable_name(_exit)})"


def generate_script(*, instances, entry=None, exit=None):
import_statements = set()
instantiation_strings = []
call_strings = []

for instance in instances:
import_statements.add(generate_import_statement(instance))
instantiation_strings.append(generate_instantiation_string(instance))
call_string = generate_call_string(instance)

if call_string:
call_strings.append(call_string)

if entry is None or exit is None:
graph_instantiation_code = ""
else:
graph_instantiation_code = generate_graph_instantiation_string(entry, exit)
# Add Graph import statement to the beginning of the script
import_statements.add("from langflow.graph.graph.base import Graph")
import_code = "\n".join(sorted(import_statements))
instantiation_code = "\n".join(instantiation_strings)
call_code = "\n".join(call_strings)
return f"{import_code}\n\n{instantiation_code}\n\n{call_code}\n\n{graph_instantiation_code}".strip()
20 changes: 20 additions & 0 deletions src/backend/base/langflow/code_gen/generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import re


def get_variable_name(instance):
return re.sub(r"[^0-9a-zA-Z_]", "_", instance._id.lower())


def generate_import_statement(instance):
class_name = instance.__class__.__name__
module_path = instance.__class__.__module__
if module_path == "langflow.utils.validate":
raise ValueError("Generating script from JSON is not yet supported.")
parts = module_path.split(".")

# Construct the correct import statement
if len(parts) > 2:
module_path = ".".join(parts)
return f"from {module_path} import {class_name}"
else:
return f"from {module_path} import {class_name}"
11 changes: 11 additions & 0 deletions src/backend/base/langflow/code_gen/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from git import TYPE_CHECKING

from langflow.code_gen.component import generate_script

if TYPE_CHECKING:
from langflow.graph.graph.base import Graph


def generate_script_from_graph(graph: "Graph"):
script = generate_script(entry=graph.entry, exit=graph.exit, instances=graph.sort_components())
return script
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(self, **kwargs):
self._parameters = inputs or {}
self._edges: list[EdgeData] = []
self._components: list[Component] = []
self._call_inputs: dict[str, Any] = {}
self._current_output = ""
self._event_manager: EventManager | None = None
self._state_model = None
Expand All @@ -82,6 +83,9 @@ def __init__(self, **kwargs):
self._set_output_types()
self.set_class_code()

def _set_call_inputs(self, key: str, value: Any):
self._call_inputs[key] = value

def set_event_manager(self, event_manager: EventManager | None = None):
self._event_manager = event_manager

Expand Down Expand Up @@ -363,6 +367,7 @@ def _process_connection_or_parameter(self, key, value):
self._connect_to_component(key, value, _input)
else:
self._set_parameter_or_attribute(key, value)
self._set_call_inputs(key, value)

def _process_connection_or_parameters(self, key, value):
# if value is a list of components, we need to process each component
Expand Down
107 changes: 91 additions & 16 deletions src/backend/base/langflow/graph/graph/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@

def __init__(
self,
start: Optional["Component"] = None,
end: Optional["Component"] = None,
entry: Optional["Component"] = None,
exit: Optional["Component"] = None,
flow_id: str | None = None,
flow_name: str | None = None,
description: str | None = None,
Expand All @@ -71,9 +71,9 @@
"""
if log_config:
configure(**log_config)
self._start = start

Check failure on line 74 in src/backend/base/langflow/graph/graph/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F821)

src/backend/base/langflow/graph/graph/base.py:74:23: F821 Undefined name `start`
self._state_model = None
self._end = end

Check failure on line 76 in src/backend/base/langflow/graph/graph/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F821)

src/backend/base/langflow/graph/graph/base.py:76:21: F821 Undefined name `end`
self._prepared = False
self._runs = 0
self._updates = 0
Expand Down Expand Up @@ -110,6 +110,8 @@
self._first_layer: list[str] = []
self._lock = asyncio.Lock()
self.raw_graph_data: GraphData = {"nodes": [], "edges": []}
self._entry = None
self._exit = None
self._is_cyclic: bool | None = None
self._cycles: list[tuple[str, str]] | None = None
self._call_order: list[str] = []
Expand All @@ -119,10 +121,10 @@
except Exception as exc:
logger.error(f"Error getting tracing service: {exc}")
self.tracing_service = None
if start is not None and end is not None:
self._set_start_and_end(start, end)
self.prepare(start_component_id=start._id)
if (start is not None and end is None) or (start is None and end is not None):
if entry is not None and exit is not None:
self._set_entry_and_exit(entry, exit)
self.prepare(start_component_id=entry._id)
if (entry is not None and exit is None) or (entry is None and exit is not None):
raise ValueError("You must provide both input and output components")

@property
Expand Down Expand Up @@ -225,13 +227,60 @@

return component_id

def _set_start_and_end(self, start: "Component", end: "Component"):
if not hasattr(start, "to_frontend_node"):
raise TypeError(f"start must be a Component. Got {type(start)}")
if not hasattr(end, "to_frontend_node"):
raise TypeError(f"end must be a Component. Got {type(end)}")
self.add_component(start, start._id)
self.add_component(end, end._id)
def _find_entry(self):
# Find a vertex with a "ChatInput" component, overriding a "TextInput" component
found_entry = None
for vertex_id in self._is_input_vertices:
if "ChatInput" in vertex_id:
found_entry = vertex_id
break
elif found_entry is None and "TextInput" in vertex_id:
found_entry = vertex_id
vertex = self.get_vertex(found_entry) if found_entry else None
return vertex.get_component_instance() if vertex else None

@property
def entry(self):
if self._entry is None:
if entry := self._find_entry():
self._entry = entry
else:
raise ValueError(
"Graph has no entry component or couldn't find a suitable entry component (e.g. ChatInput)"
)
return self._entry

def _find_exit(self):
# Find a vertex with a "ChatOutput" component, overriding a "TextOutput" component
found_exit = None
for vertex_id in self._is_output_vertices:
if "ChatOutput" in vertex_id:
found_exit = vertex_id
break
elif found_exit is None and "TextOutput" in vertex_id:
found_exit = vertex_id
vertex = self.get_vertex(found_exit) if found_exit else None

return vertex.get_component_instance() if vertex else None

@property
def exit(self):
if self._exit is None:
if _exit := self._find_exit():
self._exit = _exit
else:
raise ValueError("Graph has no exit component")
return self._exit

def _set_entry_and_exit(self, entry: "Component", exit: "Component"):
if not hasattr(entry, "to_frontend_node"):
raise TypeError(f"start must be a Component. Got {type(entry)}")
if not hasattr(exit, "to_frontend_node"):
raise TypeError(f"end must be a Component. Got {type(exit)}")
self._entry = entry
self._exit = exit
self.add_component(entry._id, entry)
self.add_component(exit._id, exit)

def add_component_edge(self, source_id: str, output_input_tuple: tuple[str, str], target_id: str):
source_vertex = self.get_vertex(source_id)
Expand Down Expand Up @@ -1886,12 +1935,12 @@
vertex_ids = sort_up_to_vertex(dictionaryized_graph, vertex_id, is_start)
return [self.get_vertex(vertex_id) for vertex_id in vertex_ids]

def sort_vertices(
def _sort_vertices(
self,
stop_component_id: str | None = None,
start_component_id: str | None = None,
) -> list[str]:
"""Sorts the vertices in the graph."""
flatten: bool = False,
):
self.mark_all_vertices("ACTIVE")
if stop_component_id is not None:
self.stop_vertex = stop_component_id
Expand All @@ -1912,6 +1961,32 @@
# Now we should sort each layer in a way that we make sure
# vertex V does not depend on vertex V+1
vertices_layers = self.sort_layer_by_dependency(vertices_layers)
if flatten:
vertices_layers = chain.from_iterable(vertices_layers)
return vertices_layers

def sort_components(
self, stop_component_id: Optional[str] = None, start_component_id: Optional[str] = None
) -> List[str]:

Check failure on line 1970 in src/backend/base/langflow/graph/graph/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F821)

src/backend/base/langflow/graph/graph/base.py:1970:10: F821 Undefined name `List`
"""Sorts the vertices in the graph."""
vertices_layers = self._sort_vertices(
stop_component_id=stop_component_id, start_component_id=start_component_id, flatten=True
)
# Now get all the vertices instances
vertices = [self.get_vertex(vertex_id) for vertex_id in vertices_layers]
# Now we need to get the components
components = [vertex.get_component_instance() for vertex in vertices if hasattr(vertex, "_custom_component")]
return components

def sort_vertices(
self,
stop_component_id: Optional[str] = None,
start_component_id: Optional[str] = None,
) -> List[str]:

Check failure on line 1985 in src/backend/base/langflow/graph/graph/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F821)

src/backend/base/langflow/graph/graph/base.py:1985:10: F821 Undefined name `List`
"""Sorts the vertices in the graph."""
vertices_layers = self._sort_vertices(
stop_component_id=stop_component_id, start_component_id=start_component_id
)
self.increment_run_count()
self._sorted_vertices_layers = vertices_layers
first_layer = vertices_layers[0]
Expand Down
38 changes: 33 additions & 5 deletions src/backend/base/langflow/graph/vertex/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import types
from collections.abc import AsyncIterator, Callable, Iterator, Mapping
from enum import Enum
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Mapping, Optional

Check failure on line 9 in src/backend/base/langflow/graph/vertex/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F811)

src/backend/base/langflow/graph/vertex/base.py:9:40: F811 Redefinition of unused `AsyncIterator` from line 7

Check failure on line 9 in src/backend/base/langflow/graph/vertex/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F811)

src/backend/base/langflow/graph/vertex/base.py:9:55: F811 Redefinition of unused `Callable` from line 7

Check failure on line 9 in src/backend/base/langflow/graph/vertex/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F811)

src/backend/base/langflow/graph/vertex/base.py:9:71: F811 Redefinition of unused `Iterator` from line 7

Check failure on line 9 in src/backend/base/langflow/graph/vertex/base.py

View workflow job for this annotation

GitHub Actions / Ruff Style Check (3.12)

Ruff (F811)

src/backend/base/langflow/graph/vertex/base.py:9:87: F811 Redefinition of unused `Mapping` from line 7
from uuid import UUID

import pandas as pd
from loguru import logger
Expand All @@ -24,6 +25,7 @@
from langflow.schema.schema import INPUT_FIELD_NAME, OutputValue, build_output_logs
from langflow.services.deps import get_storage_service
from langflow.services.tracing.schema import Log
from langflow.utils.async_helpers import run_until_complete
from langflow.utils.constants import DIRECT_TYPES
from langflow.utils.schemas import ChatOutputResponse
from langflow.utils.util import sync_to_async, unescape_string
Expand Down Expand Up @@ -464,9 +466,7 @@
raise ValueError(f"Base type for vertex {self.display_name} not found")

if not self._custom_component:
custom_component, custom_params = await initialize.loading.instantiate_class(
user_id=user_id, vertex=self, event_manager=event_manager
)
custom_component, custom_params = await self._build_instance(user_id, event_manager)
else:
custom_component = self._custom_component
self._custom_component.set_event_manager(event_manager)
Expand All @@ -482,7 +482,35 @@

self._built = True

def extract_messages_from_artifacts(self, artifacts: dict[str, Any]) -> list[dict]:
async def _build_instance(self, user_id: Optional[str | UUID] = None, event_manager=None):
"""
Builds the instance of the component.
"""
if self._custom_component is not None:
raise ValueError("Component is already built.")
custom_component, custom_params = await initialize.loading.instantiate_class(
user_id=user_id, vertex=self, event_manager=event_manager
)
return custom_component, custom_params

def get_component_instance(self, user_id: Optional[str | UUID] = None):
"""
Retrieves the instance of the component.

Args:
user_id (Optional[str | UUID], optional): The user ID. Defaults to None.

Returns:
Any: The instance of the component.

Raises:
ValueError: If the component is not built.
"""
if not self._custom_component:
self._custom_component = run_until_complete(self._build_instance(user_id))[0]
return self._custom_component

def extract_messages_from_artifacts(self, artifacts: Dict[str, Any]) -> List[dict]:
"""
Extracts messages from the artifacts.

Expand Down
37 changes: 37 additions & 0 deletions src/backend/tests/unit/code_gen/test_component_codegen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest

from langflow import components
from langflow.code_gen.component import generate_instantiation_string, generate_script
from langflow.code_gen.generic import generate_import_statement


@pytest.fixture
def client():
pass


def test_generate_script():
chat_input = components.inputs.ChatInput(_id="chatInput-1230")
text_output = components.outputs.TextOutput.TextOutputComponent(_id="textoutput-1231")
text_output.set(input_value=chat_input.message_response)
script = generate_script(instances=(chat_input, text_output), entry=chat_input, exit=text_output)
assert (
script
== """from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.outputs.TextOutput import TextOutputComponent

chatinput_1230 = ChatInput(_id='chatInput-1230')
textoutput_1231 = TextOutputComponent(_id='textoutput-1231')

textoutput_1231.set(input_value=chatinput_1230.message_response)

graph = Graph(entry=chatinput_1230, exit=textoutput_1231)"""
)


def test_generate_import_statement_and_instantiation_string():
chat_input_instance = components.inputs.ChatInput(_id="chatInput-1230")
import_statement = generate_import_statement(chat_input_instance)
instantiation_string = generate_instantiation_string(chat_input_instance)
assert import_statement == "from langflow.components.inputs.ChatInput import ChatInput"
assert instantiation_string == "chatinput_1230 = ChatInput(_id='chatInput-1230')"
Empty file.
27 changes: 27 additions & 0 deletions src/backend/tests/unit/code_gen/test_graph_codegen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from langflow import components
from langflow.code_gen.graph import generate_script_from_graph
from langflow.graph.graph.base import Graph


def test_gerenate_script_from_graph():
chat_input = components.inputs.ChatInput(_id="chatInput-1230")
text_output = components.outputs.TextOutput.TextOutputComponent(_id="textoutput-1231")
text_output.set(input_value=chat_input.message_response)
chat_output = components.outputs.ChatOutput(input_value="test", _id="chatOutput-1232")
chat_output.set(input_value=text_output.text_response)

graph = Graph(chat_input, chat_output)
script = generate_script_from_graph(graph)
assert (
script
== """from langflow.components.inputs.ChatInput import ChatInput
from langflow.components.outputs.ChatOutput import ChatOutput
from langflow.components.outputs.TextOutput import TextOutputComponent

chatinput_1230 = ChatInput(_id='chatInput-1230')
textoutput_1231 = TextOutputComponent(_id='textoutput-1231')
chatoutput_1232 = ChatOutput(_id='chatOutput-1232')

textoutput_1231(input_value=chatinput_1230.message_response)
chatoutput_1232(input_value=textoutput_1231.text_response)"""
)
Loading
Loading