Add PythonFunctionComponent and test case for it

This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-04-01 00:13:33 -03:00
commit a8779b1205
6 changed files with 56 additions and 44 deletions

View file

@ -24,4 +24,5 @@ __all__ = [
"RunnableExecComponent", "RunnableExecComponent",
"SQLExecutorComponent", "SQLExecutorComponent",
"SubFlowComponent", "SubFlowComponent",
"PythonFunctionComponent",
] ]

View file

@ -4,13 +4,13 @@ from .DocumentToRecord import DocumentToRecordComponent
from .IDGenerator import UUIDGeneratorComponent from .IDGenerator import UUIDGeneratorComponent
from .MessageHistory import MessageHistoryComponent from .MessageHistory import MessageHistoryComponent
from .UpdateRecord import UpdateRecordComponent from .UpdateRecord import UpdateRecordComponent
from .RecordsToText import RecordsToTextComponent
__all__ = [ __all__ = [
"Component", "Component",
"UpdateRecordComponent", "UpdateRecordComponent",
"DocumentToRecordComponent", "DocumentToRecordComponent",
"UUIDGeneratorComponent", "UUIDGeneratorComponent",
"PythonFunctionComponent",
"RecordsToTextComponent", "RecordsToTextComponent",
"CreateRecordComponent", "CreateRecordComponent",
"MessageHistoryComponent", "MessageHistoryComponent",

View file

@ -424,6 +424,19 @@ def test_build_vertex_invalid_vertex_id(client, added_flow_with_prompt_and_histo
assert response.status_code == 500 assert response.status_code == 500
# Helper function to poll task status
def poll_task_status(client, headers, href, max_attempts=20, sleep_time=1):
for _ in range(max_attempts):
task_status_response = client.get(
href,
headers=headers,
)
if task_status_response.status_code == 200 and task_status_response.json()["status"] == "SUCCESS":
return task_status_response.json()
time.sleep(sleep_time)
return None # Return None if task did not complete in time
def test_successful_run(client, starter_project, created_api_key): def test_successful_run(client, starter_project, created_api_key):
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
flow_id = starter_project["id"] flow_id = starter_project["id"]
@ -455,11 +468,14 @@ def test_run_with_inputs_and_outputs(client, starter_project, created_api_key):
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
flow_id = starter_project["id"] flow_id = starter_project["id"]
payload = { payload = {
"inputs": [{"components": ["component1"], "input_value": "value1"}], "input_value": "value1",
"outputs": ["Component Name", "component_id"], "input_type": "text",
"output_type": "text",
"tweaks": {"parameter_name": "value"},
"stream": False,
} }
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK, response.text
# Validate the response structure and content # Validate the response structure and content
@ -475,16 +491,10 @@ def test_run_flow_with_caching_success(client: TestClient, starter_project, crea
flow_id = starter_project["id"] flow_id = starter_project["id"]
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = { payload = {
"inputs": [ "input_value": "value1",
{"components": ["component1"], "input_value": "value1"}, "input_type": "text",
{"components": ["component3"], "input_value": "value2"}, "output_type": "text",
], "tweaks": {"parameter_name": "value"},
"outputs": ["Component Name", "component_id"],
"tweaks": {
"parameter_name": "value",
"Component Name": {"parameter_name": "value"},
"component_id": {"parameter_name": "value"},
},
"stream": False, "stream": False,
} }
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)
@ -497,7 +507,7 @@ def test_run_flow_with_caching_success(client: TestClient, starter_project, crea
def test_run_flow_with_caching_invalid_flow_id(client: TestClient, created_api_key): def test_run_flow_with_caching_invalid_flow_id(client: TestClient, created_api_key):
invalid_flow_id = uuid4() invalid_flow_id = uuid4()
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = {"inputs": [], "outputs": [], "tweaks": {}, "stream": False} payload = {"input_value": "", "input_type": "text", "output_type": "text", "tweaks": {}, "stream": False}
response = client.post(f"/api/v1/run/{invalid_flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{invalid_flow_id}", json=payload, headers=headers)
assert response.status_code == status.HTTP_404_NOT_FOUND assert response.status_code == status.HTTP_404_NOT_FOUND
data = response.json() data = response.json()
@ -508,8 +518,7 @@ def test_run_flow_with_caching_invalid_flow_id(client: TestClient, created_api_k
def test_run_flow_with_caching_invalid_input_format(client: TestClient, starter_project, created_api_key): def test_run_flow_with_caching_invalid_input_format(client: TestClient, starter_project, created_api_key):
flow_id = starter_project["id"] flow_id = starter_project["id"]
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
payload = {"inputs": [{"invalid_key": "value"}], "outputs": [], "tweaks": {}, "stream": False} payload = {"invalid_key": "value"}
# This should raise an http 422 error not validation error
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
@ -518,8 +527,9 @@ def test_run_flow_with_session_id(client, starter_project, created_api_key):
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
flow_id = starter_project["id"] flow_id = starter_project["id"]
payload = { payload = {
"inputs": [{"components": ["component1"], "input_value": "value1"}], "input_value": "value1",
"outputs": ["Component Name", "component_id"], "input_type": "text",
"output_type": "text",
"session_id": "test-session-id", "session_id": "test-session-id",
} }
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)
@ -534,8 +544,9 @@ def test_run_flow_with_invalid_session_id(client, starter_project, created_api_k
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
flow_id = starter_project["id"] flow_id = starter_project["id"]
payload = { payload = {
"inputs": [{"components": ["component1"], "input_value": "value1"}], "input_value": "value1",
"outputs": ["Component Name", "component_id"], "input_type": "text",
"output_type": "text",
"session_id": "invalid-session-id", "session_id": "invalid-session-id",
} }
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)
@ -549,8 +560,9 @@ def test_run_flow_with_invalid_tweaks(client, starter_project, created_api_key):
headers = {"x-api-key": created_api_key.api_key} headers = {"x-api-key": created_api_key.api_key}
flow_id = starter_project["id"] flow_id = starter_project["id"]
payload = { payload = {
"inputs": [{"components": ["component1"], "input_value": "value1"}], "input_value": "value1",
"outputs": ["Component Name", "component_id"], "input_type": "text",
"output_type": "text",
"tweaks": {"invalid_tweak": "value"}, "tweaks": {"invalid_tweak": "value"},
} }
response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers) response = client.post(f"/api/v1/run/{flow_id}", json=payload, headers=headers)

View file

@ -1,5 +1,4 @@
from langchain_core.documents import Document from langchain_core.documents import Document
from langflow.components import helpers from langflow.components import helpers
from langflow.interface.custom.utils import build_custom_component_template from langflow.interface.custom.utils import build_custom_component_template
from langflow.schema import Record from langflow.schema import Record
@ -52,23 +51,9 @@ def test_uuid_generator_component():
assert len(result) == 36 assert len(result) == 36
def test_python_function_component():
# Arrange
python_function_component = helpers.PythonFunctionComponent()
# Act
# function must be a string representation
function = "def function():\n return 'Hello, World!'"
# result is the callable function
result = python_function_component.build(function)
# Assert
assert result() == "Hello, World!"
def test_records_as_text_component(): def test_records_as_text_component():
# Arrange # Arrange
records_as_text_component = helpers.RecordsAsTextComponent() records_as_text_component = helpers.RecordsToTextComponent()
# Act # Act
# Replace with your actual test data # Replace with your actual test data
@ -83,7 +68,7 @@ def test_records_as_text_component():
def test_text_to_record_component(): def test_text_to_record_component():
# Arrange # Arrange
text_to_record_component = helpers.TextToRecordComponent() text_to_record_component = helpers.CreateRecordComponent()
# Act # Act
# Replace with your actual test data # Replace with your actual test data

View file

@ -1,9 +1,6 @@
from itertools import chain from itertools import chain
import pytest import pytest
from sqlalchemy import func
from sqlmodel import select
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
from langflow.graph.schema import ResultData from langflow.graph.schema import ResultData
from langflow.initial_setup.setup import ( from langflow.initial_setup.setup import (
@ -14,6 +11,8 @@ from langflow.initial_setup.setup import (
) )
from langflow.services.database.models.flow.model import Flow from langflow.services.database.models.flow.model import Flow
from langflow.services.deps import session_scope from langflow.services.deps import session_scope
from sqlalchemy import func
from sqlmodel import select
def test_load_starter_projects(): def test_load_starter_projects():
@ -26,7 +25,7 @@ def test_get_project_data():
projects = load_starter_projects() projects = load_starter_projects()
for project in projects: for project in projects:
data = get_project_data(project) data = get_project_data(project)
assert all(d is not None for d in data) assert all(d is not None for d in data), f"Project {project} data is None"
def test_create_or_update_starter_projects(client): def test_create_or_update_starter_projects(client):

View file

@ -0,0 +1,15 @@
from langflow.components import experimental
def test_python_function_component():
# Arrange
python_function_component = experimental.PythonFunctionComponent()
# Act
# function must be a string representation
function = "def function():\n return 'Hello, World!'"
# result is the callable function
result = python_function_component.build(function)
# Assert
assert result() == "Hello, World!"