fix: re-add name and description editing on tool mode except for Composio, fix MCP server code (#7901)
* Changed backend to contain readonly props for tools * Show name editing for not readonly tools * Fixed edit-tools test * Updated command to use "uvx" instead of "npx" for stability * Fixed mcp code for authentication on auto_login=false * removed args from component desc * [autofix.ci] apply automated fixes * making tool mode inputs the priority. * fix: Clean up comments and whitespace in component_tool.py * Fix column name * update the dispaly name in composio * fix format * ✨ (get-started-progress.tsx): add data-testid attribute to improve testability and accessibility 🔧 (user-progress-track.spec.ts): update test assertions to use the new data-testid attribute for get started progress title --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Edwin Jose <edwin.jose@datastax.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org> Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
This commit is contained in:
parent
82e08d6100
commit
75deddd102
8 changed files with 128 additions and 60 deletions
|
|
@ -186,7 +186,7 @@ class ComposioBaseComponent(Component):
|
||||||
build_config["action"]["options"] = [
|
build_config["action"]["options"] = [
|
||||||
{
|
{
|
||||||
"name": self.sanitize_action_name(action),
|
"name": self.sanitize_action_name(action),
|
||||||
"metaData": action,
|
"metadata": action,
|
||||||
}
|
}
|
||||||
for action in self._actions_data
|
for action in self._actions_data
|
||||||
]
|
]
|
||||||
|
|
@ -282,10 +282,12 @@ class ComposioBaseComponent(Component):
|
||||||
configured_tools = []
|
configured_tools = []
|
||||||
for tool in tools:
|
for tool in tools:
|
||||||
# Set the sanitized name
|
# Set the sanitized name
|
||||||
display_name = self._sanitized_names.get(tool.name, self._name_sanitizer.sub("-", tool.name))
|
display_name = self._actions_data.get(tool.name, {}).get(
|
||||||
|
"display_name", self._sanitized_names.get(tool.name, self._name_sanitizer.sub("-", tool.name))
|
||||||
|
)
|
||||||
# Set the tags
|
# Set the tags
|
||||||
tool.tags = [tool.name]
|
tool.tags = [tool.name]
|
||||||
tool.metadata = {"display_name": display_name, "display_description": tool.description}
|
tool.metadata = {"display_name": display_name, "display_description": tool.description, "readonly": True}
|
||||||
configured_tools.append(tool)
|
configured_tools.append(tool)
|
||||||
return configured_tools
|
return configured_tools
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Literal
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from langchain_core.tools import BaseTool, ToolException
|
from langchain_core.tools import BaseTool, ToolException
|
||||||
from langchain_core.tools.structured import StructuredTool
|
from langchain_core.tools.structured import StructuredTool
|
||||||
from loguru import logger
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from langflow.base.tools.constants import TOOL_OUTPUT_NAME
|
from langflow.base.tools.constants import TOOL_OUTPUT_NAME
|
||||||
|
|
@ -40,21 +39,8 @@ def _get_input_type(input_: InputTypes):
|
||||||
|
|
||||||
|
|
||||||
def build_description(component: Component, output: Output) -> str:
|
def build_description(component: Component, output: Output) -> str:
|
||||||
if not output.required_inputs:
|
|
||||||
logger.warning(f"Output {output.name} does not have required inputs defined")
|
|
||||||
name = component.name or component.__class__.__name__
|
name = component.name or component.__class__.__name__
|
||||||
if output.required_inputs:
|
return f"{name}. {output.method} - {component.description}"
|
||||||
args = ", ".join(
|
|
||||||
sorted(
|
|
||||||
[
|
|
||||||
f"{name}. {input_name}: {_get_input_type(component._inputs[input_name])}"
|
|
||||||
for input_name in output.required_inputs
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
args = ""
|
|
||||||
return f"{name}. {output.method}({args}) - {component.description}"
|
|
||||||
|
|
||||||
|
|
||||||
async def send_message_noop(
|
async def send_message_noop(
|
||||||
|
|
@ -211,6 +197,8 @@ class ComponentToolkit:
|
||||||
inputs=flow_mode_inputs,
|
inputs=flow_mode_inputs,
|
||||||
param_key="flow_tweak_data",
|
param_key="flow_tweak_data",
|
||||||
)
|
)
|
||||||
|
elif tool_mode_inputs:
|
||||||
|
args_schema = create_input_schema(tool_mode_inputs)
|
||||||
elif output.required_inputs:
|
elif output.required_inputs:
|
||||||
inputs = [
|
inputs = [
|
||||||
self.component._inputs[input_name]
|
self.component._inputs[input_name]
|
||||||
|
|
@ -220,6 +208,7 @@ class ComponentToolkit:
|
||||||
# If any of the required inputs are not in tool mode, this means
|
# If any of the required inputs are not in tool mode, this means
|
||||||
# that when the tool is called it will raise an error.
|
# that when the tool is called it will raise an error.
|
||||||
# so we should raise an error here.
|
# so we should raise an error here.
|
||||||
|
# TODO: This logic might need to be improved, example if the required is an api key.
|
||||||
if not all(getattr(_input, "tool_mode", False) for _input in inputs):
|
if not all(getattr(_input, "tool_mode", False) for _input in inputs):
|
||||||
non_tool_mode_inputs = [
|
non_tool_mode_inputs = [
|
||||||
input_.name
|
input_.name
|
||||||
|
|
@ -234,8 +223,7 @@ class ComponentToolkit:
|
||||||
)
|
)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
args_schema = create_input_schema(inputs)
|
args_schema = create_input_schema(inputs)
|
||||||
elif tool_mode_inputs:
|
|
||||||
args_schema = create_input_schema(tool_mode_inputs)
|
|
||||||
else:
|
else:
|
||||||
args_schema = create_input_schema(self.component.inputs)
|
args_schema = create_input_schema(self.component.inputs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1166,6 +1166,7 @@ class Component(CustomComponent):
|
||||||
async def _build_tools_metadata_input(self):
|
async def _build_tools_metadata_input(self):
|
||||||
tools = await self._get_tools()
|
tools = await self._get_tools()
|
||||||
# Always use the latest tool data
|
# Always use the latest tool data
|
||||||
|
|
||||||
tool_data = [
|
tool_data = [
|
||||||
{
|
{
|
||||||
"name": tool.name,
|
"name": tool.name,
|
||||||
|
|
@ -1174,6 +1175,7 @@ class Component(CustomComponent):
|
||||||
"status": True, # Initialize all tools with status True
|
"status": True, # Initialize all tools with status True
|
||||||
"display_name": tool.metadata.get("display_name", tool.name),
|
"display_name": tool.metadata.get("display_name", tool.name),
|
||||||
"display_description": tool.metadata.get("display_description", tool.description),
|
"display_description": tool.metadata.get("display_description", tool.description),
|
||||||
|
"readonly": tool.metadata.get("readonly", False),
|
||||||
"args": tool.args,
|
"args": tool.args,
|
||||||
# "args_schema": tool.args_schema,
|
# "args_schema": tool.args_schema,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,10 @@ export const GetStartedProgress: FC<{
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 h-[10.8rem] w-full">
|
<div className="mt-3 h-[10.8rem] w-full">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<span className="text-sm font-semibold">
|
<span
|
||||||
|
className="text-sm font-semibold"
|
||||||
|
data-testid="get_started_progress_title"
|
||||||
|
>
|
||||||
{percentageGetStarted >= 100 ? (
|
{percentageGetStarted >= 100 ? (
|
||||||
<>
|
<>
|
||||||
<span>All Set</span> <span className="pl-1"> 🎉 </span>
|
<span>All Set</span> <span className="pl-1"> 🎉 </span>
|
||||||
|
|
|
||||||
|
|
@ -147,27 +147,27 @@ export default function ToolsTable({
|
||||||
cellClass: "text-muted-foreground",
|
cellClass: "text-muted-foreground",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: isAction ? "name" : "tags",
|
field: "name",
|
||||||
headerName: isAction ? "Action" : "Slug",
|
headerName: isAction ? "Action" : "Slug",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
valueGetter: (params) =>
|
valueGetter: (params) =>
|
||||||
isAction
|
params.data.name !== ""
|
||||||
? params.data.name !== ""
|
? parseString(params.data.name, [
|
||||||
? parseString(params.data.name, [
|
|
||||||
"snake_case",
|
|
||||||
"no_blank",
|
|
||||||
"uppercase",
|
|
||||||
])
|
|
||||||
: parseString(params.data.display_name, [
|
|
||||||
"snake_case",
|
|
||||||
"no_blank",
|
|
||||||
"uppercase",
|
|
||||||
])
|
|
||||||
: parseString(params.data.tags.join(", "), [
|
|
||||||
"snake_case",
|
"snake_case",
|
||||||
|
"no_blank",
|
||||||
"uppercase",
|
"uppercase",
|
||||||
]),
|
])
|
||||||
|
: isAction
|
||||||
|
? parseString(params.data.display_name, [
|
||||||
|
"snake_case",
|
||||||
|
"no_blank",
|
||||||
|
"uppercase",
|
||||||
|
])
|
||||||
|
: parseString(params.data.tags.join(", "), [
|
||||||
|
"snake_case",
|
||||||
|
"uppercase",
|
||||||
|
]),
|
||||||
cellClass: "text-muted-foreground",
|
cellClass: "text-muted-foreground",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -281,7 +281,7 @@ export default function ToolsTable({
|
||||||
>
|
>
|
||||||
<SidebarHeader className="flex-none px-4 py-4">
|
<SidebarHeader className="flex-none px-4 py-4">
|
||||||
{focusedRow &&
|
{focusedRow &&
|
||||||
(isAction ? (
|
(isAction || !focusedRow.readonly ? (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label
|
<label
|
||||||
|
|
|
||||||
|
|
@ -69,18 +69,17 @@ const McpServerTab = ({ folderName }: { folderName: string }) => {
|
||||||
const MCP_SERVER_JSON = `{
|
const MCP_SERVER_JSON = `{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"lf-${parseString(folderName ?? "project", ["snake_case", "no_blank", "lowercase"]).slice(0, 11)}": {
|
"lf-${parseString(folderName ?? "project", ["snake_case", "no_blank", "lowercase"]).slice(0, 11)}": {
|
||||||
"command": "npx",
|
"command": "uvx",
|
||||||
"args": [
|
"args": [
|
||||||
"-y",
|
"mcp-proxy",${
|
||||||
"supergateway",
|
|
||||||
"--sse",
|
|
||||||
"${apiUrl}"${
|
|
||||||
isAutoLogin
|
isAutoLogin
|
||||||
? ""
|
? ""
|
||||||
: `,
|
: `
|
||||||
"--header",
|
"--headers",
|
||||||
"x-api-key:${apiKey || "YOUR_API_KEY"}"`
|
"x-api-key",
|
||||||
|
"${apiKey || "YOUR_API_KEY"}",`
|
||||||
}
|
}
|
||||||
|
"${apiUrl}"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ test(
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("empty_page_drag_and_drop_text"),
|
page.getByTestId("empty_page_drag_and_drop_text"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(page.getByTestId("app-header")).not.toBeVisible();
|
await expect(
|
||||||
|
page.getByTestId("get_started_progress_title"),
|
||||||
|
).not.toBeVisible();
|
||||||
|
|
||||||
await page.getByTestId("empty_page_github_button").click();
|
await page.getByTestId("empty_page_github_button").click();
|
||||||
|
|
||||||
|
|
@ -69,7 +71,7 @@ test(
|
||||||
timeout: 100000,
|
timeout: 100000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(page.getByTestId("app-header")).toBeVisible();
|
await expect(page.getByTestId("get_started_progress_title")).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("github_starred_icon_get_started"),
|
page.getByTestId("github_starred_icon_get_started"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
@ -94,7 +96,7 @@ test(
|
||||||
|
|
||||||
await newPageDiscord.close();
|
await newPageDiscord.close();
|
||||||
|
|
||||||
await expect(page.getByTestId("app-header")).toBeVisible();
|
await expect(page.getByTestId("get_started_progress_title")).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("discord_joined_icon_get_started"),
|
page.getByTestId("discord_joined_icon_get_started"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
@ -156,7 +158,9 @@ test(
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("empty_page_drag_and_drop_text"),
|
page.getByTestId("empty_page_drag_and_drop_text"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(page.getByTestId("app-header")).not.toBeVisible();
|
await expect(
|
||||||
|
page.getByTestId("get_started_progress_title"),
|
||||||
|
).not.toBeVisible();
|
||||||
|
|
||||||
await page.getByTestId("empty_page_github_button").click();
|
await page.getByTestId("empty_page_github_button").click();
|
||||||
|
|
||||||
|
|
@ -188,7 +192,7 @@ test(
|
||||||
timeout: 100000,
|
timeout: 100000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(page.getByTestId("app-header")).toBeVisible();
|
await expect(page.getByTestId("get_started_progress_title")).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId("github_starred_icon_get_started"),
|
page.getByTestId("github_starred_icon_get_started"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,50 @@ test(
|
||||||
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
|
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
|
|
||||||
|
await page.locator('input[data-ref="eInput"]').nth(0).click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
await page.getByRole("gridcell").nth(0).click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('[data-testid="sidebar_header_name"]').isHidden(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="sidebar_header_description"]')
|
||||||
|
.isHidden(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('[data-testid="input_update_name"]').isVisible(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="input_update_description"]')
|
||||||
|
.isVisible(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
await page.locator('[data-testid="input_update_name"]').fill("test name");
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="input_update_description"]')
|
||||||
|
.fill("test description");
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page.getByText("Close").last().click();
|
await page.getByText("Close").last().click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
expect(await page.getByTestId("tool_test_name").isVisible()).toBe(true);
|
||||||
|
|
||||||
await page.waitForSelector(
|
await page.waitForSelector(
|
||||||
'[data-testid="generic-node-title-arrangement"]',
|
'[data-testid="generic-node-title-arrangement"]',
|
||||||
{
|
{
|
||||||
|
|
@ -93,38 +135,66 @@ test(
|
||||||
.isVisible(),
|
.isVisible(),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
await page.getByTestId("div-tools_tools_metadata").click();
|
await page.getByTestId("button_open_actions").click();
|
||||||
|
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('input[data-ref="eInput"]').nth(3).isChecked(),
|
await page.locator('input[data-ref="eInput"]').nth(3).isChecked(),
|
||||||
).toBe(false);
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
await page.locator('input[data-ref="eInput"]').nth(4).click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
|
await page.locator('input[data-ref="eInput"]').nth(4).isChecked(),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
|
|
||||||
await page.locator('input[data-ref="eInput"]').nth(3).click();
|
await page.getByRole("gridcell").nth(0).click();
|
||||||
|
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('input[data-ref="eInput"]').nth(3).isChecked(),
|
await page.locator('[data-testid="sidebar_header_name"]').isHidden(),
|
||||||
).toBe(true);
|
|
||||||
|
|
||||||
await page.getByRole("gridcell").nth(0).click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('[data-testid="sidebar_header_name"]').isVisible(),
|
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page
|
await page
|
||||||
.locator('[data-testid="sidebar_header_description"]')
|
.locator('[data-testid="sidebar_header_description"]')
|
||||||
|
.isHidden(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('[data-testid="input_update_name"]').isVisible(),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="input_update_description"]')
|
||||||
.isVisible(),
|
.isVisible(),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('[data-testid="input_update_name"]').inputValue(),
|
||||||
|
).toBe("test_name");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="input_update_description"]')
|
||||||
|
.inputValue(),
|
||||||
|
).toBe("test description");
|
||||||
|
|
||||||
|
await page.locator('[data-testid="input_update_name"]').fill("");
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
await page.locator('[data-testid="input_update_description"]').fill("");
|
||||||
|
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
await page.getByText("Close").last().click();
|
await page.getByText("Close").last().click();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue