Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tool filter #6951

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open

Conversation

edwinjosechittilappilly
Copy link
Collaborator

@edwinjosechittilappilly edwinjosechittilappilly commented Mar 6, 2025

This pull request includes changes to enhance the tool metadata management by introducing a status flag and enabling dynamic filtering of tools based on their status. The most important changes include the addition of a status field to the tool metadata, modifications to filter tools based on their status, and the introduction of methods to manage the list of enabled tools.

Enhancements to tool metadata management:

Dynamic filtering of tools:

Management of enabled tools:

@github-actions github-actions bot added the enhancement New feature or request label Mar 6, 2025
codeflash-ai bot added a commit that referenced this pull request Mar 6, 2025
…(`feat-add-tool-filter`)

Here's the optimized version of the Python program.



In this revision, I applied the Walrus operator (`:=`) to avoid an additional lookup for the "tags" key in the dictionary, making the loop condition more direct and slightly improving performance.

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"]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return [tool["tags"][0] for tool in tools_metadata if tool["tags"]]
return [tags[0] for tool in tools_metadata if (tags := tool["tags"])]

Copy link
Contributor

codeflash-ai bot commented Mar 6, 2025

⚡️ Codeflash found optimizations for this PR

📄 19% (0.19x) speedup for Component._extract_tools_tags in src/backend/base/langflow/custom/custom_component/component.py

⏱️ Runtime : 312 microseconds 262 microseconds (best of 36 runs)

📝 Explanation and details

Here's the optimized version of the Python program.

In this revision, I applied the Walrus operator (:=) to avoid an additional lookup for the "tags" key in the dictionary, making the loop condition more direct and slightly improving performance.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 29 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage undefined
🌀 Generated Regression Tests Details
from __future__ import annotations

from copy import deepcopy
from typing import Any

import nanoid
# imports
import pytest  # used for our unit tests
from langflow.custom.custom_component.component import Component
from langflow.custom.custom_component.custom_component import CustomComponent
from langflow.events.event_manager import EventManager
from langflow.graph.edge.schema import EdgeData
from langflow.inputs.inputs import InputTypes
from langflow.services.tracing.schema import Log
from langflow.template.field.base import Output
from langflow.utils.async_helpers import run_until_complete


# unit tests
def test_basic_functionality():
    component = Component()
    codeflash_output = component._extract_tools_tags([{"tags": ["tag1"]}])
    codeflash_output = component._extract_tools_tags([{"tags": ["tag1"]}, {"tags": ["tag2"]}])
    codeflash_output = component._extract_tools_tags([{"tags": ["tag1", "tag2"]}, {"tags": ["tag3", "tag4"]}])

def test_edge_cases():
    component = Component()
    codeflash_output = component._extract_tools_tags([])
    codeflash_output = component._extract_tools_tags([{"tags": []}])
    codeflash_output = component._extract_tools_tags([{}])
    codeflash_output = component._extract_tools_tags([{"tags": ["tag1"]}, {"tags": []}, {}, {"tags": ["tag2"]}])

def test_complex_cases():
    component = Component()
    codeflash_output = component._extract_tools_tags([{"tags": [["nested_tag"]]}])
    codeflash_output = component._extract_tools_tags([{"tags": [123]}])
    codeflash_output = component._extract_tools_tags([{"tags": ["tag1", 123]}, {"tags": [456, "tag2"]}])

def test_large_scale_cases():
    component = Component()
    codeflash_output = component._extract_tools_tags([{"tags": ["tag" + str(i)]} for i in range(1000)])
    codeflash_output = component._extract_tools_tags([{"tags": ["tag" + str(i) for i in range(1000)]}])

def test_unusual_inputs():
    component = Component()
    with pytest.raises(TypeError):
        component._extract_tools_tags([{"tags": ["tag1"]}, "not_a_dict", {"tags": ["tag2"]}])
    with pytest.raises(TypeError):
        component._extract_tools_tags([{"tags": ["tag1"]}, 123, {"tags": ["tag2"]}, None])
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

from __future__ import annotations

from copy import deepcopy
from typing import Any

import nanoid
# imports
import pytest  # used for our unit tests
from langflow.custom.custom_component.component import Component
from langflow.custom.custom_component.custom_component import CustomComponent
from langflow.events.event_manager import EventManager
from langflow.graph.edge.schema import EdgeData
from langflow.inputs.inputs import InputTypes
from langflow.services.tracing.schema import Log
from langflow.template.field.base import Output
from langflow.utils.async_helpers import run_until_complete


# unit tests
@pytest.fixture
def component():
    return Component()

