[BUG] Unknown bug when using dynamic fields
I am implementing a design that allows users to easily create tools using the Langchain StructuresTool. However, the first time I wrote the code as a custom component, the LangflowUI crashed. Upon restarting, the constructor works normally.
Here’s a description of the class:
-
Receives user code input.
-
In StructedTool, selects a class for the tool parameters to be provided as a schema via a dropdown.
-
Writes the tool code.
This is Toolcode
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
calculator = StructuredTool.from_function(
func=multiply,
name="Calculator",
description="multiply numbers",
args_schema=CalculatorInput,
return_direct=True,
# coroutine= ... <- you can specify an async method if desired as well
)
This is my customcomponent
from typing import Any, Dict, List, Callable
import ast
from langchain.agents import Tool
from langchain.tools import StructuredTool
from langflow.interface.custom.custom_component import CustomComponent
from langflow.schema.dotdict import dotdict
from langchain.pydantic_v1 import BaseModel, Field
class CodeStructuredTool(CustomComponent):
display_name = "Code To Tool"
description = "structuredtool dataclass code to tool"
documentation = "https://python.langchain.com/docs/modules/tools/custom_tools/#structuredtool-dataclass"
icon = "🐍"
field_order = ["tool_code", "name", "description", "return_direct", "tool_function", "tool_class"]
def build_config(self) -> Dict[str, Any]:
return {
"tool_code": {
"display_name": "Tool Code",
"info": "Enter the dataclass code.",
"placeholder": "def my_function(args):\n pass",
"multiline": True,
"refresh_button": True,
},
"name": {
"display_name": "Tool Name",
"info": "Enter the name of the tool.",
},
"description": {
"display_name": "Description",
"info": "Provide a brief description of what the tool does.",
},
"return_direct": {
"display_name": "Return Directly",
"info": "Should the tool return the function output directly?",
},
"tool_function": {
"display_name": "Tool Function",
"info": "Select the function for additional expressions.",
"options": [],
"refresh_button": True,
},
"tool_class": {
"display_name": "Tool Class",
"info": "Select the class for additional expressions.",
"options": [],
"refresh_button": True,
"required": False,
},
}
def parse_source_name(self, code:str)-> Dict:
parsed_code = ast.parse(code)
class_names = [node.name for node in parsed_code.body if isinstance(node, ast.ClassDef)]
function_names = [node.name for node in parsed_code.body if isinstance(node, ast.FunctionDef)]
return {"class": class_names, "function": function_names}
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:
if field_name == "tool_code" or field_name == "tool_function" or field_name == "tool_class":
try:
names = self.parse_source_name(build_config.tool_code.value)
build_config.tool_class.options = names["class"]
build_config.tool_function.options = names["function"]
except Exception as e:
self.status = f"Failed to extract class names: {str(e)}"
build_config.tool_class.options = ["파싱실패", str(e)]
build_config.tool_function.options = []
return build_config
async def build(self, tool_code: Code, name: str, description: str, tool_function: List[str], return_direct: bool, tool_class: List[str]=None) -> Tool:
local_namespace = {}
exec(tool_code, globals(), local_namespace)
func = local_namespace[tool_function]
_class = None
if tool_class:
_class = local_namespace[tool_class]
tool = StructuredTool.from_function(
func=func,
args_schema=_class,
name=name,
description=description,
return_direct=return_direct
)
return tool
Additionally, when using dynamic fields and setting realtime_refresh to True, it seems there may be a conflict with the Langflow UI history back, causing the code, text, or certain entered values to revert to their previous state (especially when selecting specific values from a dropdown menu as a List type).
While manually pressing the refresh button works fine, we hope it would function without having to press the button.
Hello, Sorry for the delay. Did you try using the new version? Does the error still persist?