feat: Table Input for tools metadata (tool name and description) in component as tools (#4961)
* ✨ (endpoints.py): Add support for tool_mode field in custom component update 🔧 (schemas.py): Add tool_mode field to UpdateCustomComponentRequest schema ♻️ (component.py): Refactor run_and_validate_update_outputs method to handle tool_mode field 🔧 (index.tsx): Add tool_mode property to NodeInputField component 🔧 (index.tsx): Refactor hasToolMode logic to use checkHasToolMode utility function 🔧 (mutate-template.ts): Add callback parameter to mutateTemplate function 🔧 (use-handle-new-value.tsx): Add tool_mode property to useHandleOnNewValue hook 🔧 (popover/index.tsx): Add console.log for placeholder, value, and id 🔧 (inputGlobalComponent/index.tsx): Add tool_mode property to InputGlobalComponent 🔧 (refreshParameterComponent/index.tsx): Add tool_mode property to RefreshParameterComponent 🔧 (use-post-template-value.ts): Add tool_mode parameter to usePostTemplateValue function 🔧 (nodeToolbarComponent/index.tsx): Add support for tool_mode functionality in NodeToolbarComponent 🔧 (reactflowUtils.ts): Add checkHasToolMode utility function to check for tool_mode field in template * fix: Set default value for tool_mode in UpdateCustomComponentRequest schema * adding table input in tool mode adding table input * Update component.py update real-time refresh * added dynamic tool description input added dynamic tool description input * [autofix.ci] apply automated fixes * Update component.py simplifying the tool mode logic * Update component_tool.py updated logic to be more readable * Update component.py * adding tool table schema as constant --------- Co-authored-by: cristhianzl <cristhian.lousa@gmail.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
505368ece7
commit
2c56177ef1
4 changed files with 74 additions and 7 deletions
|
|
@ -609,7 +609,6 @@ async def custom_component_update(
|
|||
"""
|
||||
try:
|
||||
component = Component(_code=code_request.code)
|
||||
|
||||
component_node, cc_instance = build_custom_component_template(
|
||||
component,
|
||||
user_id=user.id,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import asyncio
|
|||
import re
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from langchain_core.tools import ToolException
|
||||
import pandas as pd
|
||||
from langchain_core.tools import BaseTool, ToolException
|
||||
from langchain_core.tools.structured import StructuredTool
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -18,7 +19,6 @@ if TYPE_CHECKING:
|
|||
from collections.abc import Callable
|
||||
|
||||
from langchain_core.callbacks import Callbacks
|
||||
from langchain_core.tools import BaseTool
|
||||
|
||||
from langflow.custom.custom_component.component import Component
|
||||
from langflow.events.event_manager import EventManager
|
||||
|
|
@ -160,8 +160,9 @@ def _format_tool_name(name: str):
|
|||
|
||||
|
||||
class ComponentToolkit:
|
||||
def __init__(self, component: Component):
|
||||
def __init__(self, component: Component, metadata: pd.DataFrame | None = None):
|
||||
self.component = component
|
||||
self.metadata = metadata
|
||||
|
||||
def get_tools(
|
||||
self, tool_name: str | None = None, tool_description: str | None = None, callbacks: Callbacks | None = None
|
||||
|
|
@ -241,3 +242,19 @@ class ComponentToolkit:
|
|||
)
|
||||
raise ValueError(msg)
|
||||
return tools
|
||||
|
||||
def update_tools_metadata(
|
||||
self,
|
||||
tools: list[BaseTool | StructuredTool],
|
||||
) -> list[BaseTool]:
|
||||
# update the tool_name and description according to the name and secriotion mentioned in the list
|
||||
if isinstance(self.metadata, pd.DataFrame):
|
||||
metadata_dict = self.metadata.to_dict(orient="records")
|
||||
for tool, metadata in zip(tools, metadata_dict, strict=False):
|
||||
if isinstance(tool, StructuredTool | BaseTool):
|
||||
tool.name = metadata.get("name", tool.name)
|
||||
tool.description = metadata.get("description", tool.description)
|
||||
else:
|
||||
msg = f"Expected a StructuredTool or BaseTool, got {type(tool)}"
|
||||
raise TypeError(msg)
|
||||
return tools
|
||||
|
|
|
|||
|
|
@ -1,2 +1,17 @@
|
|||
TOOL_OUTPUT_NAME = "component_as_tool"
|
||||
TOOL_OUTPUT_DISPLAY_NAME = "Toolset"
|
||||
TOOLS_METADATA_INPUT_NAME = "tools_metadata"
|
||||
TOOL_TABLE_SCHEMA = [
|
||||
{
|
||||
"name": "name",
|
||||
"display_name": "Name",
|
||||
"type": "str",
|
||||
"description": "Specify the name of the output field.",
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"display_name": "Description",
|
||||
"type": "str",
|
||||
"description": "Describe the purpose of the output field.",
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,7 +13,12 @@ import yaml
|
|||
from langchain_core.tools import StructuredTool
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
from langflow.base.tools.constants import TOOL_OUTPUT_DISPLAY_NAME, TOOL_OUTPUT_NAME
|
||||
from langflow.base.tools.constants import (
|
||||
TOOL_OUTPUT_DISPLAY_NAME,
|
||||
TOOL_OUTPUT_NAME,
|
||||
TOOL_TABLE_SCHEMA,
|
||||
TOOLS_METADATA_INPUT_NAME,
|
||||
)
|
||||
from langflow.custom.tree_visitor import RequiredInputsVisitor
|
||||
from langflow.exceptions.component import StreamingError
|
||||
from langflow.field_typing import Tool # noqa: TCH001 Needed by _add_toolkit_output
|
||||
|
|
@ -399,7 +404,12 @@ class Component(CustomComponent):
|
|||
if field_name == "tool_mode" or frontend_node.get("tool_mode"):
|
||||
is_tool_mode = field_value or frontend_node.get("tool_mode")
|
||||
frontend_node["outputs"] = [self._build_tool_output()] if is_tool_mode else frontend_node["outputs"]
|
||||
|
||||
if is_tool_mode:
|
||||
frontend_node.setdefault("template", {})
|
||||
frontend_node["template"][TOOLS_METADATA_INPUT_NAME] = self._build_tools_metadata_input().to_dict()
|
||||
elif "template" in frontend_node:
|
||||
frontend_node["template"].pop(TOOLS_METADATA_INPUT_NAME, None)
|
||||
self.tools_metadata = frontend_node.get("template", {}).get(TOOLS_METADATA_INPUT_NAME, {}).get("value")
|
||||
return self._validate_frontend_node(frontend_node)
|
||||
|
||||
def _validate_frontend_node(self, frontend_node: dict):
|
||||
|
|
@ -966,7 +976,10 @@ class Component(CustomComponent):
|
|||
|
||||
def to_toolkit(self) -> list[Tool]:
|
||||
component_toolkit = _get_component_toolkit()
|
||||
return component_toolkit(component=self).get_tools(callbacks=self.get_langchain_callbacks())
|
||||
tools = component_toolkit(component=self).get_tools(callbacks=self.get_langchain_callbacks())
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME):
|
||||
tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)
|
||||
return tools
|
||||
|
||||
def get_project_name(self):
|
||||
if hasattr(self, "_tracing_service") and self._tracing_service:
|
||||
|
|
@ -1137,6 +1150,29 @@ class Component(CustomComponent):
|
|||
|
||||
def _append_tool_to_outputs_map(self):
|
||||
self._outputs_map[TOOL_OUTPUT_NAME] = self._build_tool_output()
|
||||
# add a new input for the tool schema
|
||||
# self.inputs.append(self._build_tool_schema())
|
||||
|
||||
def _build_tool_output(self) -> Output:
|
||||
return Output(name=TOOL_OUTPUT_NAME, display_name=TOOL_OUTPUT_DISPLAY_NAME, method="to_toolkit", types=["Tool"])
|
||||
|
||||
def _build_tools_metadata_input(self):
|
||||
tools = self.to_toolkit()
|
||||
tool_data = (
|
||||
self.tools_metadata
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME)
|
||||
else [{"name": tool.name, "description": tool.description} for tool in tools]
|
||||
)
|
||||
try:
|
||||
from langflow.io import TableInput
|
||||
except ImportError as e:
|
||||
msg = "Failed to import TableInput from langflow.io"
|
||||
raise ImportError(msg) from e
|
||||
|
||||
return TableInput(
|
||||
name=TOOLS_METADATA_INPUT_NAME,
|
||||
display_name="Tools Metadata",
|
||||
real_time_refresh=True,
|
||||
table_schema=TOOL_TABLE_SCHEMA,
|
||||
value=tool_data,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue