feat: add dynamic outputs to Component (#4308)

* Refine condition to check for undefined value in use-handle-new-value hook

* update mutateTemplate to use APIClassType

* Refactor Component class to update inputs and validate outputs

This commit refactors the Component class in the `component.py` file. It introduces two new methods: `update_inputs` and `run_and_validate_update_outputs`. The `update_inputs` method allows for updating the build configuration with new field values, while the `run_and_validate_update_outputs` method updates the frontend node and validates the outputs. Additionally, the `_validate_frontend_node` method is added to check if all outputs are valid. The `_set_output_types` method is modified to accept a list of outputs and set their return types. Overall, these changes improve the functionality and maintainability of the Component class.

* Add dynamic output validation in API endpoint for component updates

* Update build_config to store field_value under "value" key

* Refactor: Convert dict values to list in _set_output_types call

* Add type check for `cc_instance` before calling `run_and_validate_update_outputs`

* Add DynamicOutputComponent with configurable outputs based on input

* Add test for updating component outputs with dynamic code input

* Refactor: Make get_dynamic_output_component_code asynchronous for improved performance

---------

Co-authored-by: Edwin Jose <edwin.jose@datastax.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-11-04 09:31:56 -03:00 • committed by GitHub
commit 42cc1dacd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 145 additions and 38 deletions

View file

@ -0,0 +1,41 @@
# from langflow.field_typing import Data
from typing import Any
from langflow.custom import Component
from langflow.io import BoolInput, MessageTextInput, Output
from langflow.schema import Data
class DynamicOutputComponent(Component):
display_name = "Dynamic Output Component"
description = "Use as a template to create your own component."
documentation: str = "http://docs.langflow.org/components/custom"
icon = "custom_components"
name = "DynamicOutputComponent"
inputs = [
MessageTextInput(name="input_value", display_name="Input Value", value="Hello, World!"),
BoolInput(name="show_output", display_name="Show Output", value=True, real_time_refresh=True),
]
outputs = [
Output(display_name="Output", name="output", method="build_output"),
]
def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any):
if field_name == "show_output":
if field_value:
frontend_node["outputs"].append(
Output(display_name="Tool Output", name="tool_output", method="build_output")
)
else:
# remove the output
frontend_node["outputs"] = [
output for output in frontend_node["outputs"] if output["name"] != "tool_output"
]
return frontend_node
def build_output(self) -> Data:
data = Data(value=self.input_value)
self.status = data
return data

View file

@ -1,5 +1,16 @@
import asyncio
from pathlib import Path
from typing import Any
from fastapi import status
from httpx import AsyncClient
from langflow.api.v1.schemas import UpdateCustomComponentRequest
async def get_dynamic_output_component_code():
return await asyncio.to_thread(
Path("src/backend/tests/data/dynamic_output_component.py").read_text, encoding="utf-8"
)
async def test_get_version(client: AsyncClient):
@ -23,3 +34,21 @@ async def test_get_config(client: AsyncClient):
assert "auto_saving" in result, "The dictionary must contain a key called 'auto_saving'"
assert "health_check_max_retries" in result, "The dictionary must contain a 'health_check_max_retries' key"
assert "max_file_size_upload" in result, "The dictionary must contain a key called 'max_file_size_upload'"
async def test_update_component_outputs(client: AsyncClient, logged_in_headers: dict):
code = await get_dynamic_output_component_code()
frontend_node: dict[str, Any] = {"outputs": []}
request = UpdateCustomComponentRequest(
code=code,
frontend_node=frontend_node,
field="show_output",
field_value=True,
template={},
)
response = await client.post("api/v1/custom_component/update", json=request.model_dump(), headers=logged_in_headers)
result = response.json()
assert response.status_code == status.HTTP_200_OK
output_names = [output["name"] for output in result["outputs"]]
assert "tool_output" in output_names