def test_single_tool_with_tags(component):
    tools_metadata = [{"tags": ["tag1", "tag2"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_multiple_tools_with_tags(component):
    tools_metadata = [{"tags": ["tag1", "tag2"]}, {"tags": ["tagA", "tagB"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_empty_list(component):
    tools_metadata = []
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_tool_without_tags(component):
    tools_metadata = [{"tags": []}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_mixed_tags_and_no_tags(component):
    tools_metadata = [{"tags": ["tag1"]}, {"tags": []}, {"tags": ["tag2"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_nested_tags(component):
    tools_metadata = [{"tags": [["nested_tag1"]]}, {"tags": ["tag2"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_tags_with_special_characters(component):
    tools_metadata = [{"tags": ["tag-1", "tag_2"]}, {"tags": ["tag@3", "tag#4"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_large_number_of_tools(component):
    tools_metadata = [{"tags": ["tag1"]}] * 1000
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_large_number_of_tags_in_a_tool(component):
    tools_metadata = [{"tags": ["tag" + str(i) for i in range(1000)]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_combination_of_large_number_of_tools_and_tags(component):
    tools_metadata = [{"tags": ["tag" + str(i) for i in range(100)]} for _ in range(1000)]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_non_dictionary_elements(component):
    tools_metadata = ["not_a_dict"]
    with pytest.raises(TypeError):
        component._extract_tools_tags(tools_metadata)



def test_single_tool_with_single_tag(component):
    tools_metadata = [{"tags": ["single_tag"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_single_tool_with_maximum_length_tag(component):
    tools_metadata = [{"tags": ["a" * 1000]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_tools_with_empty_strings_as_tags(component):
    tools_metadata = [{"tags": [""]}, {"tags": ["tag1"]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_extremely_large_data_set(component):
    tools_metadata = [{"tags": ["tag" + str(i) for i in range(1000)]} for _ in range(1000)]
    codeflash_output = component._extract_tools_tags(tools_metadata)

def test_high_complexity_tags(component):
    tools_metadata = [{"tags": ["tag" + "a" * i for i in range(1000)]}]
    codeflash_output = component._extract_tools_tags(tools_metadata)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To test or edit this optimization locally git merge codeflash/optimize-pr6951-2025-03-06T16.47.32

Copy link

codspeed-hq bot commented Mar 6, 2025

CodSpeed Performance Report

Merging #6951 will improve performances by 71.24%

Comparing feat-add-tool-filter (fb741cb) with main (a8f4090)

Summary

⚡ 1 improvements
✅ 18 untouched benchmarks

Benchmarks breakdown

Benchmark BASE HEAD Change
test_cancel_nonexistent_build 13.6 ms 7.9 ms +71.24%

@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 6, 2025
@edwinjosechittilappilly edwinjosechittilappilly changed the base branch from main to fix-tool-mode-refresh-issues March 6, 2025 18:14
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 6, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Overview

This pull request enhances tool metadata management by introducing a new status field and dynamic filtering of tools based on their status, as well as methods to manage a list of enabled tools.

  • Add a boolean "status" field to tool metadata to indicate tool activity.
  • Introduce class-level methods (set_enabled_tools and get_enabled_tools) and a filtering mechanism (_filter_tools_by_status) in custom components.
  • Update tool metadata processing in component_tool.py to filter out tools with inactive status.

Reviewed Changes

File Description
src/backend/base/langflow/custom/custom_component/component.py New methods for managing enabled tools and status-based filtering.
src/backend/base/langflow/base/tools/constants.py Added new "status" metadata field to tools constants.
src/backend/base/langflow/base/tools/component_tool.py Updated metadata filtering logic based on tool status.

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

src/backend/base/langflow/base/tools/component_tool.py:311

  • Using the '|' operator in isinstance() checks is not supported. Replace it with a tuple: isinstance(tool, (StructuredTool, BaseTool)).
if isinstance(tool, StructuredTool | BaseTool) and tool.tags:

src/backend/base/langflow/custom/custom_component/component.py:1112

  • [nitpick] The nested conditional logic in _build_tools_metadata_input is somewhat complex; consider refactoring or adding inline comments to improve readability and maintainability.
if self.check_for_tool_tag_change(old_tags, new_tags):

"display_name": "Status",
"type": "boolean",
"description": "Indicates whether the tool is currently active. Set to True to activate this tool.",
"default": "False",
Copy link
Preview

Copilot AI Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for the 'status' field is set as a string "False" even though the type is boolean. Replace it with the boolean value False.

Suggested change
"default": "False",
"default": False,

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
@edwinjosechittilappilly edwinjosechittilappilly marked this pull request as ready for review March 6, 2025 18:21
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 6, 2025
@edwinjosechittilappilly
Copy link
Collaborator Author

Merge only after #6935

@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 6, 2025
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 8, 2025
Copy link
Contributor

@ogabrielluiz ogabrielluiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely need tests for these.

"display_name": "Status",
"type": "boolean",
"description": "Indicates whether the tool is currently active. Set to True to activate this tool.",
"default": "False",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be set to True by default instead? I think it shouldn't have quotes either.

@@ -35,6 +35,14 @@
"edit_mode": EditMode.INLINE,
"hidden": True,
},
{
"name": "status",
"display_name": "Status",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a more descriptive name for this. Enabled or Active.. something like that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of status right?

Base automatically changed from fix-tool-mode-refresh-issues to main March 10, 2025 14:57
@github-actions github-actions bot removed the enhancement New feature or request label Mar 10, 2025
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Mar 10, 2025
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 10, 2025
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 10, 2025
@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Mar 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request size:M This PR changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants