fix: pythonfunction component execution (#3747)
* Added CodeInput to types * Fixed PythonFunction to execute and return data or message * [autofix.ci] apply automated fixes * Added callable to outputs of pythonfunction * [autofix.ci] apply automated fixes * Updated function and tests * Fixed lint issues * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
bf640be53f
commit
0b1198efc4
7 changed files with 94 additions and 17 deletions
|
|
@ -1,27 +1,73 @@
|
||||||
from typing import Callable
|
from typing import Callable, List
|
||||||
|
|
||||||
from langflow.custom import CustomComponent
|
from langflow.custom import Component
|
||||||
from langflow.custom.utils import get_function
|
from langflow.custom.utils import get_function
|
||||||
from langflow.field_typing import Code
|
from langflow.io import CodeInput, Output
|
||||||
|
from langflow.schema import Data, dotdict
|
||||||
|
from langflow.schema.message import Message
|
||||||
|
|
||||||
|
|
||||||
class PythonFunctionComponent(CustomComponent):
|
class PythonFunctionComponent(Component):
|
||||||
display_name = "Python Function"
|
display_name = "Python Function"
|
||||||
description = "Define a Python function."
|
description = "Define and execute a Python function that returns a Data object or a Message."
|
||||||
icon = "Python"
|
icon = "Python"
|
||||||
name = "PythonFunction"
|
name = "PythonFunction"
|
||||||
beta = True
|
beta = True
|
||||||
|
|
||||||
def build_config(self):
|
inputs = [
|
||||||
return {
|
CodeInput(
|
||||||
"function_code": {
|
name="function_code",
|
||||||
"display_name": "Code",
|
display_name="Function Code",
|
||||||
"info": "The code for the function.",
|
info="The code for the function.",
|
||||||
"show": True,
|
),
|
||||||
},
|
]
|
||||||
}
|
|
||||||
|
|
||||||
def build(self, function_code: Code) -> Callable:
|
outputs = [
|
||||||
|
Output(
|
||||||
|
name="function_output",
|
||||||
|
display_name="Function Callable",
|
||||||
|
method="get_function_callable",
|
||||||
|
),
|
||||||
|
Output(
|
||||||
|
name="function_output_data",
|
||||||
|
display_name="Function Output (Data)",
|
||||||
|
method="execute_function_data",
|
||||||
|
),
|
||||||
|
Output(
|
||||||
|
name="function_output_str",
|
||||||
|
display_name="Function Output (Message)",
|
||||||
|
method="execute_function_message",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_function_callable(self) -> Callable:
|
||||||
|
function_code = self.function_code
|
||||||
self.status = function_code
|
self.status = function_code
|
||||||
func = get_function(function_code)
|
func = get_function(function_code)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
def execute_function(self) -> List[dotdict | str] | dotdict | str:
|
||||||
|
function_code = self.function_code
|
||||||
|
|
||||||
|
if not function_code:
|
||||||
|
return "No function code provided."
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = get_function(function_code)
|
||||||
|
return func()
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error executing function: {str(e)}"
|
||||||
|
|
||||||
|
def execute_function_data(self) -> List[Data]:
|
||||||
|
results = self.execute_function()
|
||||||
|
results = results if isinstance(results, list) else [results]
|
||||||
|
data = [(Data(text=x) if isinstance(x, str) else Data(**x)) for x in results]
|
||||||
|
return data
|
||||||
|
|
||||||
|
def execute_function_message(self) -> Message:
|
||||||
|
results = self.execute_function()
|
||||||
|
results = results if isinstance(results, list) else [results]
|
||||||
|
results_list = [str(x) for x in results]
|
||||||
|
results_str = "\n".join(results_list)
|
||||||
|
data = Message(text=results_str)
|
||||||
|
return data
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from .inputs import (
|
||||||
MultiselectInput,
|
MultiselectInput,
|
||||||
NestedDictInput,
|
NestedDictInput,
|
||||||
PromptInput,
|
PromptInput,
|
||||||
|
CodeInput,
|
||||||
SecretStrInput,
|
SecretStrInput,
|
||||||
StrInput,
|
StrInput,
|
||||||
TableInput,
|
TableInput,
|
||||||
|
|
@ -36,6 +37,7 @@ __all__ = [
|
||||||
"MultilineSecretInput",
|
"MultilineSecretInput",
|
||||||
"NestedDictInput",
|
"NestedDictInput",
|
||||||
"PromptInput",
|
"PromptInput",
|
||||||
|
"CodeInput",
|
||||||
"SecretStrInput",
|
"SecretStrInput",
|
||||||
"StrInput",
|
"StrInput",
|
||||||
"MessageTextInput",
|
"MessageTextInput",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Annotated, Any
|
from typing import Annotated, Any
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, PlainSerializer, field_validator, model_serializer
|
from pydantic import (
|
||||||
|
BaseModel,
|
||||||
|
ConfigDict,
|
||||||
|
Field,
|
||||||
|
PlainSerializer,
|
||||||
|
field_validator,
|
||||||
|
model_serializer,
|
||||||
|
)
|
||||||
|
|
||||||
from langflow.field_typing.range_spec import RangeSpec
|
from langflow.field_typing.range_spec import RangeSpec
|
||||||
from langflow.inputs.validators import CoalesceBool
|
from langflow.inputs.validators import CoalesceBool
|
||||||
|
|
@ -18,6 +25,7 @@ class FieldTypes(str, Enum):
|
||||||
NESTED_DICT = "NestedDict"
|
NESTED_DICT = "NestedDict"
|
||||||
FILE = "file"
|
FILE = "file"
|
||||||
PROMPT = "prompt"
|
PROMPT = "prompt"
|
||||||
|
CODE = "code"
|
||||||
OTHER = "other"
|
OTHER = "other"
|
||||||
TABLE = "table"
|
TABLE = "table"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@ class PromptInput(BaseInputMixin, ListableInputMixin, InputTraceMixin):
|
||||||
field_type: SerializableFieldTypes = FieldTypes.PROMPT
|
field_type: SerializableFieldTypes = FieldTypes.PROMPT
|
||||||
|
|
||||||
|
|
||||||
|
class CodeInput(BaseInputMixin, ListableInputMixin, InputTraceMixin):
|
||||||
|
field_type: SerializableFieldTypes = FieldTypes.CODE
|
||||||
|
|
||||||
|
|
||||||
# Applying mixins to a specific input type
|
# Applying mixins to a specific input type
|
||||||
class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTraceMixin):
|
class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTraceMixin):
|
||||||
field_type: SerializableFieldTypes = FieldTypes.TEXT
|
field_type: SerializableFieldTypes = FieldTypes.TEXT
|
||||||
|
|
@ -493,6 +497,7 @@ InputTypes = Union[
|
||||||
MultilineSecretInput,
|
MultilineSecretInput,
|
||||||
NestedDictInput,
|
NestedDictInput,
|
||||||
PromptInput,
|
PromptInput,
|
||||||
|
CodeInput,
|
||||||
SecretStrInput,
|
SecretStrInput,
|
||||||
StrInput,
|
StrInput,
|
||||||
MessageTextInput,
|
MessageTextInput,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from langflow.inputs import (
|
||||||
MultilineSecretInput,
|
MultilineSecretInput,
|
||||||
NestedDictInput,
|
NestedDictInput,
|
||||||
PromptInput,
|
PromptInput,
|
||||||
|
CodeInput,
|
||||||
SecretStrInput,
|
SecretStrInput,
|
||||||
StrInput,
|
StrInput,
|
||||||
TableInput,
|
TableInput,
|
||||||
|
|
@ -36,6 +37,7 @@ __all__ = [
|
||||||
"MultilineSecretInput",
|
"MultilineSecretInput",
|
||||||
"NestedDictInput",
|
"NestedDictInput",
|
||||||
"PromptInput",
|
"PromptInput",
|
||||||
|
"CodeInput",
|
||||||
"SecretStrInput",
|
"SecretStrInput",
|
||||||
"StrInput",
|
"StrInput",
|
||||||
"MessageTextInput",
|
"MessageTextInput",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from pydantic import ValidationError
|
||||||
|
|
||||||
from langflow.inputs.inputs import (
|
from langflow.inputs.inputs import (
|
||||||
BoolInput,
|
BoolInput,
|
||||||
|
CodeInput,
|
||||||
DataInput,
|
DataInput,
|
||||||
DictInput,
|
DictInput,
|
||||||
DropdownInput,
|
DropdownInput,
|
||||||
|
|
@ -99,6 +100,11 @@ def test_prompt_input_valid():
|
||||||
assert prompt_input.value == "Enter your name"
|
assert prompt_input.value == "Enter your name"
|
||||||
|
|
||||||
|
|
||||||
|
def test_code_input_valid():
|
||||||
|
code_input = CodeInput(name="valid_code", value="def hello():\n print('Hello, World!')")
|
||||||
|
assert code_input.value == "def hello():\n print('Hello, World!')"
|
||||||
|
|
||||||
|
|
||||||
def test_multiline_input_valid():
|
def test_multiline_input_valid():
|
||||||
multiline_input = MultilineInput(name="valid_multiline", value="This is a\nmultiline input")
|
multiline_input = MultilineInput(name="valid_multiline", value="This is a\nmultiline input")
|
||||||
assert multiline_input.value == "This is a\nmultiline input"
|
assert multiline_input.value == "This is a\nmultiline input"
|
||||||
|
|
@ -214,7 +220,10 @@ def test_instantiate_input_comprehensive():
|
||||||
"FloatInput": {"name": "float_input", "value": 10.5},
|
"FloatInput": {"name": "float_input", "value": 10.5},
|
||||||
"BoolInput": {"name": "bool_input", "value": True},
|
"BoolInput": {"name": "bool_input", "value": True},
|
||||||
"DictInput": {"name": "dict_input", "value": {"key": "value"}},
|
"DictInput": {"name": "dict_input", "value": {"key": "value"}},
|
||||||
"MultiselectInput": {"name": "multiselect_input", "value": ["option1", "option2"]},
|
"MultiselectInput": {
|
||||||
|
"name": "multiselect_input",
|
||||||
|
"value": ["option1", "option2"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for input_type, data in valid_data.items():
|
for input_type, data in valid_data.items():
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,13 @@ def test_python_function_component():
|
||||||
# Act
|
# Act
|
||||||
# function must be a string representation
|
# function must be a string representation
|
||||||
function = "def function():\n return 'Hello, World!'"
|
function = "def function():\n return 'Hello, World!'"
|
||||||
|
python_function_component.function_code = function
|
||||||
# result is the callable function
|
# result is the callable function
|
||||||
result = python_function_component.build(function)
|
result = python_function_component.get_function_callable()
|
||||||
|
result_message = python_function_component.execute_function_message()
|
||||||
|
result_data = python_function_component.execute_function_data()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert result() == "Hello, World!"
|
assert result() == "Hello, World!"
|
||||||
|
assert result_message.text == "Hello, World!"
|
||||||
|
assert result_data[0].text == "Hello, World!"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue