Skip to content

Commit

Permalink
feat: Allow variables to be defined outside a Component (langflow-ai#…
Browse files Browse the repository at this point in the history
…4316)

* feat: Allow variables to be defined outside a Component

Fixes langflow-ai#4315

Modify `prepare_global_scope` function in `src/backend/base/langflow/utils/validate.py` to evaluate classes, functions, and variables defined outside the Component's class.

* **Function Changes:**
  - Add evaluation of external classes using `ast.ClassDef`.
  - Add evaluation of external functions using `ast.FunctionDef`.
  - Add evaluation of external variables using `ast.Assign`.

* **Unit Tests:**
  - Add tests in `src/backend/tests/unit/test_validate_code.py` to verify the evaluation of external classes, functions, and variables.
  - Include tests for multiple external classes and external variables and functions.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/langflow-ai/langflow/issues/4315?shareId=XXXX-XXXX-XXXX-XXXX).

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and diogocabral committed Nov 26, 2024
1 parent 801385c commit e895e5a
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
"GitHub.vscode-pull-request-github"
]
}
},
"tasks": {
"test": "make unit_tests"
}

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
Expand Down
Binary file added localhost-19-2.db
Binary file not shown.
10 changes: 10 additions & 0 deletions src/backend/base/langflow/utils/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ def prepare_global_scope(code, module):
except ModuleNotFoundError as e:
msg = f"Module {node.module} not found. Please install it and try again"
raise ModuleNotFoundError(msg) from e
elif isinstance(node, ast.ClassDef):
# Compile and execute the class definition to properly create the class
class_code = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(class_code, exec_globals)
elif isinstance(node, ast.FunctionDef):
function_code = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(function_code, exec_globals)
elif isinstance(node, ast.Assign):
assign_code = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(assign_code, exec_globals)
return exec_globals


Expand Down
72 changes: 71 additions & 1 deletion src/backend/tests/unit/test_validate_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
from unittest import mock

import pytest
from langflow.utils.validate import create_function, execute_function, extract_function_name, validate_code
from langflow.utils.validate import (
create_class,
create_function,
execute_function,
extract_function_name,
validate_code,
)
from requests.exceptions import MissingSchema


Expand Down Expand Up @@ -100,3 +106,67 @@ def my_function(x):
"""
with mock.patch("requests.get", side_effect=MissingSchema), pytest.raises(MissingSchema):
execute_function(code, "my_function", "invalid_url")


def test_create_class():
code = """
from langflow.custom import CustomComponent
class ExternalClass:
def __init__(self, value):
self.value = value
class MyComponent(CustomComponent):
def build(self):
return ExternalClass("test")
"""
class_name = "MyComponent"
created_class = create_class(code, class_name)
instance = created_class()
result = instance.build()
assert result.value == "test"


def test_create_class_with_multiple_external_classes():
code = """
from langflow.custom import CustomComponent
class ExternalClass1:
def __init__(self, value):
self.value = value
class ExternalClass2:
def __init__(self, value):
self.value = value
class MyComponent(CustomComponent):
def build(self):
return ExternalClass1("test1"), ExternalClass2("test2")
"""
class_name = "MyComponent"
created_class = create_class(code, class_name)
instance = created_class()
result1, result2 = instance.build()
assert result1.value == "test1"
assert result2.value == "test2"


def test_create_class_with_external_variables_and_functions():
code = """
from langflow.custom import CustomComponent
external_variable = "external_value"
def external_function():
return "external_function_value"
class MyComponent(CustomComponent):
def build(self):
return external_variable, external_function()
"""
class_name = "MyComponent"
created_class = create_class(code, class_name)
instance = created_class()
result_variable, result_function = instance.build()
assert result_variable == "external_value"
assert result_function == "external_function_value"

0 comments on commit e895e5a

Please sign in to comment.