Refactor field configuration in add_new_custom_field function (#1194)
This commit is contained in:
commit
58d91d99e1
7 changed files with 65 additions and 5 deletions
|
|
@ -56,7 +56,7 @@ The CustomComponent class serves as the foundation for creating custom component
|
||||||
- **build_config**: Used to define the configuration fields of the component (if applicable). It should always return a dictionary with specific keys representing the field names and corresponding configurations. This method is called when the code is processed (i.e., when you click _Check and Save_ in the code editor). It must follow the format described below:
|
- **build_config**: Used to define the configuration fields of the component (if applicable). It should always return a dictionary with specific keys representing the field names and corresponding configurations. This method is called when the code is processed (i.e., when you click _Check and Save_ in the code editor). It must follow the format described below:
|
||||||
|
|
||||||
- Top-level keys are field names.
|
- Top-level keys are field names.
|
||||||
- Their values are also of type _`dict`_. They specify the behavior of the generated fields.
|
- Their values are can be of type _`langflow.field_typing.TemplateField`_ or _`dict`_. They specify the behavior of the generated fields.
|
||||||
|
|
||||||
Below are the available keys used to configure component fields:
|
Below are the available keys used to configure component fields:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
from langflow.template.field.base import TemplateField
|
||||||
|
|
||||||
from .constants import (
|
from .constants import (
|
||||||
AgentExecutor,
|
AgentExecutor,
|
||||||
BaseChatMemory,
|
BaseChatMemory,
|
||||||
|
|
@ -46,4 +48,5 @@ __all__ = [
|
||||||
"BasePromptTemplate",
|
"BasePromptTemplate",
|
||||||
"ChatPromptTemplate",
|
"ChatPromptTemplate",
|
||||||
"Prompt",
|
"Prompt",
|
||||||
|
"TemplateField",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ def add_new_custom_field(
|
||||||
advanced=field_advanced,
|
advanced=field_advanced,
|
||||||
placeholder=placeholder,
|
placeholder=placeholder,
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
**field_config,
|
**sanitize_field_config(field_config),
|
||||||
)
|
)
|
||||||
template.get("template")[field_name] = new_field.model_dump(by_alias=True, exclude_none=True)
|
template.get("template")[field_name] = new_field.model_dump(by_alias=True, exclude_none=True)
|
||||||
template.get("custom_fields")[field_name] = None
|
template.get("custom_fields")[field_name] = None
|
||||||
|
|
@ -135,6 +135,13 @@ def add_new_custom_field(
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_field_config(field_config: Dict):
|
||||||
|
# If any of the already existing keys are in field_config, remove them
|
||||||
|
for key in ["name", "field_type", "value", "required", "placeholder", "display_name", "advanced", "show"]:
|
||||||
|
field_config.pop(key, None)
|
||||||
|
return field_config
|
||||||
|
|
||||||
|
|
||||||
# TODO: Move to correct place
|
# TODO: Move to correct place
|
||||||
def add_code_field(template, raw_code, field_config):
|
def add_code_field(template, raw_code, field_config):
|
||||||
# Field with the Python code to allow update
|
# Field with the Python code to allow update
|
||||||
|
|
@ -222,7 +229,10 @@ def build_field_config(
|
||||||
try:
|
try:
|
||||||
build_config: Dict = custom_class(user_id=user_id).build_config()
|
build_config: Dict = custom_class(user_id=user_id).build_config()
|
||||||
|
|
||||||
for field_name, field_dict in build_config.items():
|
for field_name, field in build_config.items():
|
||||||
|
# Allow user to build TemplateField as well
|
||||||
|
# as a dict with the same keys as TemplateField
|
||||||
|
field_dict = get_field_dict(field)
|
||||||
if update_field is not None and field_name != update_field:
|
if update_field is not None and field_name != update_field:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|
@ -244,6 +254,13 @@ def build_field_config(
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def get_field_dict(field):
|
||||||
|
"""Get the field dictionary from a TemplateField or a dict"""
|
||||||
|
if isinstance(field, TemplateField):
|
||||||
|
return field.model_dump(by_alias=True, exclude_none=True)
|
||||||
|
return field
|
||||||
|
|
||||||
|
|
||||||
def update_field_dict(field_dict):
|
def update_field_dict(field_dict):
|
||||||
"""Update the field dictionary by calling options() or value() if they are callable"""
|
"""Update the field dictionary by calling options() or value() if they are callable"""
|
||||||
if "options" in field_dict and callable(field_dict["options"]):
|
if "options" in field_dict and callable(field_dict["options"]):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from typing import Any, Optional
|
from abc import ABC
|
||||||
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, field_serializer
|
from pydantic import BaseModel, ConfigDict, Field, field_serializer
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ class TemplateField(BaseModel):
|
||||||
password: bool = False
|
password: bool = False
|
||||||
"""Specifies if the field is a password. Defaults to False."""
|
"""Specifies if the field is a password. Defaults to False."""
|
||||||
|
|
||||||
options: Optional[list[str]] = None
|
options: Union[list[str], Callable] = []
|
||||||
"""List of options for the field. Only used when is_list=True. Default is an empty list."""
|
"""List of options for the field. Only used when is_list=True. Default is an empty list."""
|
||||||
|
|
||||||
name: str = ""
|
name: str = ""
|
||||||
|
|
|
||||||
|
|
@ -308,3 +308,11 @@ def test_component_code():
|
||||||
# load the content as a string
|
# load the content as a string
|
||||||
with open(path, "r") as f:
|
with open(path, "r") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_component_with_templatefield_code():
|
||||||
|
path = Path(__file__).parent.absolute() / "data" / "component_with_templatefield.py"
|
||||||
|
# load the content as a string
|
||||||
|
with open(path, "r") as f:
|
||||||
|
return f.read()
|
||||||
|
|
|
||||||
17
tests/data/component_with_templatefield.py
Normal file
17
tests/data/component_with_templatefield.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import random
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.field_typing import TemplateField
|
||||||
|
|
||||||
|
|
||||||
|
class TestComponent(CustomComponent):
|
||||||
|
def refresh_values(self):
|
||||||
|
# This is a function that will be called every time the component is updated
|
||||||
|
# and should return a list of random strings
|
||||||
|
return [f"Random {random.randint(1, 100)}" for _ in range(5)]
|
||||||
|
|
||||||
|
def build_config(self):
|
||||||
|
return {"param": TemplateField(display_name="Param", options=self.refresh_values)}
|
||||||
|
|
||||||
|
def build(self, param: int):
|
||||||
|
return param
|
||||||
|
|
@ -549,3 +549,17 @@ def test_build_langchain_template_custom_component_valid_code(test_component_cod
|
||||||
frontend_node = build_custom_component_template(component, update_field="param")
|
frontend_node = build_custom_component_template(component, update_field="param")
|
||||||
new_param_options = frontend_node["template"]["param"]["options"]
|
new_param_options = frontend_node["template"]["param"]["options"]
|
||||||
assert param_options != new_param_options
|
assert param_options != new_param_options
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_langchain_template_custom_component_templatefield(test_component_with_templatefield_code):
|
||||||
|
component = create_and_validate_component(test_component_with_templatefield_code)
|
||||||
|
frontend_node = build_custom_component_template(component)
|
||||||
|
assert isinstance(frontend_node, dict)
|
||||||
|
template = frontend_node["template"]
|
||||||
|
assert isinstance(template, dict)
|
||||||
|
assert "param" in template
|
||||||
|
param_options = template["param"]["options"]
|
||||||
|
# Now run it again with an update field
|
||||||
|
frontend_node = build_custom_component_template(component, update_field="param")
|
||||||
|
new_param_options = frontend_node["template"]["param"]["options"]
|
||||||
|
assert param_options != new_param_options
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue