Tools
This Langflow feature is currently in public preview. Development is ongoing, and the features and functionality are subject to change. Langflow, and the use of such, is subject to the DataStax Preview Terms. |
Tools are typically connected to agent components at the Tools port. Agents use LLMs as a reasoning engine to decide which of the connected tool components to use to solve a problem.
Tools in agentic functions are, essentially, functions that the agent can call to perform tasks or access external resources.
A function is wrapped as a Tool
object, with a common interface the agent understands.
Agents become aware of tools through tool registration, where the agent is provided a list of available tools, typically at agent initialization. The Tool
object’s description tells the agent what the tool can do.
The agent then uses a connected LLM to reason through the problem to decide which tool is best for the job.
Use a tool in a flow
Tools are typically connected to agent components at the Tools port.
The simple agent starter project uses URL and Calculator tools connected to an agent component to answer a user’s questions. The OpenAI LLM acts as a brain for the agent to decide which tool to use.

To make a component into a tool that an agent can use, enable Tool mode in the component. Enabling Tool mode modifies a component input to accept calls from an agent. If the component you want to connect to an agent doesn’t have a Tool mode option, you can modify the component’s inputs to become a tool. For an example, see Make any component a tool.
arXiv
This component searches and retrieves papers from arXiv.org.
Parameters
Name | Display Name | Info |
---|---|---|
search_query |
Search Query |
The search query for arXiv papers, for example, 'quantum computing'. |
search_type |
Search Field |
The field to search in. |
max_results |
Max Results |
Maximum number of results to return. |
Name | Display Name | Info |
---|---|---|
papers |
Papers |
List of retrieved arXiv papers. |
Component code
arxiv.py
404: Not Found
Astra DB tool
This component creates a tool an Agent can use to retrieve data from a DataStax Astra DB collection.
Parameters
Name | Type | Description |
---|---|---|
tool_name |
String |
The name of the tool |
tool_description |
String |
The description of the tool |
namespace |
String |
The namespace within Astra where the collection is stored (default: "default_keyspace") |
collection_name |
String |
The name of the collection within Astra DB |
token |
SecretString |
Authentication token for accessing Astra DB |
api_endpoint |
String |
API endpoint URL for the Astra DB service |
projection_attributes |
String |
Attributes to return, separated by commas (default: "*") |
tool_params |
List[Dict] |
Attributes to filter and description for the model |
static_filters |
List[Dict] |
Attributes to filter and corresponding values |
number_of_results |
Integer |
Number of results to return (default: 5) |
Name | Type | Description |
---|---|---|
tool |
StructuredTool |
Astra DB search tool for use in LangChain |
Component code
astradb_tool.py
import os
from datetime import datetime, timezone
from typing import Any
from astrapy import Collection, DataAPIClient, Database
from astrapy.admin import parse_api_endpoint
from langchain_core.tools import StructuredTool, Tool
from pydantic import BaseModel, Field, create_model
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.io import BoolInput, DictInput, HandleInput, IntInput, SecretStrInput, StrInput, TableInput
from langflow.logging import logger
from langflow.schema.data import Data
from langflow.schema.table import EditMode
class AstraDBToolComponent(LCToolComponent):
display_name: str = "Astra DB Tool"
description: str = "Tool to run hybrid vector and metadata search on DataStax Astra DB Collection"
documentation: str = "https://docs.langflow.org/components-bundle-components"
icon: str = "AstraDB"
inputs = [
StrInput(
name="tool_name",
display_name="Tool Name",
info="The name of the tool to be passed to the LLM.",
required=True,
),
StrInput(
name="tool_description",
display_name="Tool Description",
info="Describe the tool to LLM. Add any information that can help the LLM to use the tool.",
required=True,
),
StrInput(
name="keyspace",
display_name="Keyspace Name",
info="The name of the keyspace within Astra where the collection is stored.",
value="default_keyspace",
advanced=True,
),
StrInput(
name="collection_name",
display_name="Collection Name",
info="The name of the collection within Astra DB where the vectors will be stored.",
required=True,
),
SecretStrInput(
name="token",
display_name="Astra DB Application Token",
info="Authentication token for accessing Astra DB.",
value="ASTRA_DB_APPLICATION_TOKEN",
required=True,
),
SecretStrInput(
name="api_endpoint",
display_name="Database" if os.getenv("ASTRA_ENHANCED", "false").lower() == "true" else "API Endpoint",
info="API endpoint URL for the Astra DB service.",
value="ASTRA_DB_API_ENDPOINT",
required=True,
),
StrInput(
name="projection_attributes",
display_name="Projection Attributes",
info="Attributes to be returned by the tool separated by comma.",
required=True,
value="*",
advanced=True,
),
TableInput(
name="tools_params_v2",
display_name="Tools Parameters",
info="Define the structure for the tool parameters. Describe the parameters "
"in a way the LLM can understand how to use them.",
required=False,
table_schema=[
{
"name": "name",
"display_name": "Name",
"type": "str",
"description": "Specify the name of the output field/parameter for the model.",
"default": "field",
"edit_mode": EditMode.INLINE,
},
{
"name": "attribute_name",
"display_name": "Attribute Name",
"type": "str",
"description": "Specify the attribute name to be filtered on the collection. "
"Leave empty if the attribute name is the same as the name of the field.",
"default": "",
"edit_mode": EditMode.INLINE,
},
{
"name": "description",
"display_name": "Description",
"type": "str",
"description": "Describe the purpose of the output field.",
"default": "description of field",
"edit_mode": EditMode.POPOVER,
},
{
"name": "metadata",
"display_name": "Is Metadata",
"type": "boolean",
"edit_mode": EditMode.INLINE,
"description": ("Indicate if the field is included in the metadata field."),
"options": ["True", "False"],
"default": "False",
},
{
"name": "mandatory",
"display_name": "Is Mandatory",
"type": "boolean",
"edit_mode": EditMode.INLINE,
"description": ("Indicate if the field is mandatory."),
"options": ["True", "False"],
"default": "False",
},
{
"name": "is_timestamp",
"display_name": "Is Timestamp",
"type": "boolean",
"edit_mode": EditMode.INLINE,
"description": ("Indicate if the field is a timestamp."),
"options": ["True", "False"],
"default": "False",
},
{
"name": "operator",
"display_name": "Operator",
"type": "str",
"description": "Set the operator for the field. "
"https://docs.datastax.com/en/astra-db-serverless/api-reference/documents.html#operators",
"default": "$eq",
"options": ["$gt", "$gte", "$lt", "$lte", "$eq", "$ne", "$in", "$nin", "$exists", "$all", "$size"],
"edit_mode": EditMode.INLINE,
},
],
value=[],
),
DictInput(
name="tool_params",
info="DEPRECATED: Attributes to filter and description to the model. "
"Add ! for mandatory (e.g: !customerId)",
display_name="Tool params",
is_list=True,
advanced=True,
),
DictInput(
name="static_filters",
info="Attributes to filter and correspoding value",
display_name="Static filters",
advanced=True,
is_list=True,
),
IntInput(
name="number_of_results",
display_name="Number of Results",
info="Number of results to return.",
advanced=True,
value=5,
),
BoolInput(
name="use_search_query",
display_name="Semantic Search",
info="When this parameter is activated, the search query parameter will be used to search the collection.",
advanced=False,
value=False,
),
BoolInput(
name="use_vectorize",
display_name="Use Astra DB Vectorize",
info="When this parameter is activated, Astra DB Vectorize method will be used to generate the embeddings.",
advanced=False,
value=False,
),
HandleInput(name="embedding", display_name="Embedding Model", input_types=["Embeddings"]),
StrInput(
name="semantic_search_instruction",
display_name="Semantic Search Instruction",
info="The instruction to use for the semantic search.",
required=True,
value="Search query to find relevant documents.",
advanced=True,
),
]
_cached_client: DataAPIClient | None = None
_cached_db: Database | None = None
_cached_collection: Collection | None = None
def _build_collection(self):
if self._cached_collection:
return self._cached_collection
try:
environment = parse_api_endpoint(self.api_endpoint).environment
cached_client = DataAPIClient(self.token, environment=environment)
cached_db = cached_client.get_database(self.api_endpoint, keyspace=self.keyspace)
self._cached_collection = cached_db.get_collection(self.collection_name)
except Exception as e:
msg = f"Error building collection: {e}"
raise ValueError(msg) from e
else:
return self._cached_collection
def create_args_schema(self) -> dict[str, BaseModel]:
"""DEPRECATED: This method is deprecated. Please use create_args_schema_v2 instead.
It is keep only for backward compatibility.
"""
logger.warning("This is the old way to define the tool parameters. Please use the new way.")
args: dict[str, tuple[Any, Field] | list[str]] = {}
for key in self.tool_params:
if key.startswith("!"): # Mandatory
args[key[1:]] = (str, Field(description=self.tool_params[key]))
else: # Optional
args[key] = (str | None, Field(description=self.tool_params[key], default=None))
if self.use_search_query:
args["search_query"] = (
str | None,
Field(description="Search query to find relevant documents.", default=None),
)
model = create_model("ToolInput", **args, __base__=BaseModel)
return {"ToolInput": model}
def create_args_schema_v2(self) -> dict[str, BaseModel]:
"""Create the tool input schema using the new tool parameters configuration."""
args: dict[str, tuple[Any, Field] | list[str]] = {}
for tool_param in self.tools_params_v2:
if tool_param["mandatory"]:
args[tool_param["name"]] = (str, Field(description=tool_param["description"]))
else:
args[tool_param["name"]] = (str | None, Field(description=tool_param["description"], default=None))
if self.use_search_query:
args["search_query"] = (
str,
Field(description=self.semantic_search_instruction),
)
model = create_model("ToolInput", **args, __base__=BaseModel)
return {"ToolInput": model}
def build_tool(self) -> Tool:
"""Builds an Astra DB Collection tool.
Returns:
Tool: The built Astra DB tool.
"""
schema_dict = self.create_args_schema() if len(self.tool_params.keys()) > 0 else self.create_args_schema_v2()
tool = StructuredTool.from_function(
name=self.tool_name,
args_schema=schema_dict["ToolInput"],
description=self.tool_description,
func=self.run_model,
return_direct=False,
)
self.status = "Astra DB Tool created"
return tool
def projection_args(self, input_str: str) -> dict | None:
"""Build the projection arguments for the AstraDB query."""
elements = input_str.split(",")
result = {}
if elements == ["*"]:
return None
# Force the projection to exclude the $vector field as it is not required by the tool
result["$vector"] = False
# Fields with ! as prefix should be removed from the projection
for element in elements:
if element.startswith("!"):
result[element[1:]] = False
else:
result[element] = True
return result
def parse_timestamp(self, timestamp_str: str) -> datetime:
"""Parse a timestamp string into Astra DB REST API format.
Args:
timestamp_str (str): Input timestamp string
Returns:
datetime: Datetime object
Raises:
ValueError: If the timestamp cannot be parsed
"""
# Common datetime formats to try
formats = [
"%Y-%m-%d", # 2024-03-21
"%Y-%m-%dT%H:%M:%S", # 2024-03-21T15:30:00
"%Y-%m-%dT%H:%M:%S%z", # 2024-03-21T15:30:00+0000
"%Y-%m-%d %H:%M:%S", # 2024-03-21 15:30:00
"%d/%m/%Y", # 21/03/2024
"%Y/%m/%d", # 2024/03/21
]
for fmt in formats:
try:
# Parse the date string
date_obj = datetime.strptime(timestamp_str, fmt).astimezone()
# If the parsed date has no timezone info, assume UTC
if date_obj.tzinfo is None:
date_obj = date_obj.replace(tzinfo=timezone.utc)
# Convert to UTC and format
return date_obj.astimezone(timezone.utc)
except ValueError:
continue
msg = f"Could not parse date: {timestamp_str}"
logger.error(msg)
raise ValueError(msg)
def build_filter(self, args: dict, filter_settings: list) -> dict:
"""Build filter dictionary for AstraDB query.
Args:
args: Dictionary of arguments from the tool
filter_settings: List of filter settings from tools_params_v2
Returns:
Dictionary containing the filter conditions
"""
filters = {**self.static_filters}
for key, value in args.items():
# Skip search_query as it's handled separately
if key == "search_query":
continue
filter_setting = next((x for x in filter_settings if x["name"] == key), None)
if filter_setting and value is not None:
field_name = filter_setting["attribute_name"] if filter_setting["attribute_name"] else key
filter_key = field_name if not filter_setting["metadata"] else f"metadata.{field_name}"
if filter_setting["operator"] == "$exists":
filters[filter_key] = {**filters.get(filter_key, {}), filter_setting["operator"]: True}
elif filter_setting["operator"] in ["$in", "$nin", "$all"]:
filters[filter_key] = {
**filters.get(filter_key, {}),
filter_setting["operator"]: value.split(",") if isinstance(value, str) else value,
}
elif filter_setting["is_timestamp"] == True: # noqa: E712
try:
filters[filter_key] = {
**filters.get(filter_key, {}),
filter_setting["operator"]: self.parse_timestamp(value),
}
except ValueError as e:
msg = f"Error parsing timestamp: {e} - Use the prompt to specify the date in the correct format"
logger.error(msg)
raise ValueError(msg) from e
else:
filters[filter_key] = {**filters.get(filter_key, {}), filter_setting["operator"]: value}
return filters
def run_model(self, **args) -> Data | list[Data]:
"""Run the query to get the data from the AstraDB collection."""
collection = self._build_collection()
sort = {}
# Build filters using the new method
filters = self.build_filter(args, self.tools_params_v2)
# Build the vector search on
if self.use_search_query and args["search_query"] is not None and args["search_query"] != "":
if self.use_vectorize:
sort["$vectorize"] = args["search_query"]
else:
if self.embedding is None:
msg = "Embedding model is not set. Please set the embedding model or use Astra DB Vectorize."
logger.error(msg)
raise ValueError(msg)
embedding_query = self.embedding.embed_query(args["search_query"])
sort["$vector"] = embedding_query
del args["search_query"]
find_options = {
"filter": filters,
"limit": self.number_of_results,
"sort": sort,
}
projection = self.projection_args(self.projection_attributes)
if projection and len(projection) > 0:
find_options["projection"] = projection
try:
results = collection.find(**find_options)
except Exception as e:
msg = f"Error on Astra DB Tool {self.tool_name} request: {e}"
logger.error(msg)
raise ValueError(msg) from e
logger.info(f"Tool {self.tool_name} executed`")
data: list[Data] = [Data(data=doc) for doc in results]
self.status = data
return data
Astra DB CQL tool
This component creates a tool an Agent can use to retrieve data from a DataStax Astra DB CQL table.
Parameters
Name | Type | Description |
---|---|---|
tool_name |
String |
The name of the tool |
tool_description |
String |
The tool description to be passed to the model |
keyspace |
String |
The keyspace name within Astra DB where the data is stored |
table_name |
String |
The name of the table within Astra DB where the data is stored |
token |
SecretString |
Authentication token for accessing Astra DB |
api_endpoint |
String |
API endpoint URL for the Astra DB service |
projection_fields |
String |
Attributes to return, separated by commas (default: "*") |
partition_keys |
List[Dict] |
Field name and description for partition keys |
clustering_keys |
List[Dict] |
Field name and description for clustering keys |
static_filters |
List[Dict] |
Field name and value for static filters |
number_of_results |
Integer |
Number of results to return (default: 5) |
Name | Type | Description |
---|---|---|
tool |
StructuredTool |
Astra DB CQL search tool for use in LangChain |
Component code
astradb_cql.py
import json
import urllib
from datetime import datetime, timezone
from http import HTTPStatus
from typing import Any
import requests
from langchain_core.tools import StructuredTool, Tool
from pydantic import BaseModel, Field, create_model
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.io import DictInput, IntInput, SecretStrInput, StrInput, TableInput
from langflow.logging import logger
from langflow.schema.data import Data
from langflow.schema.table import EditMode
class AstraDBCQLToolComponent(LCToolComponent):
display_name: str = "Astra DB CQL"
description: str = "Create a tool to get transactional data from DataStax Astra DB CQL Table"
documentation: str = "https://docs.langflow.org/Components/components-tools#astra-db-cql-tool"
icon: str = "AstraDB"
inputs = [
StrInput(name="tool_name", display_name="Tool Name", info="The name of the tool.", required=True),
StrInput(
name="tool_description",
display_name="Tool Description",
info="The tool description to be passed to the model.",
required=True,
),
StrInput(
name="keyspace",
display_name="Keyspace",
value="default_keyspace",
info="The keyspace name within Astra DB where the data is stored.",
required=True,
advanced=True,
),
StrInput(
name="table_name",
display_name="Table Name",
info="The name of the table within Astra DB where the data is stored.",
required=True,
),
SecretStrInput(
name="token",
display_name="Astra DB Application Token",
info="Authentication token for accessing Astra DB.",
value="ASTRA_DB_APPLICATION_TOKEN",
required=True,
),
StrInput(
name="api_endpoint",
display_name="API Endpoint",
info="API endpoint URL for the Astra DB service.",
value="ASTRA_DB_API_ENDPOINT",
required=True,
),
StrInput(
name="projection_fields",
display_name="Projection fields",
info="Attributes to return separated by comma.",
required=True,
value="*",
advanced=True,
),
TableInput(
name="tools_params",
display_name="Tools Parameters",
info="Define the structure for the tool parameters. Describe the parameters "
"in a way the LLM can understand how to use them. Add the parameters "
"respecting the table schema (Partition Keys, Clustering Keys and Indexed Fields).",
required=False,
table_schema=[
{
"name": "name",
"display_name": "Name",
"type": "str",
"description": "Name of the field/parameter to be used by the model.",
"default": "field",
"edit_mode": EditMode.INLINE,
},
{
"name": "field_name",
"display_name": "Field Name",
"type": "str",
"description": "Specify the column name to be filtered on the table. "
"Leave empty if the attribute name is the same as the name of the field.",
"default": "",
"edit_mode": EditMode.INLINE,
},
{
"name": "description",
"display_name": "Description",
"type": "str",
"description": "Describe the purpose of the parameter.",
"default": "description of tool parameter",
"edit_mode": EditMode.POPOVER,
},
{
"name": "mandatory",
"display_name": "Is Mandatory",
"type": "boolean",
"edit_mode": EditMode.INLINE,
"description": ("Indicate if the field is mandatory."),
"options": ["True", "False"],
"default": "False",
},
{
"name": "is_timestamp",
"display_name": "Is Timestamp",
"type": "boolean",
"edit_mode": EditMode.INLINE,
"description": ("Indicate if the field is a timestamp."),
"options": ["True", "False"],
"default": "False",
},
{
"name": "operator",
"display_name": "Operator",
"type": "str",
"description": "Set the operator for the field. "
"https://docs.datastax.com/en/astra-db-serverless/api-reference/documents.html#operators",
"default": "$eq",
"options": ["$gt", "$gte", "$lt", "$lte", "$eq", "$ne", "$in", "$nin", "$exists", "$all", "$size"],
"edit_mode": EditMode.INLINE,
},
],
value=[],
),
DictInput(
name="partition_keys",
display_name="DEPRECATED: Partition Keys",
is_list=True,
info="Field name and description to the model",
required=False,
advanced=True,
),
DictInput(
name="clustering_keys",
display_name="DEPRECATED: Clustering Keys",
is_list=True,
info="Field name and description to the model",
required=False,
advanced=True,
),
DictInput(
name="static_filters",
display_name="Static Filters",
is_list=True,
advanced=True,
info="Field name and value. When filled, it will not be generated by the LLM.",
),
IntInput(
name="number_of_results",
display_name="Number of Results",
info="Number of results to return.",
advanced=True,
value=5,
),
]
def parse_timestamp(self, timestamp_str: str) -> str:
"""Parse a timestamp string into Astra DB REST API format.
Args:
timestamp_str (str): Input timestamp string
Returns:
str: Formatted timestamp string in YYYY-MM-DDTHH:MI:SS.000Z format
Raises:
ValueError: If the timestamp cannot be parsed
"""
# Common datetime formats to try
formats = [
"%Y-%m-%d", # 2024-03-21
"%Y-%m-%dT%H:%M:%S", # 2024-03-21T15:30:00
"%Y-%m-%dT%H:%M:%S%z", # 2024-03-21T15:30:00+0000
"%Y-%m-%d %H:%M:%S", # 2024-03-21 15:30:00
"%d/%m/%Y", # 21/03/2024
"%Y/%m/%d", # 2024/03/21
]
for fmt in formats:
try:
# Parse the date string
date_obj = datetime.strptime(timestamp_str, fmt).astimezone()
# If the parsed date has no timezone info, assume UTC
if date_obj.tzinfo is None:
date_obj = date_obj.replace(tzinfo=timezone.utc)
# Convert to UTC and format
utc_date = date_obj.astimezone(timezone.utc)
return utc_date.strftime("%Y-%m-%dT%H:%M:%S.000Z")
except ValueError:
continue
msg = f"Could not parse date: {timestamp_str}"
logger.error(msg)
raise ValueError(msg)
def astra_rest(self, args):
headers = {"Accept": "application/json", "X-Cassandra-Token": f"{self.token}"}
astra_url = f"{self.api_endpoint}/api/rest/v2/keyspaces/{self.keyspace}/{self.table_name}/"
where = {}
for param in self.tools_params:
field_name = param["field_name"] if param["field_name"] else param["name"]
field_value = None
if field_name in self.static_filters:
field_value = self.static_filters[field_name]
elif param["name"] in args:
field_value = args[param["name"]]
if field_value is None:
continue
if param["is_timestamp"] == True: # noqa: E712
try:
field_value = self.parse_timestamp(field_value)
except ValueError as e:
msg = f"Error parsing timestamp: {e} - Use the prompt to specify the date in the correct format"
logger.error(msg)
raise ValueError(msg) from e
if param["operator"] == "$exists":
where[field_name] = {**where.get(field_name, {}), param["operator"]: True}
elif param["operator"] in ["$in", "$nin", "$all"]:
where[field_name] = {
**where.get(field_name, {}),
param["operator"]: field_value.split(",") if isinstance(field_value, str) else field_value,
}
else:
where[field_name] = {**where.get(field_name, {}), param["operator"]: field_value}
url = f"{astra_url}?page-size={self.number_of_results}"
url += f"&where={json.dumps(where)}"
if self.projection_fields != "*":
url += f"&fields={urllib.parse.quote(self.projection_fields.replace(' ', ''))}"
res = requests.request("GET", url=url, headers=headers, timeout=10)
if int(res.status_code) >= HTTPStatus.BAD_REQUEST:
msg = f"Error on Astra DB CQL Tool {self.tool_name} request: {res.text}"
logger.error(msg)
raise ValueError(msg)
try:
res_data = res.json()
return res_data["data"]
except ValueError:
return res.status_code
def create_args_schema(self) -> dict[str, BaseModel]:
args: dict[str, tuple[Any, Field]] = {}
for param in self.tools_params:
field_name = param["field_name"] if param["field_name"] else param["name"]
if field_name not in self.static_filters:
if param["mandatory"]:
args[param["name"]] = (str, Field(description=param["description"]))
else:
args[param["name"]] = (str | None, Field(description=param["description"], default=None))
model = create_model("ToolInput", **args, __base__=BaseModel)
return {"ToolInput": model}
def build_tool(self) -> Tool:
"""Builds a Astra DB CQL Table tool.
Args:
name (str, optional): The name of the tool.
Returns:
Tool: The built AstraDB tool.
"""
schema_dict = self.create_args_schema()
return StructuredTool.from_function(
name=self.tool_name,
args_schema=schema_dict["ToolInput"],
description=self.tool_description,
func=self.run_model,
return_direct=False,
)
def projection_args(self, input_str: str) -> dict:
elements = input_str.split(",")
result = {}
for element in elements:
if element.startswith("!"):
result[element[1:]] = False
else:
result[element] = True
return result
def run_model(self, **args) -> Data | list[Data]:
results = self.astra_rest(args)
data: list[Data] = []
if isinstance(results, list):
data = [Data(data=doc) for doc in results]
else:
self.status = results
return []
self.status = data
return data
Bing Search API
This component allows you to call the Bing Search API.
Parameters
Name | Type | Description |
---|---|---|
bing_subscription_key |
SecretString |
Bing API subscription key |
input_value |
String |
Search query input |
bing_search_url |
String |
Custom Bing Search URL (optional) |
k |
Integer |
Number of search results to return |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Bing Search tool for use in LangChain |
Component code
bing_search_api.py
404: Not Found
Calculator Tool
This component allows you to evaluate basic arithmetic expressions. It supports addition, subtraction, multiplication, division, and exponentiation. The tool uses a secure evaluation method that prevents the execution of arbitrary Python code.
Parameters
Name | Type | Description |
---|---|---|
expression |
String |
The arithmetic expression to evaluate (for example, 4*4*(33/22)+12-20). |
Name | Type | Description |
---|---|---|
result |
Tool |
Calculator tool for use in LangChain. |
Component code
calculator.py
import ast
import operator
from langchain.tools import StructuredTool
from langchain_core.tools import ToolException
from loguru import logger
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs.inputs import MessageTextInput
from langflow.schema.data import Data
class CalculatorToolComponent(LCToolComponent):
display_name = "Calculator [DEPRECATED]"
description = "Perform basic arithmetic operations on a given expression."
icon = "calculator"
name = "CalculatorTool"
legacy = True
inputs = [
MessageTextInput(
name="expression",
display_name="Expression",
info="The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').",
),
]
class CalculatorToolSchema(BaseModel):
expression: str = Field(..., description="The arithmetic expression to evaluate.")
def run_model(self) -> list[Data]:
return self._evaluate_expression(self.expression)
def build_tool(self) -> Tool:
return StructuredTool.from_function(
name="calculator",
description="Evaluate basic arithmetic expressions. Input should be a string containing the expression.",
func=self._eval_expr_with_error,
args_schema=self.CalculatorToolSchema,
)
def _eval_expr(self, node):
if isinstance(node, ast.Num):
return node.n
if isinstance(node, ast.BinOp):
left_val = self._eval_expr(node.left)
right_val = self._eval_expr(node.right)
return self.operators[type(node.op)](left_val, right_val)
if isinstance(node, ast.UnaryOp):
operand_val = self._eval_expr(node.operand)
return self.operators[type(node.op)](operand_val)
if isinstance(node, ast.Call):
msg = (
"Function calls like sqrt(), sin(), cos() etc. are not supported. "
"Only basic arithmetic operations (+, -, *, /, **) are allowed."
)
raise TypeError(msg)
msg = f"Unsupported operation or expression type: {type(node).__name__}"
raise TypeError(msg)
def _eval_expr_with_error(self, expression: str) -> list[Data]:
try:
return self._evaluate_expression(expression)
except Exception as e:
raise ToolException(str(e)) from e
def _evaluate_expression(self, expression: str) -> list[Data]:
try:
# Parse the expression and evaluate it
tree = ast.parse(expression, mode="eval")
result = self._eval_expr(tree.body)
# Format the result to a reasonable number of decimal places
formatted_result = f"{result:.6f}".rstrip("0").rstrip(".")
self.status = formatted_result
return [Data(data={"result": formatted_result})]
except (SyntaxError, TypeError, KeyError) as e:
error_message = f"Invalid expression: {e}"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]
except ZeroDivisionError:
error_message = "Error: Division by zero"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error evaluating expression")
error_message = f"Error: {e}"
self.status = error_message
return [Data(data={"error": error_message, "input": expression})]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
}
DuckDuckGo search
This component performs web searches using the DuckDuckGo search engine with result-limiting capabilities.
Parameters
Name | Display Name | Info |
---|---|---|
input_value |
Search Query |
The search query to execute with DuckDuckGo. |
max_results |
Max Results |
The maximum number of search results to return. Default: |
max_snippet_length |
Max Snippet Length |
The maximum length of each result snippet. Default: |
Name | Display Name | Info |
---|---|---|
data |
Data |
List of search results as Data objects containing snippets and full content. |
text |
Text |
Search results formatted as a single text string. |
Component code
duck_duck_go_search_run.py
404: Not Found
Exa Search
This component provides an Exa Search toolkit for search and content retrieval.
Parameters
Name | Display Name | Info |
---|---|---|
metaphor_api_key |
Exa Search API Key |
API key for Exa Search, entered as a password. |
use_autoprompt |
Use Autoprompt |
Whether to use the autoprompt feature. (default: true) |
search_num_results |
Search Number of Results |
Number of results to return for search. (default: 5) |
similar_num_results |
Similar Number of Results |
Number of similar results to return. (default: 5) |
Name | Display Name | Info |
---|---|---|
tools |
Tools |
List of search tools provided by the toolkit. |
Component code
exa_search.py
404: Not Found
Glean Search API
This component allows you to call the Glean Search API.
Parameters
Name | Type | Description |
---|---|---|
glean_api_url |
String |
URL of the Glean API |
glean_access_token |
SecretString |
Access token for Glean API authentication |
query |
String |
Search query input |
page_size |
Integer |
Number of results per page (default: 10) |
request_options |
Dict |
Additional options for the API request (optional) |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Glean Search tool for use in LangChain |
Component code
glean_search_api.py
404: Not Found
Google search API
This component allows you to call the Google Search API.
Parameters
Name | Type | Description |
---|---|---|
google_api_key |
SecretString |
Google API key for authentication |
google_cse_id |
SecretString |
Google Custom Search Engine ID |
input_value |
String |
Search query input |
k |
Integer |
Number of search results to return |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Google Search tool for use in LangChain |
Component code
google_search_api.py
404: Not Found
Google serper API
This component allows you to call the Serper.dev Google Search API.
Parameters
Name | Type | Description |
---|---|---|
serper_api_key |
SecretString |
API key for Serper.dev authentication |
input_value |
String |
Search query input |
k |
Integer |
Number of search results to return |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Google Serper search tool for use in LangChain |
Component code
google_serper_api.py
404: Not Found
MCP server
This component connects to a Model Context Protocol (MCP) server and exposes the MCP server’s tools as tools.
To use the MCP server component with an agent component, follow these steps:
-
Add the MCP server component to your workflow.
-
In the MCP server component, in the MCP Command field, enter the command to start your MCP server. uvx is included with DataStax Langflow.
npx
server commands are not available in DataStax Langflow. For example, to start a Fetch server, the command is:uvx mcp-server-fetch
-
Click
to get the server’s list of Tools. -
In the Tool field, select the server tool you want the component to use. The available fields change based on the selected tool. For information on the parameters, see the MCP server’s documentation.
-
In the MCP server component, enable Tool mode. Connect the MCP server component’s Toolset port to an Agent component’s Tools port. The flow looks similar to this:
-
Open the Playground. Ask the agent to summarize recent tech news. The agent calls the MCP server function fetch and returns the summary. This confirms the MCP server is connected, and its tools are being used in Langflow.
SSE mode is not available in DataStax Langflow. If you want to use SSE mode, use Langflow OSS. |
MCP Tools (stdio)
This component is deprecated as of Langflow version 1.3. Instead, use the MCP server component. |
MCP Tools (SSE)
This component is deprecated as of Langflow version 1.3. Instead, use the MCP server component. |
Python code structured tool
This component creates a structured tool from Python code using a dataclass.
The component dynamically updates its configuration based on the provided Python code, allowing for custom function arguments and descriptions.
Parameters
Name | Type | Description |
---|---|---|
tool_code |
String |
Python code for the tool’s dataclass |
tool_name |
String |
Name of the tool |
tool_description |
String |
Description of the tool |
return_direct |
Boolean |
Whether to return the function output directly |
tool_function |
String |
Selected function for the tool |
global_variables |
Dict |
Global variables or data for the tool |
Name | Type | Description |
---|---|---|
result_tool |
Tool |
Structured tool created from the Python code |
Component code
python_code_structured_tool.py
import ast
import json
from typing import Any
from langchain.agents import Tool
from langchain_core.tools import StructuredTool
from loguru import logger
from pydantic.v1 import Field, create_model
from pydantic.v1.fields import Undefined
from typing_extensions import override
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.inputs.inputs import (
BoolInput,
DropdownInput,
FieldTypes,
HandleInput,
MessageTextInput,
MultilineInput,
)
from langflow.io import Output
from langflow.schema.data import Data
from langflow.schema.dotdict import dotdict
class PythonCodeStructuredTool(LCToolComponent):
DEFAULT_KEYS = [
"code",
"_type",
"text_key",
"tool_code",
"tool_name",
"tool_description",
"return_direct",
"tool_function",
"global_variables",
"_classes",
"_functions",
]
display_name = "Python Code Structured"
description = "structuredtool dataclass code to tool"
documentation = "https://python.langchain.com/docs/modules/tools/custom_tools/#structuredtool-dataclass"
name = "PythonCodeStructuredTool"
icon = "Python"
field_order = ["name", "description", "tool_code", "return_direct", "tool_function"]
legacy: bool = True
inputs = [
MultilineInput(
name="tool_code",
display_name="Tool Code",
info="Enter the dataclass code.",
placeholder="def my_function(args):\n pass",
required=True,
real_time_refresh=True,
refresh_button=True,
),
MessageTextInput(
name="tool_name",
display_name="Tool Name",
info="Enter the name of the tool.",
required=True,
),
MessageTextInput(
name="tool_description",
display_name="Description",
info="Enter the description of the tool.",
required=True,
),
BoolInput(
name="return_direct",
display_name="Return Directly",
info="Should the tool return the function output directly?",
),
DropdownInput(
name="tool_function",
display_name="Tool Function",
info="Select the function for additional expressions.",
options=[],
required=True,
real_time_refresh=True,
refresh_button=True,
),
HandleInput(
name="global_variables",
display_name="Global Variables",
info="Enter the global variables or Create Data Component.",
input_types=["Data"],
field_type=FieldTypes.DICT,
is_list=True,
),
MessageTextInput(name="_classes", display_name="Classes", advanced=True),
MessageTextInput(name="_functions", display_name="Functions", advanced=True),
]
outputs = [
Output(display_name="Tool", name="result_tool", method="build_tool"),
]
@override
async def update_build_config(
self, build_config: dotdict, field_value: Any, field_name: str | None = None
) -> dotdict:
if field_name is None:
return build_config
if field_name not in {"tool_code", "tool_function"}:
return build_config
try:
named_functions = {}
[classes, functions] = self._parse_code(build_config["tool_code"]["value"])
existing_fields = {}
if len(build_config) > len(self.DEFAULT_KEYS):
for key in build_config.copy():
if key not in self.DEFAULT_KEYS:
existing_fields[key] = build_config.pop(key)
names = []
for func in functions:
named_functions[func["name"]] = func
names.append(func["name"])
for arg in func["args"]:
field_name = f"{func['name']}|{arg['name']}"
if field_name in existing_fields:
build_config[field_name] = existing_fields[field_name]
continue
field = MessageTextInput(
display_name=f"{arg['name']}: Description",
name=field_name,
info=f"Enter the description for {arg['name']}",
required=True,
)
build_config[field_name] = field.to_dict()
build_config["_functions"]["value"] = json.dumps(named_functions)
build_config["_classes"]["value"] = json.dumps(classes)
build_config["tool_function"]["options"] = names
except Exception as e: # noqa: BLE001
self.status = f"Failed to extract names: {e}"
logger.opt(exception=True).debug(self.status)
build_config["tool_function"]["options"] = ["Failed to parse", str(e)]
return build_config
async def build_tool(self) -> Tool:
local_namespace = {} # type: ignore[var-annotated]
modules = self._find_imports(self.tool_code)
import_code = ""
for module in modules["imports"]:
import_code += f"global {module}\nimport {module}\n"
for from_module in modules["from_imports"]:
for alias in from_module.names:
import_code += f"global {alias.name}\n"
import_code += (
f"from {from_module.module} import {', '.join([alias.name for alias in from_module.names])}\n"
)
exec(import_code, globals())
exec(self.tool_code, globals(), local_namespace)
class PythonCodeToolFunc:
params: dict = {}
def run(**kwargs):
for key, arg in kwargs.items():
if key not in PythonCodeToolFunc.params:
PythonCodeToolFunc.params[key] = arg
return local_namespace[self.tool_function](**PythonCodeToolFunc.params)
globals_ = globals()
local = {}
local[self.tool_function] = PythonCodeToolFunc
globals_.update(local)
if isinstance(self.global_variables, list):
for data in self.global_variables:
if isinstance(data, Data):
globals_.update(data.data)
elif isinstance(self.global_variables, dict):
globals_.update(self.global_variables)
classes = json.loads(self._attributes["_classes"])
for class_dict in classes:
exec("\n".join(class_dict["code"]), globals_)
named_functions = json.loads(self._attributes["_functions"])
schema_fields = {}
for attr in self._attributes:
if attr in self.DEFAULT_KEYS:
continue
func_name = attr.split("|")[0]
field_name = attr.split("|")[1]
func_arg = self._find_arg(named_functions, func_name, field_name)
if func_arg is None:
msg = f"Failed to find arg: {field_name}"
raise ValueError(msg)
field_annotation = func_arg["annotation"]
field_description = self._get_value(self._attributes[attr], str)
if field_annotation:
exec(f"temp_annotation_type = {field_annotation}", globals_)
schema_annotation = globals_["temp_annotation_type"]
else:
schema_annotation = Any
schema_fields[field_name] = (
schema_annotation,
Field(
default=func_arg.get("default", Undefined),
description=field_description,
),
)
if "temp_annotation_type" in globals_:
globals_.pop("temp_annotation_type")
python_code_tool_schema = None
if schema_fields:
python_code_tool_schema = create_model("PythonCodeToolSchema", **schema_fields)
return StructuredTool.from_function(
func=local[self.tool_function].run,
args_schema=python_code_tool_schema,
name=self.tool_name,
description=self.tool_description,
return_direct=self.return_direct,
)
async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):
"""This function is called after the code validation is done."""
frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)
frontend_node["template"] = await self.update_build_config(
frontend_node["template"],
frontend_node["template"]["tool_code"]["value"],
"tool_code",
)
frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)
for key in frontend_node["template"]:
if key in self.DEFAULT_KEYS:
continue
frontend_node["template"] = await self.update_build_config(
frontend_node["template"], frontend_node["template"][key]["value"], key
)
frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)
return frontend_node
def _parse_code(self, code: str) -> tuple[list[dict], list[dict]]:
parsed_code = ast.parse(code)
lines = code.split("\n")
classes = []
functions = []
for node in parsed_code.body:
if isinstance(node, ast.ClassDef):
class_lines = lines[node.lineno - 1 : node.end_lineno]
class_lines[-1] = class_lines[-1][: node.end_col_offset]
class_lines[0] = class_lines[0][node.col_offset :]
classes.append(
{
"name": node.name,
"code": class_lines,
}
)
continue
if not isinstance(node, ast.FunctionDef):
continue
func = {"name": node.name, "args": []}
for arg in node.args.args:
if arg.lineno != arg.end_lineno:
msg = "Multiline arguments are not supported"
raise ValueError(msg)
func_arg = {
"name": arg.arg,
"annotation": None,
}
for default in node.args.defaults:
if (
arg.lineno > default.lineno
or arg.col_offset > default.col_offset
or (
arg.end_lineno is not None
and default.end_lineno is not None
and arg.end_lineno < default.end_lineno
)
or (
arg.end_col_offset is not None
and default.end_col_offset is not None
and arg.end_col_offset < default.end_col_offset
)
):
continue
if isinstance(default, ast.Name):
func_arg["default"] = default.id
elif isinstance(default, ast.Constant):
func_arg["default"] = default.value
if arg.annotation:
annotation_line = lines[arg.annotation.lineno - 1]
annotation_line = annotation_line[: arg.annotation.end_col_offset]
annotation_line = annotation_line[arg.annotation.col_offset :]
func_arg["annotation"] = annotation_line
if isinstance(func_arg["annotation"], str) and func_arg["annotation"].count("=") > 0:
func_arg["annotation"] = "=".join(func_arg["annotation"].split("=")[:-1]).strip()
if isinstance(func["args"], list):
func["args"].append(func_arg)
functions.append(func)
return classes, functions
def _find_imports(self, code: str) -> dotdict:
imports: list[str] = []
from_imports = []
parsed_code = ast.parse(code)
for node in parsed_code.body:
if isinstance(node, ast.Import):
imports.extend(alias.name for alias in node.names)
elif isinstance(node, ast.ImportFrom):
from_imports.append(node)
return dotdict({"imports": imports, "from_imports": from_imports})
def _get_value(self, value: Any, annotation: Any) -> Any:
return value if isinstance(value, annotation) else value["value"]
def _find_arg(self, named_functions: dict, func_name: str, arg_name: str) -> dict | None:
for arg in named_functions[func_name]["args"]:
if arg["name"] == arg_name:
return arg
return None
Python REPL Tool
This component creates a Python REPL (Read-Eval-Print Loop) tool for executing Python code.
Parameters
Name | Type | Description |
---|---|---|
name |
String |
The name of the tool (default: "python_repl") |
description |
String |
A description of the tool’s functionality |
global_imports |
List[String] |
List of modules to import globally (default: ["math"]) |
Name | Type | Description |
---|---|---|
tool |
Tool |
Python REPL tool for use in LangChain |
Component code
python_repl.py
import importlib
from langchain.tools import StructuredTool
from langchain_core.tools import ToolException
from langchain_experimental.utilities import PythonREPL
from loguru import logger
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs.inputs import StrInput
from langflow.schema.data import Data
class PythonREPLToolComponent(LCToolComponent):
display_name = "Python REPL [DEPRECATED]"
description = "A tool for running Python code in a REPL environment."
name = "PythonREPLTool"
icon = "Python"
legacy = True
inputs = [
StrInput(
name="name",
display_name="Tool Name",
info="The name of the tool.",
value="python_repl",
),
StrInput(
name="description",
display_name="Tool Description",
info="A description of the tool.",
value="A Python shell. Use this to execute python commands. "
"Input should be a valid python command. "
"If you want to see the output of a value, you should print it out with `print(...)`.",
),
StrInput(
name="global_imports",
display_name="Global Imports",
info="A comma-separated list of modules to import globally, e.g. 'math,numpy'.",
value="math",
),
StrInput(
name="code",
display_name="Python Code",
info="The Python code to execute.",
value="print('Hello, World!')",
),
]
class PythonREPLSchema(BaseModel):
code: str = Field(..., description="The Python code to execute.")
def get_globals(self, global_imports: str | list[str]) -> dict:
global_dict = {}
if isinstance(global_imports, str):
modules = [module.strip() for module in global_imports.split(",")]
elif isinstance(global_imports, list):
modules = global_imports
else:
msg = "global_imports must be either a string or a list"
raise TypeError(msg)
for module in modules:
try:
imported_module = importlib.import_module(module)
global_dict[imported_module.__name__] = imported_module
except ImportError as e:
msg = f"Could not import module {module}"
raise ImportError(msg) from e
return global_dict
def build_tool(self) -> Tool:
globals_ = self.get_globals(self.global_imports)
python_repl = PythonREPL(_globals=globals_)
def run_python_code(code: str) -> str:
try:
return python_repl.run(code)
except Exception as e:
logger.opt(exception=True).debug("Error running Python code")
raise ToolException(str(e)) from e
tool = StructuredTool.from_function(
name=self.name,
description=self.description,
func=run_python_code,
args_schema=self.PythonREPLSchema,
)
self.status = f"Python REPL Tool created with global imports: {self.global_imports}"
return tool
def run_model(self) -> list[Data]:
tool = self.build_tool()
result = tool.run(self.code)
return [Data(data={"result": result})]
Retriever Tool
This component creates a tool for interacting with a retriever in LangChain.
Parameters
Name | Type | Description |
---|---|---|
retriever |
BaseRetriever |
The retriever to interact with |
name |
String |
The name of the tool |
description |
String |
A description of the tool’s functionality |
Name | Type | Description |
---|---|---|
tool |
Tool |
Retriever tool for use in LangChain |
Component code
retriever.py
404: Not Found
SearXNG Search Tool
This component creates a tool for searching using SearXNG, a metasearch engine.
Parameters
Name | Type | Description |
---|---|---|
url |
String |
The URL of the SearXNG instance |
max_results |
Integer |
Maximum number of results to return |
categories |
List[String] |
Categories to search in |
language |
String |
Language for the search results |
Name | Type | Description |
---|---|---|
result_tool |
Tool |
SearXNG search tool for use in LangChain |
Component code
searxng.py
import json
from collections.abc import Sequence
from typing import Any
import requests
from langchain.agents import Tool
from langchain_core.tools import StructuredTool
from loguru import logger
from pydantic.v1 import Field, create_model
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.inputs.inputs import DropdownInput, IntInput, MessageTextInput, MultiselectInput
from langflow.io import Output
from langflow.schema.dotdict import dotdict
class SearXNGToolComponent(LCToolComponent):
search_headers: dict = {}
display_name = "SearXNG Search"
description = "A component that searches for tools using SearXNG."
name = "SearXNGTool"
legacy: bool = True
inputs = [
MessageTextInput(
name="url",
display_name="URL",
value="http://localhost",
required=True,
refresh_button=True,
),
IntInput(
name="max_results",
display_name="Max Results",
value=10,
required=True,
),
MultiselectInput(
name="categories",
display_name="Categories",
options=[],
value=[],
),
DropdownInput(
name="language",
display_name="Language",
options=[],
),
]
outputs = [
Output(display_name="Tool", name="result_tool", method="build_tool"),
]
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:
if field_name is None:
return build_config
if field_name != "url":
return build_config
try:
url = f"{field_value}/config"
response = requests.get(url=url, headers=self.search_headers.copy(), timeout=10)
data = None
if response.headers.get("Content-Encoding") == "zstd":
data = json.loads(response.content)
else:
data = response.json()
build_config["categories"]["options"] = data["categories"].copy()
for selected_category in build_config["categories"]["value"]:
if selected_category not in build_config["categories"]["options"]:
build_config["categories"]["value"].remove(selected_category)
languages = list(data["locales"])
build_config["language"]["options"] = languages.copy()
except Exception as e: # noqa: BLE001
self.status = f"Failed to extract names: {e}"
logger.opt(exception=True).debug(self.status)
build_config["categories"]["options"] = ["Failed to parse", str(e)]
return build_config
def build_tool(self) -> Tool:
class SearxSearch:
_url: str = ""
_categories: list[str] = []
_language: str = ""
_headers: dict = {}
_max_results: int = 10
@staticmethod
def search(query: str, categories: Sequence[str] = ()) -> list:
if not SearxSearch._categories and not categories:
msg = "No categories provided."
raise ValueError(msg)
all_categories = SearxSearch._categories + list(set(categories) - set(SearxSearch._categories))
try:
url = f"{SearxSearch._url}/"
headers = SearxSearch._headers.copy()
response = requests.get(
url=url,
headers=headers,
params={
"q": query,
"categories": ",".join(all_categories),
"language": SearxSearch._language,
"format": "json",
},
timeout=10,
).json()
num_results = min(SearxSearch._max_results, len(response["results"]))
return [response["results"][i] for i in range(num_results)]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error running SearXNG Search")
return [f"Failed to search: {e}"]
SearxSearch._url = self.url
SearxSearch._categories = self.categories.copy()
SearxSearch._language = self.language
SearxSearch._headers = self.search_headers.copy()
SearxSearch._max_results = self.max_results
globals_ = globals()
local = {}
local["SearxSearch"] = SearxSearch
globals_.update(local)
schema_fields = {
"query": (str, Field(..., description="The query to search for.")),
"categories": (
list[str],
Field(default=[], description="The categories to search in."),
),
}
searx_search_schema = create_model("SearxSearchSchema", **schema_fields)
return StructuredTool.from_function(
func=local["SearxSearch"].search,
args_schema=searx_search_schema,
name="searxng_search_tool",
description="A tool that searches for tools using SearXNG.\nThe available categories are: "
+ ", ".join(self.categories),
)
Search API
This component is in Legacy, which means it is no longer in active development as of Langflow version 1.3. |
This component calls the searchapi.io
API. It can be used to search the web for information.
For more information, see the SearchAPI documentation.
Parameters
Name | Display Name | Info |
---|---|---|
engine |
Engine |
The search engine to use (default: "google") |
api_key |
SearchAPI API Key |
The API key for authenticating with SearchAPI |
input_value |
Input |
The search query or input for the API call |
search_params |
Search parameters |
Additional parameters for customizing the search |
Name | Display Name | Info |
---|---|---|
data |
Search Results |
List of Data objects containing search results |
tool |
Search API Tool |
A Tool object for use in LangChain workflows |
Component code
search_api.py
from typing import Any
from langchain.tools import StructuredTool
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs.inputs import DictInput, IntInput, MessageTextInput, MultilineInput, SecretStrInput
from langflow.schema.data import Data
class SearchAPIComponent(LCToolComponent):
display_name: str = "Search API [DEPRECATED]"
description: str = "Call the searchapi.io API with result limiting"
name = "SearchAPI"
documentation: str = "https://www.searchapi.io/docs/google"
icon = "SearchAPI"
legacy = True
inputs = [
MessageTextInput(name="engine", display_name="Engine", value="google"),
SecretStrInput(name="api_key", display_name="SearchAPI API Key", required=True),
MultilineInput(
name="input_value",
display_name="Input",
),
DictInput(name="search_params", display_name="Search parameters", advanced=True, is_list=True),
IntInput(name="max_results", display_name="Max Results", value=5, advanced=True),
IntInput(name="max_snippet_length", display_name="Max Snippet Length", value=100, advanced=True),
]
class SearchAPISchema(BaseModel):
query: str = Field(..., description="The search query")
params: dict[str, Any] = Field(default_factory=dict, description="Additional search parameters")
max_results: int = Field(5, description="Maximum number of results to return")
max_snippet_length: int = Field(100, description="Maximum length of each result snippet")
def _build_wrapper(self):
return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)
def build_tool(self) -> Tool:
wrapper = self._build_wrapper()
def search_func(
query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100
) -> list[dict[str, Any]]:
params = params or {}
full_results = wrapper.results(query=query, **params)
organic_results = full_results.get("organic_results", [])[:max_results]
limited_results = []
for result in organic_results:
limited_result = {
"title": result.get("title", "")[:max_snippet_length],
"link": result.get("link", ""),
"snippet": result.get("snippet", "")[:max_snippet_length],
}
limited_results.append(limited_result)
return limited_results
tool = StructuredTool.from_function(
name="search_api",
description="Search for recent results using searchapi.io with result limiting",
func=search_func,
args_schema=self.SearchAPISchema,
)
self.status = f"Search API Tool created with engine: {self.engine}"
return tool
def run_model(self) -> list[Data]:
tool = self.build_tool()
results = tool.run(
{
"query": self.input_value,
"params": self.search_params or {},
"max_results": self.max_results,
"max_snippet_length": self.max_snippet_length,
}
)
data_list = [Data(data=result, text=result.get("snippet", "")) for result in results]
self.status = data_list
return data_list
Serp Search API
This component creates a tool for searching using the Serp API.
Parameters
Name | Type | Description |
---|---|---|
serpapi_api_key |
SecretString |
API key for Serp API authentication |
input_value |
String |
Search query input |
search_params |
Dict |
Additional search parameters (optional) |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Serp API search tool for use in LangChain |
Component code
serp_api.py
from typing import Any
from langchain.tools import StructuredTool
from langchain_community.utilities.serpapi import SerpAPIWrapper
from langchain_core.tools import ToolException
from loguru import logger
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs.inputs import DictInput, IntInput, MultilineInput, SecretStrInput
from langflow.schema.data import Data
class SerpAPISchema(BaseModel):
"""Schema for SerpAPI search parameters."""
query: str = Field(..., description="The search query")
params: dict[str, Any] | None = Field(
default={
"engine": "google",
"google_domain": "google.com",
"gl": "us",
"hl": "en",
},
description="Additional search parameters",
)
max_results: int = Field(5, description="Maximum number of results to return")
max_snippet_length: int = Field(100, description="Maximum length of each result snippet")
class SerpAPIComponent(LCToolComponent):
display_name = "Serp Search API [DEPRECATED]"
description = "Call Serp Search API with result limiting"
name = "SerpAPI"
icon = "SerpSearch"
legacy = True
inputs = [
SecretStrInput(name="serpapi_api_key", display_name="SerpAPI API Key", required=True),
MultilineInput(
name="input_value",
display_name="Input",
),
DictInput(name="search_params", display_name="Parameters", advanced=True, is_list=True),
IntInput(name="max_results", display_name="Max Results", value=5, advanced=True),
IntInput(name="max_snippet_length", display_name="Max Snippet Length", value=100, advanced=True),
]
def _build_wrapper(self, params: dict[str, Any] | None = None) -> SerpAPIWrapper:
"""Build a SerpAPIWrapper with the provided parameters."""
params = params or {}
if params:
return SerpAPIWrapper(
serpapi_api_key=self.serpapi_api_key,
params=params,
)
return SerpAPIWrapper(serpapi_api_key=self.serpapi_api_key)
def build_tool(self) -> Tool:
wrapper = self._build_wrapper(self.search_params)
def search_func(
query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100
) -> list[dict[str, Any]]:
try:
local_wrapper = wrapper
if params:
local_wrapper = self._build_wrapper(params)
full_results = local_wrapper.results(query)
organic_results = full_results.get("organic_results", [])[:max_results]
limited_results = []
for result in organic_results:
limited_result = {
"title": result.get("title", "")[:max_snippet_length],
"link": result.get("link", ""),
"snippet": result.get("snippet", "")[:max_snippet_length],
}
limited_results.append(limited_result)
except Exception as e:
error_message = f"Error in SerpAPI search: {e!s}"
logger.debug(error_message)
raise ToolException(error_message) from e
return limited_results
tool = StructuredTool.from_function(
name="serp_search_api",
description="Search for recent results using SerpAPI with result limiting",
func=search_func,
args_schema=SerpAPISchema,
)
self.status = "SerpAPI Tool created"
return tool
def run_model(self) -> list[Data]:
tool = self.build_tool()
try:
results = tool.run(
{
"query": self.input_value,
"params": self.search_params or {},
"max_results": self.max_results,
"max_snippet_length": self.max_snippet_length,
}
)
data_list = [Data(data=result, text=result.get("snippet", "")) for result in results]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error running SerpAPI")
self.status = f"Error: {e}"
return [Data(data={"error": str(e)}, text=str(e))]
self.status = data_list # type: ignore[assignment]
return data_list
Tavily search API
This component creates a tool for performing web searches using the Tavily API.
Parameters
Name | Type | Description | Required | Default |
---|---|---|---|---|
api_key |
SecretString |
Tavily API Key |
Yes |
- |
query |
String |
Search query |
Yes |
- |
search_depth |
Enum |
Depth of search (BASIC/ADVANCED) |
No |
BASIC |
topic |
Enum |
Search category (GENERAL/NEWS) |
No |
GENERAL |
max_results |
Integer |
Maximum number of results |
No |
5 |
include_images |
Boolean |
Include related images |
No |
False |
include_answer |
Boolean |
Include short answer |
No |
False |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of search results |
tool |
Tool |
Tavily search tool for use in LangChain |
Component code
tavily_search.py
import httpx
from loguru import logger
from langflow.custom.custom_component.component import Component
from langflow.inputs.inputs import BoolInput, DropdownInput, IntInput, MessageTextInput, SecretStrInput
from langflow.schema.data import Data
from langflow.schema.dataframe import DataFrame
from langflow.template.field.base import Output
class TavilySearchComponent(Component):
display_name = "Tavily Search API"
description = """**Tavily Search** is a search engine optimized for LLMs and RAG, \
aimed at efficient, quick, and persistent search results."""
icon = "TavilyIcon"
inputs = [
SecretStrInput(
name="api_key",
display_name="Tavily API Key",
required=True,
info="Your Tavily API Key.",
),
MessageTextInput(
name="query",
display_name="Search Query",
info="The search query you want to execute with Tavily.",
tool_mode=True,
),
DropdownInput(
name="search_depth",
display_name="Search Depth",
info="The depth of the search.",
options=["basic", "advanced"],
value="advanced",
advanced=True,
),
IntInput(
name="chunks_per_source",
display_name="Chunks Per Source",
info=("The number of content chunks to retrieve from each source (1-3). Only works with advanced search."),
value=3,
advanced=True,
),
DropdownInput(
name="topic",
display_name="Search Topic",
info="The category of the search.",
options=["general", "news"],
value="general",
advanced=True,
),
IntInput(
name="days",
display_name="Days",
info="Number of days back from current date to include. Only available with news topic.",
value=7,
advanced=True,
),
IntInput(
name="max_results",
display_name="Max Results",
info="The maximum number of search results to return.",
value=5,
advanced=True,
),
BoolInput(
name="include_answer",
display_name="Include Answer",
info="Include a short answer to original query.",
value=True,
advanced=True,
),
DropdownInput(
name="time_range",
display_name="Time Range",
info="The time range back from the current date to filter results.",
options=["day", "week", "month", "year"],
value=None, # Default to None to make it optional
advanced=True,
),
BoolInput(
name="include_images",
display_name="Include Images",
info="Include a list of query-related images in the response.",
value=True,
advanced=True,
),
MessageTextInput(
name="include_domains",
display_name="Include Domains",
info="Comma-separated list of domains to include in the search results.",
advanced=True,
),
MessageTextInput(
name="exclude_domains",
display_name="Exclude Domains",
info="Comma-separated list of domains to exclude from the search results.",
advanced=True,
),
BoolInput(
name="include_raw_content",
display_name="Include Raw Content",
info="Include the cleaned and parsed HTML content of each search result.",
value=False,
advanced=True,
),
]
outputs = [
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
]
def fetch_content(self) -> list[Data]:
try:
# Only process domains if they're provided
include_domains = None
exclude_domains = None
if self.include_domains:
include_domains = [domain.strip() for domain in self.include_domains.split(",") if domain.strip()]
if self.exclude_domains:
exclude_domains = [domain.strip() for domain in self.exclude_domains.split(",") if domain.strip()]
url = "https://api.tavily.com/search"
headers = {
"content-type": "application/json",
"accept": "application/json",
}
payload = {
"api_key": self.api_key,
"query": self.query,
"search_depth": self.search_depth,
"topic": self.topic,
"max_results": self.max_results,
"include_images": self.include_images,
"include_answer": self.include_answer,
"include_raw_content": self.include_raw_content,
"days": self.days,
"time_range": self.time_range,
}
# Only add domains to payload if they exist and have values
if include_domains:
payload["include_domains"] = include_domains
if exclude_domains:
payload["exclude_domains"] = exclude_domains
# Add conditional parameters only if they should be included
if self.search_depth == "advanced" and self.chunks_per_source:
payload["chunks_per_source"] = self.chunks_per_source
if self.topic == "news" and self.days:
payload["days"] = int(self.days) # Ensure days is an integer
# Add time_range if it's set
if hasattr(self, "time_range") and self.time_range:
payload["time_range"] = self.time_range
# Add timeout handling
with httpx.Client(timeout=90.0) as client:
response = client.post(url, json=payload, headers=headers)
response.raise_for_status()
search_results = response.json()
data_results = []
if self.include_answer and search_results.get("answer"):
data_results.append(Data(text=search_results["answer"]))
for result in search_results.get("results", []):
content = result.get("content", "")
result_data = {
"title": result.get("title"),
"url": result.get("url"),
"content": content,
"score": result.get("score"),
}
if self.include_raw_content:
result_data["raw_content"] = result.get("raw_content")
data_results.append(Data(text=content, data=result_data))
if self.include_images and search_results.get("images"):
data_results.append(Data(text="Images found", data={"images": search_results["images"]}))
except httpx.TimeoutException:
error_message = "Request timed out (90s). Please try again or adjust parameters."
logger.error(error_message)
return [Data(text=error_message, data={"error": error_message})]
except httpx.HTTPStatusError as exc:
error_message = f"HTTP error occurred: {exc.response.status_code} - {exc.response.text}"
logger.error(error_message)
return [Data(text=error_message, data={"error": error_message})]
except httpx.RequestError as exc:
error_message = f"Request error occurred: {exc}"
logger.error(error_message)
return [Data(text=error_message, data={"error": error_message})]
except ValueError as exc:
error_message = f"Invalid response format: {exc}"
logger.error(error_message)
return [Data(text=error_message, data={"error": error_message})]
else:
self.status = data_results
return data_results
def fetch_content_dataframe(self) -> DataFrame:
data = self.fetch_content()
return DataFrame(data)
Wikidata
This component is in Legacy, which means it is no longer in active development as of Langflow version 1.3. |
This component performs a search using the Wikidata API.
Parameters
Name | Display Name | Info |
---|---|---|
query |
Query |
The text query for similarity search on Wikidata. |
Name | Display Name | Info |
---|---|---|
data |
Data |
The search results from Wikidata API as a list of Data objects. |
text |
Message |
The search results formatted as a text message. |
Component code
wikidata.py
404: Not Found
Wikipedia API
This component is in Legacy, which means it is no longer in active development as of Langflow version 1.3. |
This component creates a tool for searching and retrieving information from Wikipedia.
Parameters
Name | Type | Description |
---|---|---|
input_value |
String |
Search query input |
lang |
String |
Language code for Wikipedia (default: "en") |
k |
Integer |
Number of results to return |
load_all_available_meta |
Boolean |
Whether to load all available metadata (advanced) |
doc_content_chars_max |
Integer |
Maximum number of characters for document content (advanced) |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List of Wikipedia search results |
tool |
Tool |
Wikipedia search tool for use in LangChain |
Component code
wikipedia_api.py
404: Not Found
Wolfram Alpha API
This component creates a tool for querying the Wolfram Alpha API.
Parameters
Name | Type | Description |
---|---|---|
input_value |
String |
Query input for Wolfram Alpha |
app_id |
SecretString |
Wolfram Alpha API App ID |
Name | Type | Description |
---|---|---|
results |
List[Data] |
List containing the Wolfram Alpha API response |
tool |
Tool |
Wolfram Alpha API tool for use in LangChain |
Component code
wolfram_alpha_api.py
404: Not Found
Yahoo Finance News Tool
This component creates a tool for retrieving news from Yahoo Finance.
Parameters
This component does not have any input parameters.
Name | Type | Description |
---|---|---|
tool |
Tool |
Yahoo Finance News tool for use in LangChain |
Component code
yahoo.py
404: Not Found