mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-25 10:32:22 +08:00
fix: MCP component auto reset issue in non Tool Mode (#10440)
* Optimize tool dropdown handling and output processing Improves logic for updating tool dropdown options by checking if the server has changed and whether tool mode is active, reducing unnecessary updates. Adds a process_output_item method to parse tool output as JSON when appropriate, enhancing output handling. * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) * [autofix.ci] apply automated fixes * Update component_index.json * Update Nvidia Remix.json * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) * Update component_index.json * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * use cache enabled even for the no tool mode * Update component_index.json * [autofix.ci] apply automated fixes * Update Nvidia Remix.json * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * Add Notion integration components to index Updated component_index.json to include new Notion integration components: AddContentToPage, NotionDatabaseProperties, NotionListPages, NotionPageContent, NotionPageCreator, NotionPageUpdate, and NotionSearch. These components provide functionality for interacting with Notion databases and pages, including querying, creating, updating, and retrieving content. * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * fix: add selector to let dropdown load add Select a tool selector to let dropdown load before interacting * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) * Update MCPToolsComponent code and metadata Updated the code and code_hash for MCPToolsComponent in Nvidia Remix starter project and synchronized the component_index.json to reflect the latest code and metadata. This ensures consistency and includes recent improvements or fixes to the MCPToolsComponent implementation. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Adam Aghili <Adam.Aghili@ibm.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@ -91,6 +91,8 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
await page.getByText("Select a tool");
|
||||
|
||||
await page.getByTestId("dropdown_str_tool").click();
|
||||
|
||||
const fetchOptionCount = await page.getByTestId("fetch-0-option").count();
|
||||
@ -283,6 +285,8 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
await page.getByText("Select a tool");
|
||||
|
||||
await page.getByTestId("dropdown_str_tool").click();
|
||||
|
||||
const fetchOptionCount = await page.getByTestId("fetch-0-option").count();
|
||||
@ -769,6 +773,8 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
await page.getByText("Select a tool");
|
||||
|
||||
await page.getByTestId("dropdown_str_tool").click();
|
||||
|
||||
const fetchOptionCount = await page.getByTestId("fetch-0-option").count();
|
||||
@ -977,6 +983,8 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
await page.getByText("Select a tool");
|
||||
|
||||
await page.getByTestId("dropdown_str_tool").click();
|
||||
|
||||
const fetchOptionCount2 = await page.getByTestId("fetch-0-option").count();
|
||||
@ -1162,6 +1170,8 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
await page.getByText("Select a tool");
|
||||
|
||||
await page.getByTestId("dropdown_str_tool").click();
|
||||
|
||||
// Check for tools from wiki
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import json
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from langchain_core.tools import StructuredTool # noqa: TC002
|
||||
|
||||
@ -325,9 +324,29 @@ class MCPToolsComponent(ComponentWithCache):
|
||||
|
||||
current_server_name = field_value.get("name") if isinstance(field_value, dict) else field_value
|
||||
_last_selected_server = safe_cache_get(self._shared_component_cache, "last_selected_server", "")
|
||||
server_changed = current_server_name != _last_selected_server
|
||||
|
||||
# Determine if "Tool Mode" is active by checking if the tool dropdown is hidden.
|
||||
is_in_tool_mode = build_config["tools_metadata"]["show"]
|
||||
|
||||
# Get use_cache setting to determine if we should use cached data
|
||||
use_cache = getattr(self, "use_cache", False)
|
||||
|
||||
# Fast path: if server didn't change and we already have options, keep them as-is
|
||||
# BUT only if caching is enabled or we're in tool mode
|
||||
existing_options = build_config.get("tool", {}).get("options") or []
|
||||
if not server_changed and existing_options:
|
||||
# In non-tool mode with cache disabled, skip the fast path to force refresh
|
||||
if not is_in_tool_mode and not use_cache:
|
||||
pass # Continue to refresh logic below
|
||||
else:
|
||||
if not is_in_tool_mode:
|
||||
build_config["tool"]["show"] = True
|
||||
return build_config
|
||||
|
||||
# To avoid unnecessary updates, only proceed if the server has actually changed
|
||||
if (_last_selected_server in (current_server_name, "")) and build_config["tool"]["show"]:
|
||||
# OR if caching is disabled (to force refresh in non-tool mode)
|
||||
if (_last_selected_server in (current_server_name, "")) and build_config["tool"]["show"] and use_cache:
|
||||
if current_server_name:
|
||||
servers_cache = safe_cache_get(self._shared_component_cache, "servers", {})
|
||||
if isinstance(servers_cache, dict):
|
||||
@ -339,36 +358,35 @@ class MCPToolsComponent(ComponentWithCache):
|
||||
return build_config
|
||||
else:
|
||||
return build_config
|
||||
|
||||
# Determine if "Tool Mode" is active by checking if the tool dropdown is hidden.
|
||||
is_in_tool_mode = build_config["tools_metadata"]["show"]
|
||||
safe_cache_set(self._shared_component_cache, "last_selected_server", current_server_name)
|
||||
|
||||
# Check if tools are already cached for this server before clearing
|
||||
cached_tools = None
|
||||
if current_server_name:
|
||||
use_cache = getattr(self, "use_cache", True)
|
||||
if use_cache:
|
||||
servers_cache = safe_cache_get(self._shared_component_cache, "servers", {})
|
||||
if isinstance(servers_cache, dict):
|
||||
cached = servers_cache.get(current_server_name)
|
||||
if cached is not None:
|
||||
try:
|
||||
cached_tools = cached["tools"]
|
||||
self.tools = cached_tools
|
||||
self.tool_names = cached["tool_names"]
|
||||
self._tool_cache = cached["tool_cache"]
|
||||
except (TypeError, KeyError, AttributeError) as e:
|
||||
# Handle corrupted cache data by ignoring it
|
||||
msg = f"Unable to use cached data for MCP Server,{current_server_name}: {e}"
|
||||
await logger.awarning(msg)
|
||||
cached_tools = None
|
||||
if current_server_name and use_cache:
|
||||
servers_cache = safe_cache_get(self._shared_component_cache, "servers", {})
|
||||
if isinstance(servers_cache, dict):
|
||||
cached = servers_cache.get(current_server_name)
|
||||
if cached is not None:
|
||||
try:
|
||||
cached_tools = cached["tools"]
|
||||
self.tools = cached_tools
|
||||
self.tool_names = cached["tool_names"]
|
||||
self._tool_cache = cached["tool_cache"]
|
||||
except (TypeError, KeyError, AttributeError) as e:
|
||||
# Handle corrupted cache data by ignoring it
|
||||
msg = f"Unable to use cached data for MCP Server,{current_server_name}: {e}"
|
||||
await logger.awarning(msg)
|
||||
cached_tools = None
|
||||
|
||||
# Only clear tools if we don't have cached tools for the current server
|
||||
if not cached_tools:
|
||||
self.tools = [] # Clear previous tools only if no cache
|
||||
|
||||
self.remove_non_default_keys(build_config) # Clear previous tool inputs
|
||||
# Clear previous tool inputs if:
|
||||
# 1. Server actually changed
|
||||
# 2. Cache is disabled (meaning tool list will be refreshed)
|
||||
if server_changed or not use_cache:
|
||||
self.remove_non_default_keys(build_config)
|
||||
|
||||
# Only show the tool dropdown if not in tool_mode
|
||||
if not is_in_tool_mode:
|
||||
@ -381,7 +399,12 @@ class MCPToolsComponent(ComponentWithCache):
|
||||
# Show loading state only when we need to fetch tools
|
||||
build_config["tool"]["placeholder"] = "Loading tools..."
|
||||
build_config["tool"]["options"] = []
|
||||
build_config["tool"]["value"] = uuid.uuid4()
|
||||
# Force a value refresh when:
|
||||
# 1. Server changed
|
||||
# 2. We don't have cached tools
|
||||
# 3. Cache is disabled (to force refresh on config changes)
|
||||
if server_changed or not cached_tools or not use_cache:
|
||||
build_config["tool"]["value"] = uuid.uuid4()
|
||||
else:
|
||||
# Keep the tool dropdown hidden if in tool_mode
|
||||
self._not_load_actions = True
|
||||
@ -426,18 +449,6 @@ class MCPToolsComponent(ComponentWithCache):
|
||||
continue
|
||||
return inputs
|
||||
|
||||
def remove_input_schema_from_build_config(
|
||||
self, build_config: dict, tool_name: str, input_schema: dict[list[InputTypes], Any]
|
||||
):
|
||||
"""Remove the input schema for the tool from the build config."""
|
||||
# Keep only schemas that don't belong to the current tool
|
||||
input_schema = {k: v for k, v in input_schema.items() if k != tool_name}
|
||||
# Remove all inputs from other tools
|
||||
for value in input_schema.values():
|
||||
for _input in value:
|
||||
if _input.name in build_config:
|
||||
build_config.pop(_input.name)
|
||||
|
||||
def remove_non_default_keys(self, build_config: dict) -> None:
|
||||
"""Remove non-default keys from the build config."""
|
||||
for key in list(build_config.keys()):
|
||||
@ -461,24 +472,23 @@ class MCPToolsComponent(ComponentWithCache):
|
||||
return
|
||||
|
||||
try:
|
||||
# Store current values before removing inputs
|
||||
# Store current values before removing inputs (only for the current tool)
|
||||
current_values = {}
|
||||
for key, value in build_config.items():
|
||||
if key not in self.default_keys and isinstance(value, dict) and "value" in value:
|
||||
current_values[key] = value["value"]
|
||||
|
||||
# Get all tool inputs and remove old ones
|
||||
input_schema_for_all_tools = self.get_inputs_for_all_tools(self.tools)
|
||||
self.remove_input_schema_from_build_config(build_config, tool_name, input_schema_for_all_tools)
|
||||
# Remove ALL non-default keys (all previous tool inputs)
|
||||
self.remove_non_default_keys(build_config)
|
||||
|
||||
# Get and validate new inputs
|
||||
# Get and validate new inputs for the selected tool
|
||||
self.schema_inputs = await self._validate_schema_inputs(tool_obj)
|
||||
if not self.schema_inputs:
|
||||
msg = f"No input parameters to configure for tool '{tool_name}'"
|
||||
await logger.ainfo(msg)
|
||||
return
|
||||
|
||||
# Add new inputs to build config
|
||||
# Add new inputs to build config for the selected tool only
|
||||
for schema_input in self.schema_inputs:
|
||||
if not schema_input or not hasattr(schema_input, "name"):
|
||||
msg = "Invalid schema input detected, skipping"
|
||||
|
||||
Reference in New Issue
Block a user