chore: import upstream snapshot with attribution
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
Docker image release / Build base image (push) Has been cancelled
Sync docs with Docusaurus / sync (push) Has been cancelled
Tests / Check if changed (push) Has been cancelled
Tests / format (push) Has been cancelled
Tests / check-imports (push) Has been cancelled
Tests / Unit / macos-latest (push) Has been cancelled
Tests / Unit / ubuntu-latest (push) Has been cancelled
Tests / Unit / windows-latest (push) Has been cancelled
Tests / mypy (push) Has been cancelled
Tests / Integration / ubuntu-latest (push) Has been cancelled
Tests / Integration / macos-latest (push) Has been cancelled
Tests / Integration / windows-latest (push) Has been cancelled
Tests / notify-slack-on-failure (push) Has been cancelled
Tests / Mark tests as completed (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:22:28 +08:00
commit c56bef871b
9296 changed files with 1854228 additions and 0 deletions
@@ -0,0 +1,131 @@
---
title: "ComponentTool"
id: componenttool
slug: "/componenttool"
description: "This wrapper allows using Haystack components to be used as tools by LLMs."
---
# ComponentTool
This wrapper allows using Haystack components to be used as tools by LLMs.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `component`: The Haystack component to wrap |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/component_tool.py |
</div>
## Overview
`ComponentTool` is a Tool that wraps Haystack components, allowing them to be used as tools by LLMs. ComponentTool automatically generates LLM-compatible tool schemas from component input sockets, which are derived from the component's `run` method signature and type hints.
It does input type conversion and offers support for components with run methods that have the following input types:
- Basic types (str, int, float, bool, dict)
- Dataclasses (both simple and nested structures)
- Lists of basic types (such as List[str])
- Lists of dataclasses (such as List[Document])
- Parameters with mixed types (such as List[Document], str...)
### Parameters
- `component` is mandatory and needs to be a Haystack component, either an existing one or a custom component.
- `name` is optional and defaults to the name of the component written in snake case, for example, "serper_dev_web_search" for SerperDevWebSearch.
- `description` is optional and defaults to the components docstring. Its the description that explains to the LLM what the tool can be used for.
## Usage
Install the additional dependencies `docstring-parser` and `jsonschema` package to use the `ComponentTool`:
```shell
pip install docstring-parser jsonschema
```
### In a pipeline
You can create a `ComponentTool` from an existing `SerperDevWebSearch` component and let an `OpenAIChatGenerator` use it as a tool in a pipeline.
```python
from haystack import component, Pipeline
from haystack.tools import ComponentTool
from haystack.components.websearch import SerperDevWebSearch
from haystack.utils import Secret
from haystack.components.tools.tool_invoker import ToolInvoker
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
## Create a SerperDev search component
search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3)
## Create a tool from the component
tool = ComponentTool(
component=search,
name="web_search", # Optional: defaults to "serper_dev_web_search"
description="Search the web for current information on any topic", # Optional: defaults to component docstring
)
## Create pipeline with OpenAIChatGenerator and ToolInvoker
pipeline = Pipeline()
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=[tool]))
pipeline.add_component("tool_invoker", ToolInvoker(tools=[tool]))
## Connect components
pipeline.connect("llm.replies", "tool_invoker.messages")
message = ChatMessage.from_user(
"Use the web search tool to find information about Nikola Tesla",
)
## Run pipeline
result = pipeline.run({"llm": {"messages": [message]}})
print(result)
```
### With the Agent Component
You can use `ComponentTool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import ComponentTool
from haystack.components.agents import Agent
from haystack.components.websearch import SerperDevWebSearch
from typing import List
## Create a SerperDev search component
search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3)
## Create a tool from the component
search_tool = ComponentTool(
component=search,
name="web_search", # Optional: defaults to "serper_dev_web_search"
description="Search the web for current information on any topic", # Optional: defaults to component docstring
)
## Agent Setup
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[search_tool],
exit_conditions=["text"],
)
## Run the Agent
response = agent.run(
messages=[ChatMessage.from_user("Find information about Nikola Tesla")],
)
## Output
print(response["messages"][-1].text)
```
## Additional References
🧑‍🍳 Cookbook: [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
📓 Tutorial: [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent)
@@ -0,0 +1,191 @@
---
title: "MCPTool"
id: mcptool
slug: "/mcptool"
description: "MCPTool enables integration with external tools and services through the Model Context Protocol (MCP)."
---
# MCPTool
MCPTool enables integration with external tools and services through the Model Context Protocol (MCP).
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `name`: The name of the tool<br />`server_info`: Information about the MCP server to connect to |
| **API reference** | [MCP](/reference/integrations-mcp) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/mcp |
</div>
## Overview
`MCPTool` is a Tool that allows Haystack to communicate with external tools and services using the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). MCP is an open protocol that standardizes how applications provide context to LLMs, similar to how USB-C provides a standardized way to connect devices.
The `MCPTool` supports multiple transport options:
- Streamable HTTP for connecting to HTTP servers,
- SSE (Server-Sent Events) for connecting to HTTP servers **(deprecated)**,
- StdIO for direct execution of local programs.
Learn more about the MCP protocol and its architecture at the [official MCP website](https://modelcontextprotocol.io/).
### Parameters
- `name` is _mandatory_ and specifies the name of the tool.
- `server_info` is _mandatory_ and needs to be either an `SSEServerInfo`, `StreamableHttpServerInfo` or `StdioServerInfo` object that contains connection information.
- `description` is _optional_ and provides context to the LLM about what the tool does.
### Results
The Tool return results as a list of JSON objects, representing `TextContent`, `ImageContent`, or `EmbeddedResource` types from the mcp-sdk.
## Usage
Install the MCP-Haystack integration to use the `MCPTool`:
```shell
pip install mcp-haystack
```
### With Streamable HTTP Transport
You can create an `MCPTool` that connects to an external HTTP server using streamable-http transport:
```python
from haystack_integrations.tools.mcp import MCPTool, StreamableHttpServerInfo
## Create an MCP tool that connects to an HTTP server
server_info = StreamableHttpServerInfo(url="http://localhost:8000/mcp")
tool = MCPTool(name="my_tool", server_info=server_info)
## Use the tool
result = tool.invoke(param1="value1", param2="value2")
```
### With SSE Transport (deprecated)
:::warning
SSE transport has been [deprecated by the MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http) in favor of Streamable HTTP. Use [Streamable HTTP](#with-streamable-http-transport) for new integrations. If you are connecting to an existing SSE-only server, `SSEServerInfo` will continue to work, but consider migrating to `StreamableHttpServerInfo` when the server supports it.
:::
You can create an `MCPTool` that connects to an external HTTP server using SSE transport:
```python
from haystack_integrations.tools.mcp import MCPTool, SSEServerInfo
## Create an MCP tool that connects to an HTTP server
server_info = SSEServerInfo(url="http://localhost:8000/sse")
tool = MCPTool(name="my_tool", server_info=server_info)
## Use the tool
result = tool.invoke(param1="value1", param2="value2")
```
### With StdIO Transport
You can also create an `MCPTool` that executes a local program directly and connects to it through stdio transport:
```python
from haystack_integrations.tools.mcp import MCPTool, StdioServerInfo
## Create an MCP tool that uses stdio transport
server_info = StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
)
tool = MCPTool(name="get_current_time", server_info=server_info)
## Get the current time in New York
result = tool.invoke(timezone="America/New_York")
```
### In a pipeline
You can integrate an `MCPTool` into a pipeline with a `ChatGenerator` and a `ToolInvoker`:
```python
from haystack import Pipeline
from haystack.components.converters import OutputAdapter
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.dataclasses import ChatMessage
from haystack_integrations.tools.mcp import MCPTool, StdioServerInfo
time_tool = MCPTool(
name="get_current_time",
server_info=StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
),
)
pipeline = Pipeline()
pipeline.add_component(
"llm",
OpenAIChatGenerator(model="gpt-4o-mini", tools=[time_tool]),
)
pipeline.add_component("tool_invoker", ToolInvoker(tools=[time_tool]))
pipeline.add_component(
"adapter",
OutputAdapter(
template="{{ initial_msg + initial_tool_messages + tool_messages }}",
output_type=list[ChatMessage],
unsafe=True,
),
)
pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
pipeline.connect("llm.replies", "tool_invoker.messages")
pipeline.connect("llm.replies", "adapter.initial_tool_messages")
pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
pipeline.connect("adapter.output", "response_llm.messages")
user_input = "What is the time in New York? Be brief." # can be any city
user_input_msg = ChatMessage.from_user(text=user_input)
result = pipeline.run(
{
"llm": {"messages": [user_input_msg]},
"adapter": {"initial_msg": [user_input_msg]},
},
)
print(result["response_llm"]["replies"][0].text)
## The current time in New York is 1:57 PM.
```
### With the Agent Component
You can use `MCPTool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.components.agents import Agent
from haystack_integrations.tools.mcp import MCPTool, StdioServerInfo
time_tool = MCPTool(
name="get_current_time",
server_info=StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
),
)
## Agent Setup
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[time_tool],
exit_conditions=["text"],
)
## Run the Agent
response = agent.run(
messages=[ChatMessage.from_user("What is the time in New York? Be brief.")],
)
## Output
print(response["messages"][-1].text)
```
@@ -0,0 +1,154 @@
---
title: "MCPToolset"
id: mcptoolset
slug: "/mcptoolset"
description: "`MCPToolset` connects to an MCP-compliant server and automatically loads all available tools into a single manageable unit. These tools can be used directly with components like Chat Generator, `ToolInvoker`, or `Agent`."
---
# MCPToolset
`MCPToolset` connects to an MCP-compliant server and automatically loads all available tools into a single manageable unit. These tools can be used directly with components like Chat Generator, `ToolInvoker`, or `Agent`.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `server_info`: Information about the MCP server to connect to |
| **API reference** | [mcp](/reference/integrations-mcp) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/mcp |
</div>
## Overview
MCPToolset is a subclass of `Toolset` that dynamically discovers and loads tools from any MCP-compliant server.
It supports:
- **Streamable HTTP** for connecting to HTTP servers
- **SSE (Server-Sent Events)** _(deprecated)_ for remote MCP servers through HTTP
- **StdIO** for local tool execution through subprocess
The MCPToolset makes it easy to plug external tools into pipelines (with Chat Generators and `ToolInvoker`) or agents, with built-in support for filtering (with `tool_names`).
### Parameters
To initialize the MCPToolset, use the following parameters:
- `server_info` (required): Connection information for the MCP server
- `tool_names` (optional): A list of tool names to add to the Toolset
:::info
Note that if `tool_names` is not specified, all tools from the MCP server will be loaded. Be cautious if there are many tools (2030+), as this can overwhelm the LLMs tool resolution logic.
:::
### Installation
```shell
pip install mcp-haystack
```
## Usage
### With StdIO Transport
```python
from haystack_integrations.tools.mcp import MCPToolset, StdioServerInfo
server_info = StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
)
toolset = MCPToolset(
server_info=server_info,
tool_names=["get_current_time"],
) # If tool_names is omitted, all tools on this MCP server will be loaded (can overwhelm LLM if too many)
```
### With Streamable HTTP Transport
```python
from haystack_integrations.tools.mcp import MCPToolset, StreamableHttpServerInfo
server_info = SSEServerInfo(url="http://localhost:8000/mcp")
toolset = MCPToolset(server_info=server_info, tool_names=["get_current_time"])
```
### With SSE Transport (deprecated)
```python
from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
server_info = SSEServerInfo(url="http://localhost:8000/sse")
toolset = MCPToolset(server_info=server_info, tool_names=["get_current_time"])
```
### In a Pipeline
```python
from haystack import Pipeline
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.components.converters import OutputAdapter
from haystack.dataclasses import ChatMessage
from haystack_integrations.tools.mcp import MCPToolset, StdioServerInfo
server_info = StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
)
toolset = MCPToolset(server_info=server_info)
pipeline = Pipeline()
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=toolset))
pipeline.add_component("tool_invoker", ToolInvoker(tools=toolset))
pipeline.add_component(
"adapter",
OutputAdapter(
template="{{ initial_msg + initial_tool_messages + tool_messages }}",
output_type=list[ChatMessage],
unsafe=True,
),
)
pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
pipeline.connect("llm.replies", "tool_invoker.messages")
pipeline.connect("llm.replies", "adapter.initial_tool_messages")
pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
pipeline.connect("adapter.output", "response_llm.messages")
user_input = ChatMessage.from_user(text="What is the time in New York?")
result = pipeline.run(
{"llm": {"messages": [user_input]}, "adapter": {"initial_msg": [user_input]}},
)
print(result["response_llm"]["replies"][0].text)
```
### With the Agent
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.agents import Agent
from haystack.dataclasses import ChatMessage
from haystack_integrations.tools.mcp import MCPToolset, StdioServerInfo
toolset = MCPToolset(
server_info=StdioServerInfo(
command="uvx",
args=["mcp-server-time", "--local-timezone=Europe/Berlin"],
),
tool_names=[
"get_current_time",
], # Omit to load all tools, but may overwhelm LLM if many
)
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=toolset,
exit_conditions=["text"],
)
response = agent.run(messages=[ChatMessage.from_user("What is the time in New York?")])
print(response["messages"][-1].text)
```
@@ -0,0 +1,245 @@
---
title: "PipelineTool"
id: pipelinetool
slug: "/pipelinetool"
description: "Wraps a Haystack pipeline so an LLM can call it as a tool."
---
# PipelineTool
Wraps a Haystack pipeline so an LLM can call it as a tool.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `pipeline`: The Haystack pipeline to wrap <br /> <br />`name`: The name of the tool <br /> <br />`description`: Description of the tool |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/pipeline_tool.py |
</div>
## Overview
`PipelineTool` lets you wrap a whole Haystack pipeline and expose it as a tool that an LLM can call.
It replaces the older workflow of first wrapping a pipeline in a `SuperComponent` and then passing that to
`ComponentTool`.
`PipelineTool` builds the tool parameter schema from the pipelines input sockets and uses the underlying components docstrings for input descriptions. You can choose which pipeline inputs and outputs to expose with
`input_mapping` and `output_mapping`. It works with both `Pipeline` and `AsyncPipeline` and can be used in a pipeline with `ToolInvoker` or directly with the `Agent` component.
### Parameters
- `pipeline` is mandatory and must be a `Pipeline` or `AsyncPipeline` instance.
- `name` is mandatory and specifies the tool name.
- `description` is mandatory and explains what the tool does.
- `input_mapping` is optional. It maps tool input names to pipeline input socket paths. If omitted, a default
mapping is created from all pipeline inputs.
- `output_mapping` is optional. It maps pipeline output socket paths to tool output names. If omitted, a default
mapping is created from all pipeline outputs.
## Usage
### Basic Usage
You can create a `PipelineTool` from any existing Haystack pipeline:
```python
from haystack import Document, Pipeline
from haystack.tools import PipelineTool
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack.components.rankers.sentence_transformers_similarity import (
SentenceTransformersSimilarityRanker,
)
from haystack.document_stores.in_memory import InMemoryDocumentStore
## Create your pipeline
document_store = InMemoryDocumentStore()
## Add some example documents
document_store.write_documents(
[
Document(
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
),
Document(
content="Alternating current (AC) is an electric current which periodically reverses direction.",
),
Document(
content="Thomas Edison promoted direct current (DC) and competed with AC in the War of Currents.",
),
],
)
retrieval_pipeline = Pipeline()
retrieval_pipeline.add_component(
"bm25_retriever",
InMemoryBM25Retriever(document_store=document_store),
)
retrieval_pipeline.add_component(
"ranker",
SentenceTransformersSimilarityRanker(model="cross-encoder/ms-marco-MiniLM-L-6-v2"),
)
retrieval_pipeline.connect("bm25_retriever.documents", "ranker.documents")
## Wrap the pipeline as a tool
retrieval_tool = PipelineTool(
pipeline=retrieval_pipeline,
input_mapping={"query": ["bm25_retriever.query", "ranker.query"]},
output_mapping={"ranker.documents": "documents"},
name="retrieval_tool",
description="Search short articles about Nikola Tesla, AC electricity, and related inventors",
)
```
### In a pipeline
Create a `PipelineTool` from a retrieval pipeline and let an `OpenAIChatGenerator` use it as a tool in a pipeline.
```python
from haystack import Document, Pipeline
from haystack.tools import PipelineTool
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.embedders.sentence_transformers_text_embedder import (
SentenceTransformersTextEmbedder,
)
from haystack.components.embedders.sentence_transformers_document_embedder import (
SentenceTransformersDocumentEmbedder,
)
from haystack.components.retrievers import InMemoryEmbeddingRetriever
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools.tool_invoker import ToolInvoker
from haystack.dataclasses import ChatMessage
## Initialize a document store and add some documents
document_store = InMemoryDocumentStore()
document_embedder = SentenceTransformersDocumentEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
)
documents = [
Document(
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
),
Document(
content="He is best known for his contributions to the design of the modern alternating current (AC) electricity supply system.",
),
]
document_embedder.warm_up()
docs_with_embeddings = document_embedder.run(documents=documents)["documents"]
document_store.write_documents(docs_with_embeddings)
## Build a simple retrieval pipeline
retrieval_pipeline = Pipeline()
retrieval_pipeline.add_component(
"embedder",
SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
)
retrieval_pipeline.add_component(
"retriever",
InMemoryEmbeddingRetriever(document_store=document_store),
)
retrieval_pipeline.connect("embedder.embedding", "retriever.query_embedding")
## Wrap the pipeline as a tool
retriever_tool = PipelineTool(
pipeline=retrieval_pipeline,
input_mapping={"query": ["embedder.text"]},
output_mapping={"retriever.documents": "documents"},
name="document_retriever",
description="For any questions about Nikola Tesla, always use this tool",
)
## Create pipeline with OpenAIChatGenerator and ToolInvoker
pipeline = Pipeline()
pipeline.add_component(
"llm",
OpenAIChatGenerator(model="gpt-4o-mini", tools=[retriever_tool]),
)
pipeline.add_component("tool_invoker", ToolInvoker(tools=[retriever_tool]))
## Connect components
pipeline.connect("llm.replies", "tool_invoker.messages")
message = ChatMessage.from_user(
"Use the document retriever tool to find information about Nikola Tesla",
)
## Run pipeline
result = pipeline.run({"llm": {"messages": [message]}})
print(result)
```
### With the Agent Component
Use `PipelineTool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. The `Agent` includes a `ToolInvoker` and your chosen ChatGenerator to execute tool calls and process tool results.
```python
from haystack import Document, Pipeline
from haystack.tools import PipelineTool
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.embedders.sentence_transformers_text_embedder import (
SentenceTransformersTextEmbedder,
)
from haystack.components.embedders.sentence_transformers_document_embedder import (
SentenceTransformersDocumentEmbedder,
)
from haystack.components.retrievers import InMemoryEmbeddingRetriever
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.agents import Agent
from haystack.dataclasses import ChatMessage
## Initialize a document store and add some documents
document_store = InMemoryDocumentStore()
document_embedder = SentenceTransformersDocumentEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
)
documents = [
Document(
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
),
Document(
content="He is best known for his contributions to the design of the modern alternating current (AC) electricity supply system.",
),
]
document_embedder.warm_up()
docs_with_embeddings = document_embedder.run(documents=documents)["documents"]
document_store.write_documents(docs_with_embeddings)
## Build a simple retrieval pipeline
retrieval_pipeline = Pipeline()
retrieval_pipeline.add_component(
"embedder",
SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
)
retrieval_pipeline.add_component(
"retriever",
InMemoryEmbeddingRetriever(document_store=document_store),
)
retrieval_pipeline.connect("embedder.embedding", "retriever.query_embedding")
## Wrap the pipeline as a tool
retriever_tool = PipelineTool(
pipeline=retrieval_pipeline,
input_mapping={"query": ["embedder.text"]},
output_mapping={"retriever.documents": "documents"},
name="document_retriever",
description="For any questions about Nikola Tesla, always use this tool",
)
## Create an Agent with the tool
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=[retriever_tool],
)
## Let the Agent handle a query
result = agent.run([ChatMessage.from_user("Who was Nikola Tesla?")])
## Print result of the tool call
print("Tool Call Result:")
print(result["messages"][2].tool_call_result.result)
print("")
## Print answer
print("Answer:")
print(result["messages"][-1].text)
```
@@ -0,0 +1,113 @@
---
title: "GitHubFileEditorTool"
id: githubfileeditortool
slug: "/githubfileeditortool"
description: "A Tool that allows Agents and ToolInvokers to edit files in GitHub repositories."
---
# GitHubFileEditorTool
A Tool that allows Agents and ToolInvokers to edit files in GitHub repositories.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `github_token`: GitHub personal access token. Can be set with `GITHUB_TOKEN` env var. |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/github |
</div>
## Overview
`GitHubFileEditorTool` wraps the [`GitHubFileEditor`](../../pipeline-components/connectors/githubfileeditor.mdx) component, providing a tool interface for use in agent workflows and tool-based pipelines.
The tool supports multiple file operations including editing existing files, creating new files, deleting files, and undoing recent changes. It supports four main commands:
- **EDIT**: Edit an existing file by replacing specific content
- **CREATE**: Create a new file with specified content
- **DELETE**: Delete an existing file
- **UNDO**: Revert the last commit if made by the same user
### Parameters
- `name` is _optional_ and defaults to "file_editor". Specifies the name of the tool.
- `description` is _optional_ and provides context to the LLM about what the tool does.
- `github_token` is _mandatory_ and must be a GitHub personal access token for API authentication. The default setting uses the environment variable `GITHUB_TOKEN`.
- `repo` is _optional_ and sets a default repository in owner/repo format.
- `branch` is _optional_ and defaults to "main". Sets the default branch to work with.
- `raise_on_failure` is _optional_ and defaults to `True`. If False, errors are returned instead of raising exceptions.
## Usage
Install the GitHub integration to use the `GitHubFileEditorTool`:
```shell
pip install github-haystack
```
:::info[Repository Placeholder]
To run the following code snippets, you need to replace the `owner/repo` with your own GitHub repository name.
:::
### On its own
Basic usage to edit a file:
```python
from haystack_integrations.tools.github import GitHubFileEditorTool
tool = GitHubFileEditorTool()
result = tool.invoke(
command="edit",
payload={
"path": "src/example.py",
"original": "def old_function():",
"replacement": "def new_function():",
"message": "Renamed function for clarity",
},
repo="owner/repo",
branch="main",
)
print(result)
```
```bash
{'result': 'Edit successful'}
```
### With an Agent
You can use `GitHubFileEditorTool` with the [Agent](../../pipeline-components/agents-1/agent.mdx) component. The Agent will automatically invoke the tool when needed to edit files in GitHub repositories.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.components.agents import Agent
from haystack_integrations.tools.github import GitHubFileEditorTool
editor_tool = GitHubFileEditorTool(repo="owner/repo")
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[editor_tool],
exit_conditions=["text"],
)
response = agent.run(
messages=[
ChatMessage.from_user(
"Edit the file README.md in the repository \"owner/repo\" and replace the original string 'tpyo' with the replacement 'typo'. This is all context you need.",
),
],
)
print(response["last_message"].text)
```
```bash
The file `README.md` has been successfully edited to correct the spelling of 'tpyo' to 'typo'.
```
@@ -0,0 +1,100 @@
---
title: "GitHubIssueCommenterTool"
id: githubissuecommentertool
slug: "/githubissuecommentertool"
description: "A Tool that allows Agents and ToolInvokers to post comments to GitHub issues."
---
# GitHubIssueCommenterTool
A Tool that allows Agents and ToolInvokers to post comments to GitHub issues.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `github_token`: GitHub personal access token. Can be set with `GITHUB_TOKEN` env var. |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/github |
</div>
## Overview
`GitHubIssueCommenterTool` wraps the [`GitHubIssueCommenter`](../../pipeline-components/connectors/githubissuecommenter.mdx) component, providing a tool interface for use in agent workflows and tool-based pipelines.
The tool takes a GitHub issue URL and comment text, then posts the comment to the specified issue using the GitHub API. This requires authentication since posting comments is an authenticated operation.
### Parameters
- `name` is _optional_ and defaults to "issue_commenter". Specifies the name of the tool.
- `description` is _optional_ and provides context to the LLM about what the tool does.
- `github_token` is _mandatory_ and must be a GitHub personal access token for API authentication. The default setting uses the environment variable `GITHUB_TOKEN`.
- `raise_on_failure` is _optional_ and defaults to `True`. If False, errors are returned instead of raising exceptions.
- `retry_attempts` is _optional_ and defaults to `2`. Number of retry attempts for failed requests.
## Usage
Install the GitHub integration to use the `GitHubIssueCommenterTool`:
```shell
pip install github-haystack
```
:::info[Repository Placeholder]
To run the following code snippets, you need to replace the `owner/repo` with your own GitHub repository name.
:::
### On its own
Basic usage to comment on an issue:
```python
from haystack_integrations.tools.github import GitHubIssueCommenterTool
tool = GitHubIssueCommenterTool()
result = tool.invoke(
url="https://github.com/owner/repo/issues/123",
comment="Thanks for reporting this issue! We'll look into it.",
)
print(result)
```
```bash
{'success': True}
```
### With an Agent
You can use `GitHubIssueCommenterTool` with the [Agent](../../pipeline-components/agents-1/agent.mdx) component. The Agent will automatically invoke the tool when needed to post comments on GitHub issues.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.components.agents import Agent
from haystack_integrations.tools.github import GitHubIssueCommenterTool
comment_tool = GitHubIssueCommenterTool(name="github_issue_commenter")
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[comment_tool],
exit_conditions=["text"],
)
response = agent.run(
messages=[
ChatMessage.from_user(
"Please post a helpful comment on this GitHub issue: https://github.com/owner/repo/issues/123 acknowledging the bug report and mentioning that we're investigating",
),
],
)
print(response["last_message"].text)
```
```bash
I have posted the comment on the GitHub issue, acknowledging the bug report and mentioning that the team is investigating the problem. If you need anything else, feel free to ask!
```
@@ -0,0 +1,108 @@
---
title: "GitHubIssueViewerTool"
id: githubissueviewertool
slug: "/githubissueviewertool"
description: "A Tool that allows Agents and ToolInvokers to fetch and parse GitHub issues into documents."
---
# GitHubIssueViewerTool
A Tool that allows Agents and ToolInvokers to fetch and parse GitHub issues into documents.
<div className="key-value-table">
| | |
| --- | --- |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/github |
</div>
## Overview
`GitHubIssueViewerTool` wraps the [`GitHubIssueViewer`](../../pipeline-components/connectors/githubissueviewer.mdx) component, providing a tool interface for use in agent workflows and tool-based pipelines.
The tool takes a GitHub issue URL and returns a list of documents where:
- The first document contains the main issue content,
- Subsequent documents contain the issue comments (if any).
Each document includes rich metadata such as the issue title, number, state, creation date, author, and more.
### Parameters
- `name` is _optional_ and defaults to "issue_viewer". Specifies the name of the tool.
- `description` is _optional_ and provides context to the LLM about what the tool does.
- `github_token` is _optional_ but recommended for private repositories or to avoid rate limiting.
- `raise_on_failure` is _optional_ and defaults to `True`. If False, errors are returned as documents instead of raising exceptions.
- `retry_attempts` is _optional_ and defaults to `2`. Number of retry attempts for failed requests.
## Usage
Install the GitHub integration to use the `GitHubIssueViewerTool`:
```shell
pip install github-haystack
```
:::info[Repository Placeholder]
To run the following code snippets, you need to replace the `owner/repo` with your own GitHub repository name.
:::
### On its own
```python
from haystack_integrations.tools.github import GitHubIssueViewerTool
tool = GitHubIssueViewerTool()
result = tool.invoke(url="https://github.com/deepset-ai/haystack/issues/123")
print(result)
```
```bash
{'documents': [Document(id=3989459bbd8c2a8420a9ba7f3cd3cf79bb41d78bd0738882e57d509e1293c67a, content: 'sentence-transformers = 0.2.6.1
haystack = latest
farm = 0.4.3 latest branch
In the call to Emb...', meta: {'type': 'issue', 'title': 'SentenceTransformer no longer accepts \'gpu" as argument', 'number': 123, 'state': 'closed', 'created_at': '2020-05-28T04:49:31Z', 'updated_at': '2020-05-28T07:11:43Z', 'author': 'predoctech', 'url': 'https://github.com/deepset-ai/haystack/issues/123'}), Document(id=a8a56b9ad119244678804d5873b13da0784587773d8f839e07f644c4d02c167a, content: 'Thanks for reporting!
Fixed with #124 ', meta: {'type': 'comment', 'issue_number': 123, 'created_at': '2020-05-28T07:11:42Z', 'updated_at': '2020-05-28T07:11:42Z', 'author': 'tholor', 'url': 'https://github.com/deepset-ai/haystack/issues/123#issuecomment-635153940'})]}
```
### With an Agent
You can use `GitHubIssueViewerTool` with the [Agent](../../pipeline-components/agents-1/agent.mdx) component. The Agent will automatically invoke the tool when needed to fetch GitHub issue information.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.components.agents import Agent
from haystack_integrations.tools.github import GitHubIssueViewerTool
issue_tool = GitHubIssueViewerTool(name="github_issue_viewer")
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[issue_tool],
exit_conditions=["text"],
)
response = agent.run(
messages=[
ChatMessage.from_user(
"Please analyze this GitHub issue and summarize the main problem: https://github.com/deepset-ai/haystack/issues/123",
),
],
)
print(response["last_message"].text)
```
```bash
The GitHub issue titled "SentenceTransformer no longer accepts 'gpu' as argument" (issue \#123) discusses a problem encountered when using the `EmbeddingRetriever()` function. The user reports that passing the argument `gpu=True` now causes an error because the method that processes this argument does not accept "gpu" anymore; instead, it previously accepted "cuda" without issues.
The user indicates that this change is problematic since it prevents users from instantiating the embedding model with GPU support, forcing them to default to using only the CPU for model execution.
The issue was later closed with a comment indicating it was fixed in another pull request (#124).
```
@@ -0,0 +1,102 @@
---
title: "GitHubPRCreatorTool"
id: githubprcreatortool
slug: "/githubprcreatortool"
description: "A Tool that allows Agents and ToolInvokers to create pull requests from a fork back to the original repository."
---
# GitHubPRCreatorTool
A Tool that allows Agents and ToolInvokers to create pull requests from a fork back to the original repository.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `github_token`: GitHub personal access token. Can be set with `GITHUB_TOKEN` env var. |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/github |
</div>
## Overview
`GitHubPRCreatorTool` wraps the [`GitHubPRCreator`](../../pipeline-components/connectors/githubprcreator.mdx) component, providing a tool interface for use in agent workflows and tool-based pipelines.
The tool takes a GitHub issue URL and creates a pull request from your fork to the original repository, automatically linking it to the specified issue. It's designed to work with existing forks and assumes you have already made changes in a branch.
### Parameters
- `name` is _optional_ and defaults to "pr_creator". Specifies the name of the tool.
- `description` is _optional_ and provides context to the LLM about what the tool does.
- `github_token` is _mandatory_ and must be a GitHub personal access token from the fork owner. The default setting uses the environment variable `GITHUB_TOKEN`.
- `raise_on_failure` is _optional_ and defaults to `True`. If False, errors are returned instead of raising exceptions.
## Usage
Install the GitHub integration to use the `GitHubPRCreatorTool`:
```shell
pip install github-haystack
```
:::info[Repository Placeholder]
To run the following code snippets, you need to replace the `owner/repo` with your own GitHub repository name.
:::
### On its own
Basic usage to create a pull request:
```python
from haystack_integrations.tools.github import GitHubPRCreatorTool
tool = GitHubPRCreatorTool()
result = tool.invoke(
issue_url="https://github.com/owner/repo/issues/123",
title="Fix issue #123",
body="This PR addresses issue #123 by implementing the requested changes.",
branch="fix-123", # Branch in your fork with the changes
base="main", # Branch in original repo to merge into
)
print(result)
```
```bash
{'result': 'Pull request #16 created successfully and linked to issue #4'}
```
### With an Agent
You can use `GitHubPRCreatorTool` with the [Agent](../../pipeline-components/agents-1/agent.mdx) component. The Agent will automatically invoke the tool when needed to create pull requests.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.components.agents import Agent
from haystack_integrations.tools.github import GitHubPRCreatorTool
pr_tool = GitHubPRCreatorTool(name="github_pr_creator")
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[pr_tool],
exit_conditions=["text"],
)
response = agent.run(
messages=[
ChatMessage.from_user(
"Create a pull request for issue https://github.com/owner/repo/issues/4 with title 'Fix authentication bug' and empty body using my fix-4 branch and main as target branch",
),
],
)
print(response["last_message"].text)
```
```bash
The pull request titled "Fix authentication bug" has been created successfully and linked to issue [#123](https://github.com/owner/repo/issues/4).
```
@@ -0,0 +1,136 @@
---
title: "GitHubRepoViewerTool"
id: githubrepoviewertool
slug: "/githubrepoviewertool"
description: "A Tool that allows Agents and ToolInvokers to navigate and fetch content from GitHub repositories."
---
# GitHubRepoViewerTool
A Tool that allows Agents and ToolInvokers to navigate and fetch content from GitHub repositories.
<div className="key-value-table">
| | |
| --- | --- |
| **API reference** | [Tools](/reference/tools-api) |
| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/github |
</div>
## Overview
`GitHubRepoViewerTool` wraps the [`GitHubRepoViewer`](../../pipeline-components/connectors/githubrepoviewer.mdx) component, providing a tool interface for use in agent workflows and tool-based pipelines.
The tool provides different behavior based on the path type:
- **For directories**: Returns a list of documents, one for each item (files and subdirectories),
- **For files**: Returns a single document containing the file content.
Each document includes rich metadata such as the path, type, size, and URL.
### Parameters
- `name` is _optional_ and defaults to "repo_viewer". Specifies the name of the tool.
- `description` is _optional_ and provides context to the LLM about what the tool does.
- `github_token` is _optional_ but recommended for private repositories or to avoid rate limiting.
- `repo` is _optional_ and sets a default repository in owner/repo format.
- `branch` is _optional_ and defaults to "main". Sets the default branch to work with.
- `raise_on_failure` is _optional_ and defaults to `True`. If False, errors are returned as documents instead of raising exceptions.
- `max_file_size` is _optional_ and defaults to `1,000,000` bytes (1MB). Maximum file size to fetch.
## Usage
Install the GitHub integration to use the `GitHubRepoViewerTool`:
```shell
pip install github-haystack
```
:::info[Repository Placeholder]
To run the following code snippets, you need to replace the `owner/repo` with your own GitHub repository name.
:::
### On its own
Basic usage to view repository contents:
```python
from haystack_integrations.tools.github import GitHubRepoViewerTool
tool = GitHubRepoViewerTool()
result = tool.invoke(
repo="deepset-ai/haystack",
path="haystack/components",
branch="main",
)
print(result)
```
```bash
{'documents': [Document(id=..., content: 'agents', meta: {'path': 'haystack/components/agents', 'type': 'dir', 'size': 0, 'url': 'https://github.com/deepset-ai/haystack/tree/main/haystack/components/agents'}), Document(id=..., content: 'audio', meta: {'path': 'haystack/components/audio', 'type': 'dir', 'size': 0, 'url': 'https://github.com/deepset-ai/haystack/tree/main/haystack/components/audio'}),...]}
```
### With an Agent
You can use `GitHubRepoViewerTool` with the [Agent](../../pipeline-components/agents-1/agent.mdx) component. The Agent will automatically invoke the tool when needed to explore repository structure and read files.
Note that we set the Agent's `state_schema` parameter in this code example so that the GitHubRepoViewerTool can write documents to the state.
```python
from typing import List
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage, Document
from haystack.components.agents import Agent
from haystack_integrations.tools.github import GitHubRepoViewerTool
repo_tool = GitHubRepoViewerTool(name="github_repo_viewer")
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[repo_tool],
exit_conditions=["text"],
state_schema={"documents": {"type": List[Document]}},
)
response = agent.run(
messages=[
ChatMessage.from_user(
"Can you analyze the structure of the deepset-ai/haystack repository and tell me about the main components?",
),
],
)
print(response["last_message"].text)
```
```bash
The `deepset-ai/haystack` repository has a structured layout that includes several important components. Here's an overview of its main parts:
1. **Directories**:
- **`.github`**: Contains GitHub-specific configuration files and workflows.
- **`docker`**: Likely includes Docker-related files for containerization of the Haystack application.
- **`docs`**: Contains documentation for the Haystack project. This could include guides, API documentation, and other related resources.
- **`e2e`**: This likely stands for "end-to-end", possibly containing tests or examples related to end-to-end functionality of the Haystack framework.
- **`examples`**: Includes example scripts or notebooks demonstrating how to use Haystack.
- **`haystack`**: This is likely the core source code of the Haystack framework itself, containing the main functionality and classes.
- **`proposals`**: A directory that may contain proposals for new features or changes to the Haystack project.
- **`releasenotes`**: Contains notes about various releases, including changes and improvements.
- **`test`**: This directory likely contains unit tests and other testing utilities to ensure code quality and functionality.
2. **Files**:
- **`.gitignore`**: Specifies files and directories that should be ignored by Git.
- **`.pre-commit-config.yaml`**: Configuration file for pre-commit hooks to automate code quality checks.
- **`CITATION.cff`**: Might include information on how to cite the repository in academic work.
- **`code_of_conduct.txt`**: Contains the code of conduct for contributors and users of the repository.
- **`CONTRIBUTING.md`**: Guidelines for contributing to the repository.
- **`LICENSE`**: The license under which the project is distributed.
- **`VERSION.txt`**: Contains versioning information for the project.
- **`README.md`**: A markdown file that usually provides an overview of the project, installation instructions, and usage examples.
- **`SECURITY.md`**: Contains information about the security policy of the repository.
This structure indicates a well-organized repository that follows common conventions in open-source projects, with a focus on documentation, contribution guidelines, and testing. The core functionalities are likely housed in the `haystack` directory, with additional resources provided in the other directories.
```
@@ -0,0 +1,126 @@
---
title: "SearchableToolset"
id: searchabletoolset
slug: "/searchabletoolset"
description: "Enable agents to dynamically discover tools from large catalogs using keyword-based search."
---
# SearchableToolset
Enable agents to dynamically discover tools from large catalogs using keyword-based search.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `catalog`: A list of Tools and/or Toolsets, or a single Toolset |
| **API reference** | [SearchableToolset](/reference/tools-api#searchabletoolset) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/searchable_toolset.py |
</div>
## Overview
`SearchableToolset` is designed for working with large tool catalogs.
Instead of exposing all tools at once, which can overwhelm the LLM context, it provides a single `search_tools` bootstrap tool.
The agent uses this tool to find and load specific tools from the catalog using BM25 keyword search.
Once the agent calls `search_tools`, the matching tools become immediately available and the agent can invoke them in
subsequent iterations.
### Modes of operation
`SearchableToolset` operates in one of two modes depending on catalog size:
- **Search mode** (default for large catalogs): The agent starts with only the `search_tools` bootstrap tool and discovers other tools on demand. This is activated when the catalog size meets or exceeds `search_threshold`.
- **Passthrough mode** (small catalogs): All tools are exposed directly, with no discovery step needed. This is activated automatically when the catalog has fewer tools than `search_threshold`.
### Parameters
- `catalog` (required): The source of tools — a list of `Tool` and/or `Toolset` instances, or a single `Toolset`. This includes [MCPTool](mcptool.mdx) and [MCPToolset](mcptoolset.mdx) instances.
- `top_k` (optional): The default number of tools returned by each `search_tools` call. Default is `3`.
- `search_threshold` (optional): Minimum catalog size to activate search mode. Catalogs smaller than this value use passthrough mode instead. Default is `8`.
:::info
`SearchableToolset` does not support adding new tools after initialization or merging with other toolsets. Use `catalog` to provide all tools upfront.
:::
## Usage
### Basic usage with an Agent
```python
from typing import Annotated
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import create_tool_from_function, SearchableToolset
def get_weather(city: Annotated[str, "The city to get the weather for"]) -> str:
"""Get current weather for a city."""
return f"Sunny, 22°C in {city}"
def search_web(query: Annotated[str, "The search query"]) -> str:
"""Search the web for information."""
return f"Results for: {query}"
# Build a catalog from tools
catalog = [
create_tool_from_function(get_weather),
create_tool_from_function(search_web),
# ... many more tools
]
toolset = SearchableToolset(catalog=catalog)
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=toolset,
)
# The agent initially sees only `search_tools`. It will call it to find relevant tools,
# then use the discovered tools to answer the question.
result = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])
print(result["messages"][-1].text)
```
### Customizing the bootstrap tool
You can customize the name, description, and parameter descriptions of the `search_tools` bootstrap tool:
- `search_tool_name`: Custom name for the bootstrap tool. Default is `"search_tools"`.
- `search_tool_description`: Custom description for the bootstrap tool.
- `search_tool_parameters_description`: Custom descriptions for the bootstrap tool's parameters. Keys must be a subset of `{"tool_keywords", "k"}`.
```python
toolset = SearchableToolset(
catalog=catalog,
search_tool_name="find_tools",
search_tool_description="Search for tools in the catalog by keyword.",
search_tool_parameters_description={
"tool_keywords": "Keywords to find tools, e.g. 'email send'",
"k": "Max number of tools to return",
},
)
```
### Reusing the toolset across multiple agent runs
When reusing the same `SearchableToolset` instance across multiple agent runs, you can call `clear()` to reset any tools discovered in the previous run:
```python
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=toolset,
)
result1 = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])
# Reset discovered tools before the next run
toolset.clear()
result2 = agent.run(messages=[ChatMessage.from_user("Search for news about AI.")])
```
@@ -0,0 +1,541 @@
---
title: "Tool"
id: tool
slug: "/tool"
description: "`Tool` is a data class representing a function that Language Models can prepare a call for."
---
# Tool
`Tool` is a data class representing a function that Language Models can prepare a call for.
A growing number of Language Models now support passing tool definitions alongside the prompt.
Tool calling refers to the ability of Language Models to generate calls to tools - be they functions or APIs - when responding to user queries. The model prepares the tool call but does not execute it.
If you are looking for the details of this data class's methods and parameters, visit our [API documentation](/reference/tools-api).
## Tool class
`Tool` is a simple and unified abstraction to represent tools in the Haystack framework.
A tool is a function for which Language Models can prepare a call.
The `Tool` class is used in Chat Generators and provides a consistent experience across models. `Tool` is also used in the [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx) component that executes calls prepared by Language Models.
```python
@dataclass
class Tool:
name: str
description: str
parameters: Dict[str, Any]
function: Callable
outputs_to_string: dict[str, Any] | None = None
inputs_from_state: dict[str, str] | None = None
outputs_to_state: dict[str, dict[str, Any]] | None = None
```
- `name` is the name of the Tool.
- `description` is a string describing what the Tool does.
- `parameters` is a JSON schema describing the expected parameters.
- `function` is invoked when the Tool is called.
- `outputs_to_string` (optional) controls how parts of the tools output are converted into one or more strings (e.g. for LLM consumption).
- `inputs_from_state` (optional) maps values from the agent state to the tools input parameters (e.g. to share info between tools)
- `outputs_to_state` (optional) specifies how tool outputs are written back into the agent state, with optional handlers.
Keep in mind that the accurate definitions of `name` and `description` are important for the Language Model to prepare the call correctly.
`Tool` exposes a `tool_spec` property, returning the tool specification to be used by Language Models.
It also has an `invoke` method that executes the underlying function with the provided parameters.
## Tool Initialization
Here is how to initialize a Tool to work with a specific function:
```python
from haystack.tools import Tool
def add(a: int, b: int) -> int:
return a + b
parameters = {
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
}
add_tool = Tool(
name="addition_tool",
description="This tool adds two numbers",
parameters=parameters,
function=add,
)
print(add_tool.tool_spec)
print(add_tool.invoke(a=15, b=10))
```
```
{'name': 'addition_tool',
'description': 'This tool adds two numbers',
'parameters':{'type': 'object',
'properties':{'a':{'type': 'integer'}, 'b':{'type': 'integer'}},
'required':['a', 'b']}}
25
```
### Advanced Tool Configuration
`outputs_to_string` and `outputs_to_state` let you control how a tools outputs are surfaced to the LLM and stored in the agent state.
Use them to format structured outputs for the LLM while keeping raw data available for later steps.
```python
from haystack.tools import Tool
def format_documents(documents):
return "\n".join(f"{i+1}. Document: {doc.content}" for i, doc in enumerate(documents))
def format_summary(metadata):
return f"Found {metadata['count']} results"
tool = Tool(
name="search",
description="Search for documents",
parameters={...},
function=search_func, # Returns {"documents": [Document(...)], "metadata": {"count": 5}, "debug_info": {...}}
outputs_to_string={
"formatted_docs": {"source": "documents", "handler": format_documents},
"summary": {"source": "metadata", "handler": format_summary}
}
outputs_to_state={"documents": {"source": "documents"}}, # Save Documents into Agent's state
)
# After the tool invocation, the tool result includes:
# {
# "formatted_docs": "1. Document Title\n Content...\n2. ...",
# "summary": "Found 5 results"
# }
```
After invocation, only the configured string outputs are returned to the LLM, while selected fields through `outputs_to_state` (like documents) are saved in the agent state.
#### Shaping Tool outputs with `outputs_to_string`
By default, a tool's return value is converted to a string using a default handler before being sent to the Language Model.
You can use `outputs_to_string` to customize this behavior using one of two formats:
1. **Single output format**: Use `source`, `handler`, and/or `raw_result` at the root level.
```python
{
"source": "docs", "handler": format_documents, "raw_result": False
}
```
- `source`: (Optional) Specifies the key to extract from the tool's output dictionary. If omitted, the entire result is passed to the handler.
- `handler`: (Optional) A function that takes the output (or the extracted source value) and returns the final result.
- `raw_result`: (Optional) If `True`, the result is returned "as is" without further string conversion, but applying the `handler` if provided.
This is intended for multimodal tools returning images. In this mode, the tool or handler should return a list of
`TextContent` and `ImageContent` objects for compatibility with Chat Generators.
2. **Multiple output format**: Map custom keys to individual configurations.
```python
{
"formatted_docs": {"source": "docs", "handler": format_documents},
"summary": {"source": "summary_text", "handler": str.upper}
}
```
Each entry defines a `source` key and can optionally include a `handler`. The individual outputs are processed,
collected into a dictionary, and then converted into a single string (usually a JSON-like representation) for the LLM.
:::note
`raw_result` is not supported in the multiple output format.
:::
The example below shows how to use `outputs_to_string` with `raw_result: True` to return images:
```python
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.dataclasses import ChatMessage, ImageContent, TextContent
from haystack.tools import create_tool_from_function
def retrieve_image():
"""Tool to retrieve an image"""
return [
TextContent("Here is the retrieved image."),
ImageContent.from_file_path("test/test_files/images/apple.jpg"),
]
image_retriever_tool = create_tool_from_function(
function=retrieve_image,
outputs_to_string={"raw_result": True},
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5-nano"),
system_prompt="You are an Agent that can retrieve images and describe them.",
tools=[image_retriever_tool],
)
user_message = ChatMessage.from_user(
"Retrieve the image and describe it in max 10 words.",
)
result = agent.run(messages=[user_message])
print(result["last_message"].text)
# Red apple with stem resting on straw.
```
### @tool decorator
The `@tool` decorator simplifies converting a function into a Tool. It infers Tool name, description, and parameters from the function and automatically generates a JSON schema. It uses Python's `typing.Annotated` for the description of parameters. If you need to customize Tool name and description, use `create_tool_from_function` instead.
```python
from typing import Annotated, Literal
from haystack.tools import tool
@tool
def get_weather(
city: Annotated[str, "the city for which to get the weather"] = "Munich",
unit: Annotated[
Literal["Celsius", "Fahrenheit"],
"the unit for the temperature",
] = "Celsius",
):
"""A simple function to get the current weather for a location."""
return f"Weather report for {city}: 20 {unit}, sunny"
print(get_weather)
```
```
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
parameters={
'type': 'object',
'properties': {
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
'unit': {
'type': 'string',
'enum': ['Celsius', 'Fahrenheit'],
'description': 'the unit for the temperature',
'default': 'Celsius',
},
}
},
function=<function get_weather at 0x7f7b3a8a9b80>)
```
### create_tool_from_function
The `create_tool_from_function` method provides more flexibility than the`@tool` decorator and allows setting Tool name and description. It infers the Tool parameters automatically and generates a JSON schema automatically in the same way as the `@tool` decorator.
```python
from typing import Annotated, Literal
from haystack.tools import create_tool_from_function
def get_weather(
city: Annotated[str, "the city for which to get the weather"] = "Munich",
unit: Annotated[
Literal["Celsius", "Fahrenheit"],
"the unit for the temperature",
] = "Celsius",
):
"""A simple function to get the current weather for a location."""
return f"Weather report for {city}: 20 {unit}, sunny"
tool = create_tool_from_function(get_weather)
print(tool)
```
```
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
parameters={
'type': 'object',
'properties': {
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
'unit': {
'type': 'string',
'enum': ['Celsius', 'Fahrenheit'],
'description': 'the unit for the temperature',
'default': 'Celsius',
},
}
},
function=<function get_weather at 0x7f7b3a8a9b80>)
```
## Toolset
A Toolset groups multiple Tool instances into a single manageable unit. It simplifies the passing of tools to components like Chat Generators or `ToolInvoker`, and supports filtering, serialization, and reuse.
```python
from haystack.tools import Toolset
math_toolset = Toolset([add_tool, subtract_tool])
```
See more details and examples on the [Toolset documentation page](toolset.mdx).
## Usage
To better understand this section, make sure you are also familiar with Haystack's [`ChatMessage`](../concepts/data-classes/chatmessage.mdx) data class.
### Passing Tools to a Chat Generator
Using the `tools` parameter, you can pass tools as a list of Tool instances or a single Toolset during initialization or in the `run` method. Tools passed at runtime override those set at initialization.
:::info[Chat Generators support]
Not all Chat Generators currently support tools, but we are actively expanding tool support across more models.
Look out for the `tools` parameter in a specific Chat Generator's `__init__` and `run` methods.
:::
```python
from haystack.dataclasses import ChatMessage
from haystack.components.generators.chat import OpenAIChatGenerator
## Initialize the Chat Generator with the addition tool
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[add_tool])
## here we expect the Tool to be invoked
res = chat_generator.run([ChatMessage.from_user("10 + 238")])
print(res)
## here the model can respond without using the Tool
res = chat_generator.run([ChatMessage.from_user("What is the habitat of a lion?")])
print(res)
```
```
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
_content=[ToolCall(tool_name='addition_tool',
arguments={'a':10, 'b':238},
id='call_rbYtbCdW0UbWMfy2x0sgF1Ap'
)],
_meta={...})]}
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
_content=[TextContent(text='Lions primarily inhabit grasslands, savannas, and open woodlands. ...'
)],
_meta={...})]}
```
The same result of the previous run can be achieved by passing tools at runtime:
```python
## Initialize the Chat Generator without tools
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini")
## pass tools in the run method
res_w_tool_call = chat_generator.run(
[ChatMessage.from_user("10 + 238")],
tools=math_toolset,
)
print(res_w_tool_call)
```
### Executing Tool Calls
To execute prepared tool calls, you can use the [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx) component. This component acts as the execution engine for tools, processing the calls prepared by the Language Model.
Here's an example:
```python
import random
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.tools import Tool
## Define a dummy weather toolimport random
def dummy_weather(location: str):
return {
"temp": f"{random.randint(-10, 40)} °C",
"humidity": f"{random.randint(0, 100)}%",
}
weather_tool = Tool(
name="weather",
description="A tool to get the weather",
function=dummy_weather,
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
)
## Initialize the Chat Generator with the weather tool
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
## Initialize the Tool Invoker with the weather tool
tool_invoker = ToolInvoker(tools=[weather_tool])
user_message = ChatMessage.from_user("What is the weather in Berlin?")
replies = chat_generator.run(messages=[user_message])["replies"]
print(f"assistant messages: {replies}")
## If the assistant message contains a tool call, run the tool invoker
if replies[0].tool_calls:
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
print(f"tool messages: {tool_messages}")
```
```
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
arguments={'location': 'Berlin'}, id='call_YEvCEAmlvc42JGXV84NU8wtV')], _meta={'model': 'gpt-4o-mini-2024-07-18',
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
63}})]
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '22 °C',
'humidity': '35%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
id='call_YEvCEAmlvc42JGXV84NU8wtV'), error=False)], _meta={})]
```
### Processing Tool Results with the Chat Generator
In some cases, the raw output from a tool may not be immediately suitable for the end user.
You can refine the tools response by passing it back to the Chat Generator. This generates a user-friendly and conversational message.
In this example, well pass the tools response back to the Chat Generator for final processing:
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.tools import Tool
## Define a dummy weather toolimport random
def dummy_weather(location: str):
return {
"temp": f"{random.randint(-10, 40)} °C",
"humidity": f"{random.randint(0, 100)}%",
}
weather_tool = Tool(
name="weather",
description="A tool to get the weather",
function=dummy_weather,
parameters={
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
)
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
tool_invoker = ToolInvoker(tools=[weather_tool])
user_message = ChatMessage.from_user("What is the weather in Berlin?")
replies = chat_generator.run(messages=[user_message])["replies"]
print(f"assistant messages: {replies}")
if replies[0].tool_calls:
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
print(f"tool messages: {tool_messages}")
# we pass all the messages to the Chat Generator
messages = [user_message] + replies + tool_messages
final_replies = chat_generator.run(messages=messages)["replies"]
print(f"final assistant messages: {final_replies}")
```
```
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
arguments={'location': 'Berlin'}, id='call_jHX0RCDHRKX7h8V9RrNs6apy')], _meta={'model': 'gpt-4o-mini-2024-07-18',
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
63}})]
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '2 °C',
'humidity': '15%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
id='call_jHX0RCDHRKX7h8V9RrNs6apy'), error=False)], _meta={})]
final assistant messages: [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[TextContent(text='The
current weather in Berlin is 2 °C with a humidity level of 15%.')], _meta={'model': 'gpt-4o-mini-2024-07-18',
'index': 0, 'finish_reason': 'stop', 'usage': {'completion_tokens': 19, 'prompt_tokens': 85, 'total_tokens':
104}})]
```
### Passing Tools to Agent
You can also use `Tool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools.tool import Tool
from haystack.components.agents import Agent
from typing import List
## Tool Function
def calculate(expression: str) -> dict:
try:
result = eval(expression, {"__builtins__": {}})
return {"result": result}
except Exception as e:
return {"error": str(e)}
## Tool Definition
calculator_tool = Tool(
name="calculator",
description="Evaluate basic math expressions.",
parameters={
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Math expression to evaluate",
},
},
"required": ["expression"],
},
function=calculate,
outputs_to_state={"calc_result": {"source": "result"}},
)
## Agent Setup
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[calculator_tool],
exit_conditions=["calculator"],
state_schema={
"calc_result": {"type": int},
},
)
## Run the Agent
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
## Output
print(response["messages"])
print("Calc Result:", response.get("calc_result"))
```
## Additional References
🧑‍🍳 Cookbooks:
- [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
- [Newsletter Sending Agent with Haystack Tools](https://haystack.deepset.ai/cookbook/newsletter-agent)
- [Create a Swarm of Agents](https://haystack.deepset.ai/cookbook/swarm)
@@ -0,0 +1,204 @@
---
title: "Toolset"
id: toolset
slug: "/toolset"
description: "Group multiple Tools into a single unit."
---
# Toolset
Group multiple Tools into a single unit.
<div className="key-value-table">
| | |
| --- | --- |
| **Mandatory init variables** | `tools`: A list of tools |
| **API reference** | [Toolset](/reference/tools-api#toolset) |
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/toolset.py |
</div>
## Overview
A `Toolset` groups multiple Tool instances into a single manageable unit. It simplifies passing tools to components like Chat Generators, [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx), or [`Agent`](../pipeline-components/agents-1/agent.mdx), and supports filtering, serialization, and reuse.
Additionally, by subclassing `Toolset`, you can create implementations that dynamically load tools from external sources like OpenAPI URLs, MCP servers, or other resources.
### Initializing Toolset
Heres how to initialize `Toolset` with [Tool](tool.mdx). Alternatively, you can use [ComponentTool](componenttool.mdx) or [MCPTool](mcptool.mdx) in `Toolset` as Tool instances.
```python
from haystack.tools import Tool, Toolset
## Define math functions
def add_numbers(a: int, b: int) -> int:
return a + b
def subtract_numbers(a: int, b: int) -> int:
return a - b
## Create tools with proper schemas
add_tool = Tool(
name="add",
description="Add two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=add_numbers,
)
subtract_tool = Tool(
name="subtract",
description="Subtract b from a",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=subtract_numbers,
)
## Create a toolset with the math tools
math_toolset = Toolset([add_tool, subtract_tool])
```
### Adding New Tools to Toolset
```python
def multiply_numbers(a: int, b: int) -> int:
return a * b
multiply_tool = Tool(
name="multiply",
description="Multiply two numbers",
parameters={
"type": "object",
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
"required": ["a", "b"],
},
function=multiply_numbers,
)
math_toolset.add(multiply_tool)
## or, you can merge toolsets together
math_toolset.add(another_toolset)
```
## Usage
You can use `Toolset` wherever you can use Tools in Haystack.
### With ChatGenerator and ToolInvoker
```python
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.dataclasses import ChatMessage
## Create a toolset with the math tools
math_toolset = Toolset([add_tool, subtract_tool])
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=math_toolset)
## Initialize the Tool Invoker with the weather tool
tool_invoker = ToolInvoker(tools=math_toolset)
user_message = ChatMessage.from_user("What is 10 minus 5?")
replies = chat_generator.run(messages=[user_message])["replies"]
print(f"assistant message: {replies}")
## If the assistant message contains a tool call, run the tool invoker
if replies[0].tool_calls:
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
print(f"tool result: {tool_messages[0].tool_call_result.result}")
```
Output:
```
assistant message: [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='subtract', arguments={'a': 10, 'b': 5}, id='call_awGa5q7KtQ9BrMGPTj6IgEH1')], _name=None, _meta={'model': 'gpt-4o-mini-2024-07-18', 'index': 0, 'finish_reason': 'tool_calls', 'usage': {'completion_tokens': 18, 'prompt_tokens': 75, 'total_tokens': 93, 'completion_tokens_details': CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), 'prompt_tokens_details': PromptTokensDetails(audio_tokens=0, cached_tokens=0)}})]
tool result: 5
```
### In a Pipeline
```python
from haystack import Pipeline
from haystack.components.converters import OutputAdapter
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.dataclasses import ChatMessage
math_toolset = Toolset([add_tool, subtract_tool])
pipeline = Pipeline()
pipeline.add_component(
"llm",
OpenAIChatGenerator(model="gpt-4o-mini", tools=math_toolset),
)
pipeline.add_component("tool_invoker", ToolInvoker(tools=math_toolset))
pipeline.add_component(
"adapter",
OutputAdapter(
template="{{ initial_msg + initial_tool_messages + tool_messages }}",
output_type=list[ChatMessage],
unsafe=True,
),
)
pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
pipeline.connect("llm.replies", "tool_invoker.messages")
pipeline.connect("llm.replies", "adapter.initial_tool_messages")
pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
pipeline.connect("adapter.output", "response_llm.messages")
user_input = "What is 2+2?"
user_input_msg = ChatMessage.from_user(text=user_input)
result = pipeline.run(
{
"llm": {"messages": [user_input_msg]},
"adapter": {"initial_msg": [user_input_msg]},
},
)
print(result["response_llm"]["replies"][0].text)
```
Output:
```
2 + 2 equals 4.
```
### With the Agent
```python
from haystack.components.agents import Agent
from haystack.dataclasses import ChatMessage
from haystack.components.generators.chat import OpenAIChatGenerator
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=math_toolset,
)
response = agent.run(messages=[ChatMessage.from_user("What is 4 + 2?")])
print(response["messages"][-1].text)
```
Output:
```
4 + 2 equals 6.
```