feat: improving tool mode metadata updates and tables refresh (#6935)
* updates to tool mode * Update src/backend/base/langflow/custom/custom_component/component.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [autofix.ci] apply automated fixes * fix the update real time table refresh issues. * [autofix.ci] apply automated fixes * Update component.py * Update component.py * format errors * [autofix.ci] apply automated fixes * Update index.tsx * [autofix.ci] apply automated fixes * lint error fix * [autofix.ci] apply automated fixes * fix format issue * [autofix.ci] apply automated fixes * fix typing.override does not exist in your Python version --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
94140ccf2e
commit
a8f4090888
4 changed files with 98 additions and 50 deletions
|
|
@ -38,3 +38,5 @@ TOOL_TABLE_SCHEMA = [
|
|||
]
|
||||
|
||||
TOOLS_METADATA_INFO = "Modify tool names and descriptions to help agents understand when to use each tool."
|
||||
|
||||
TOOL_UPDATE_CONSTANTS = ["tool_mode", "tool_actions", TOOLS_METADATA_INPUT_NAME, "flow_name_selected"]
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ from abc import abstractmethod
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
from typing_extensions import override
|
||||
|
||||
from langflow.base.tools.constants import TOOLS_METADATA_INPUT_NAME
|
||||
from langflow.custom import Component
|
||||
from langflow.custom.custom_component.component import _get_component_toolkit
|
||||
from langflow.field_typing import Tool
|
||||
|
|
@ -36,7 +36,6 @@ class RunFlowBaseComponent(Component):
|
|||
info="The name of the flow to run.",
|
||||
options=[],
|
||||
real_time_refresh=True,
|
||||
refresh_button=True,
|
||||
value=None,
|
||||
),
|
||||
MessageInput(
|
||||
|
|
@ -201,12 +200,13 @@ class RunFlowBaseComponent(Component):
|
|||
field.input_types = []
|
||||
return fields
|
||||
|
||||
async def to_toolkit(self) -> list[Tool]:
|
||||
@override
|
||||
async def _get_tools(self) -> list[Tool]:
|
||||
component_toolkit: type[ComponentToolkit] = _get_component_toolkit()
|
||||
flow_description, tool_mode_inputs = await self.get_required_data(self.flow_name_selected)
|
||||
# # convert list of dicts to list of dotdicts
|
||||
tool_mode_inputs = [dotdict(field) for field in tool_mode_inputs]
|
||||
tools = component_toolkit(component=self).get_tools(
|
||||
return component_toolkit(component=self).get_tools(
|
||||
tool_name=f"{self.flow_name_selected}_tool",
|
||||
tool_description=(
|
||||
f"Tool designed to execute the flow '{self.flow_name_selected}'. Flow details: {flow_description}."
|
||||
|
|
@ -214,6 +214,3 @@ class RunFlowBaseComponent(Component):
|
|||
callbacks=self.get_langchain_callbacks(),
|
||||
flow_mode_inputs=tool_mode_inputs,
|
||||
)
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME):
|
||||
tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)
|
||||
return tools
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ if TYPE_CHECKING:
|
|||
from langflow.graph.edge.schema import EdgeData
|
||||
from langflow.graph.vertex.base import Vertex
|
||||
from langflow.inputs.inputs import InputTypes
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
from langflow.schema.log import LoggableType
|
||||
|
||||
|
||||
|
|
@ -1050,12 +1051,94 @@ class Component(CustomComponent):
|
|||
return Input(**kwargs)
|
||||
|
||||
async def to_toolkit(self) -> list[Tool]:
|
||||
component_toolkit: type[ComponentToolkit] = _get_component_toolkit()
|
||||
tools = component_toolkit(component=self).get_tools(callbacks=self.get_langchain_callbacks())
|
||||
"""Convert component to a list of tools.
|
||||
|
||||
This is a template method that defines the skeleton of the toolkit creation
|
||||
algorithm. Subclasses can override _get_tools() to provide custom tool
|
||||
implementations while maintaining the metadata update functionality.
|
||||
|
||||
Returns:
|
||||
list[Tool]: A list of tools with updated metadata. Each tool contains:
|
||||
- name: The name of the tool
|
||||
- description: A description of what the tool does
|
||||
- tags: List of tags associated with the tool
|
||||
"""
|
||||
# Get tools from subclass implementation
|
||||
tools = await self._get_tools()
|
||||
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME):
|
||||
tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)
|
||||
return self._update_tools_with_metadata(tools, self.tools_metadata)
|
||||
return tools
|
||||
|
||||
async def _get_tools(self) -> list[Tool]:
|
||||
"""Get the list of tools for this component.
|
||||
|
||||
This method can be overridden by subclasses to provide custom tool implementations.
|
||||
The default implementation uses ComponentToolkit.
|
||||
|
||||
Returns:
|
||||
list[Tool]: List of tools provided by this component
|
||||
"""
|
||||
component_toolkit: type[ComponentToolkit] = _get_component_toolkit()
|
||||
return component_toolkit(component=self).get_tools(callbacks=self.get_langchain_callbacks())
|
||||
|
||||
def _extract_tools_tags(self, tools_metadata: list[dict]) -> list[str]:
|
||||
"""Extract the first tag from each tool's metadata."""
|
||||
return [tool["tags"][0] for tool in tools_metadata if tool["tags"]]
|
||||
|
||||
def _update_tools_with_metadata(self, tools: list[Tool], metadata: DataFrame | None) -> list[Tool]:
|
||||
"""Update tools with provided metadata."""
|
||||
component_toolkit: type[ComponentToolkit] = _get_component_toolkit()
|
||||
return component_toolkit(component=self, metadata=metadata).update_tools_metadata(tools=tools)
|
||||
|
||||
def check_for_tool_tag_change(self, old_tags: list[str], new_tags: list[str]) -> bool:
|
||||
return old_tags != new_tags
|
||||
|
||||
async def _build_tools_metadata_input(self):
|
||||
tools = await self.to_toolkit()
|
||||
# Always use the latest tool data
|
||||
tool_data = [{"name": tool.name, "description": tool.description, "tags": tool.tags} for tool in tools]
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME):
|
||||
old_tags = self._extract_tools_tags(self.tools_metadata)
|
||||
new_tags = self._extract_tools_tags(tool_data)
|
||||
if self.check_for_tool_tag_change(old_tags, new_tags):
|
||||
self.tools_metadata = tool_data
|
||||
else:
|
||||
tool_data = self.tools_metadata
|
||||
else:
|
||||
self.tools_metadata = tool_data
|
||||
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="Edit tools",
|
||||
real_time_refresh=True,
|
||||
table_schema=TOOL_TABLE_SCHEMA,
|
||||
value=tool_data,
|
||||
table_icon="Hammer",
|
||||
trigger_icon="Hammer",
|
||||
trigger_text="",
|
||||
table_options=TableOptions(
|
||||
block_add=True,
|
||||
block_delete=True,
|
||||
block_edit=True,
|
||||
block_sort=True,
|
||||
block_filter=True,
|
||||
block_hide=True,
|
||||
block_select=True,
|
||||
hide_options=True,
|
||||
field_parsers={
|
||||
"name": [FieldParserType.SNAKE_CASE, FieldParserType.NO_BLANK],
|
||||
"commands": FieldParserType.COMMANDS,
|
||||
},
|
||||
description=TOOLS_METADATA_INFO,
|
||||
),
|
||||
)
|
||||
|
||||
def get_project_name(self):
|
||||
if hasattr(self, "_tracing_service") and self._tracing_service:
|
||||
return self._tracing_service.project_name
|
||||
|
|
@ -1267,45 +1350,6 @@ class Component(CustomComponent):
|
|||
def _build_tool_output(self) -> Output:
|
||||
return Output(name=TOOL_OUTPUT_NAME, display_name=TOOL_OUTPUT_DISPLAY_NAME, method="to_toolkit", types=["Tool"])
|
||||
|
||||
async def _build_tools_metadata_input(self):
|
||||
tools = await self.to_toolkit()
|
||||
tool_data = (
|
||||
self.tools_metadata
|
||||
if hasattr(self, TOOLS_METADATA_INPUT_NAME)
|
||||
else [{"name": tool.name, "description": tool.description, "tags": tool.tags} 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="Edit tools",
|
||||
real_time_refresh=True,
|
||||
table_schema=TOOL_TABLE_SCHEMA,
|
||||
value=tool_data,
|
||||
table_icon="Hammer",
|
||||
trigger_icon="Hammer",
|
||||
trigger_text="",
|
||||
table_options=TableOptions(
|
||||
block_add=True,
|
||||
block_delete=True,
|
||||
block_edit=True,
|
||||
block_sort=True,
|
||||
block_filter=True,
|
||||
block_hide=True,
|
||||
block_select=True,
|
||||
hide_options=True,
|
||||
field_parsers={
|
||||
"name": [FieldParserType.SNAKE_CASE, FieldParserType.NO_BLANK],
|
||||
"commands": FieldParserType.COMMANDS,
|
||||
},
|
||||
description=TOOLS_METADATA_INFO,
|
||||
),
|
||||
)
|
||||
|
||||
def get_input_display_name(self, input_name: str) -> str:
|
||||
"""Get the display name of an input.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FormatColumns, generateBackendColumnsFromValue } from "@/utils/utils";
|
|||
import { DataTypeDefinition, SelectionChangedEvent } from "ag-grid-community";
|
||||
import { AgGridReact } from "ag-grid-react";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { ForwardedIconComponent } from "../../../../common/genericIconComponent";
|
||||
import { Button } from "../../../../ui/button";
|
||||
import { InputProps, TableComponentType } from "../../types";
|
||||
|
|
@ -68,6 +68,11 @@ export default function TableNodeComponent({
|
|||
const [tempValue, setTempValue] = useState<any[]>(cloneDeep(value));
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const agGrid = useRef<AgGridReact>(null);
|
||||
// Add useEffect to sync with incoming value changes
|
||||
useEffect(() => {
|
||||
setTempValue(cloneDeep(value));
|
||||
}, [value]);
|
||||
|
||||
const componentColumns = columns
|
||||
? columns
|
||||
: generateBackendColumnsFromValue(tempValue ?? [], table_options);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue