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
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:
@@ -0,0 +1,130 @@
|
||||
---
|
||||
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** | [ComponentTool](/reference/tools-api#componenttool) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/component_tool.py |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</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 must be a Haystack component instance, either an existing one or a custom component.
|
||||
- `name` is optional and defaults to the component class name in snake case, for example, "serper_dev_web_search" for `SerperDevWebSearch`.
|
||||
- `description` is optional and defaults to the component’s docstring. This is what the LLM uses to decide when to call the tool.
|
||||
- `parameters` is optional and lets you override the auto-generated JSON schema for the tool’s inputs.
|
||||
- `outputs_to_string` is optional and controls how the component’s output is converted to a string for the LLM. By default, the full result dict is serialized. Use `{"source": "key"}` to extract a single output key, or add `"handler"` to apply a custom formatter. When wrapping an `Agent` as a sub-tool, use `{"source": "last_message"}` to surface only the agent’s final reply.
|
||||
- `inputs_from_state` is optional and maps agent state keys to component input parameters. Example: `{"repository": "repo"}` passes the state value at `"repository"` as the component’s `"repo"` input.
|
||||
- `outputs_to_state` is optional and maps component output keys to agent state keys. Example: `{"documents": {"source": "docs"}}` writes the component’s `"docs"` output to `"documents"` in state.
|
||||
|
||||
## Usage
|
||||
|
||||
:::tip
|
||||
The recommended way to use `ComponentTool` in Haystack is with the [`Agent`](../pipeline-components/agents-1/agent.mdx) component, which manages the tool call loop for you. The pipeline example below shows the manual approach for cases where you need fine-grained control.
|
||||
:::
|
||||
|
||||
### With the Agent Component
|
||||
|
||||
```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 haystack.utils import Secret
|
||||
|
||||
# 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 = Agent(
|
||||
system_prompt="You are an assistant that can use web search to find information.",
|
||||
chat_generator=OpenAIChatGenerator(),
|
||||
tools=[search_tool],
|
||||
)
|
||||
|
||||
response = agent.run(
|
||||
messages=[ChatMessage.from_user("Give me a brief summary on who Nikola Tesla is")],
|
||||
)
|
||||
|
||||
print(response["messages"][-1].text)
|
||||
```
|
||||
|
||||
### In a Pipeline
|
||||
|
||||
You can also wire `ComponentTool` into a pipeline manually with `ChatGenerator` and `ToolInvoker` for full control over the tool call loop.
|
||||
|
||||
```python
|
||||
from haystack import 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
|
||||
)
|
||||
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-5.4-nano", tools=[tool]))
|
||||
pipeline.add_component("tool_invoker", ToolInvoker(tools=[tool]))
|
||||
pipeline.connect("llm.replies", "tool_invoker.messages")
|
||||
|
||||
message = ChatMessage.from_user(
|
||||
"Use the web search tool to find information about Nikola Tesla",
|
||||
)
|
||||
|
||||
result = pipeline.run({"llm": {"messages": [message]}})
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
📖 Related docs:
|
||||
|
||||
- [Multi-Agent Systems](../concepts/agents/multi-agent-systems.mdx)
|
||||
|
||||
📚 Tutorials:
|
||||
|
||||
- [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent)
|
||||
- [Creating a Multi-Agent System with Haystack](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system)
|
||||
@@ -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 (20–30+), as this can overwhelm the LLM’s 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,240 @@
|
||||
---
|
||||
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** | [PipelineTool](/reference/tools-api#pipeline_tool) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/pipeline_tool.py |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</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 pipeline’s 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.
|
||||
- `parameters` is optional and lets you override the auto-generated JSON schema for the tool's inputs.
|
||||
- `outputs_to_string` is optional and controls how the pipeline's output is converted to a string for the LLM. By default, the full result dict is serialized. Use `{"source": "key"}` to extract a single output key, or add `"handler"` to apply a custom formatter.
|
||||
- `inputs_from_state` is optional and maps agent state keys to pipeline input parameters. Example: `{"repository": "repo"}` passes the state value at `"repository"` as the pipeline's `"repo"` input.
|
||||
- `outputs_to_state` is optional and maps pipeline output keys to agent state keys. Example: `{"documents": {"source": "docs"}}` writes the pipeline's `"docs"` output to `"documents"` in state.
|
||||
|
||||
## Usage
|
||||
|
||||
:::tip
|
||||
The recommended way to use `PipelineTool` in Haystack is with the [`Agent`](../pipeline-components/agents-1/agent.mdx) component, which manages the tool call loop for you. The pipeline example below shows the manual approach for cases where you need fine-grained control.
|
||||
:::
|
||||
|
||||
### 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",
|
||||
)
|
||||
|
||||
print(retrieval_tool)
|
||||
```
|
||||
|
||||
### With the Agent Component
|
||||
|
||||
```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.",
|
||||
),
|
||||
]
|
||||
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",
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
system_prompt="You are an assistant that can use a retrieval tool to find information about Nikola Tesla.",
|
||||
chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
|
||||
tools=[retriever_tool],
|
||||
)
|
||||
|
||||
result = agent.run([ChatMessage.from_user("Who was Nikola Tesla?")])
|
||||
|
||||
print("Answer:")
|
||||
print(result["messages"][-1].text)
|
||||
```
|
||||
|
||||
### In a Pipeline
|
||||
|
||||
You can also wire `PipelineTool` into a pipeline manually with `ChatGenerator` and `ToolInvoker` for full control over the tool call loop.
|
||||
|
||||
```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",
|
||||
)
|
||||
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component(
|
||||
"llm",
|
||||
OpenAIChatGenerator(model="gpt-5.4-nano", tools=[retriever_tool]),
|
||||
)
|
||||
pipeline.add_component("tool_invoker", ToolInvoker(tools=[retriever_tool]))
|
||||
pipeline.connect("llm.replies", "tool_invoker.messages")
|
||||
|
||||
message = ChatMessage.from_user(
|
||||
"Use the document retriever tool to find information about Nikola Tesla",
|
||||
)
|
||||
|
||||
result = pipeline.run({"llm": {"messages": [message]}})
|
||||
|
||||
print(result)
|
||||
```
|
||||
+113
@@ -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'.
|
||||
```
|
||||
+100
@@ -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!
|
||||
```
|
||||
+108
@@ -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).
|
||||
```
|
||||
+102
@@ -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).
|
||||
```
|
||||
+136
@@ -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,127 @@
|
||||
---
|
||||
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 |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</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,527 @@
|
||||
---
|
||||
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 tool’s 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 tool’s 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
|
||||
|
||||
There are three ways to create a `Tool`:
|
||||
|
||||
- **`@tool` decorator** — recommended for most cases; infers name, description, and schema from the function.
|
||||
- **`create_tool_from_function`** — same as `@tool` but called as a function; useful when you can’t decorate directly.
|
||||
- **Manual initialization** — construct `Tool(...)` directly when you need full control over the JSON schema.
|
||||
|
||||
:::tip
|
||||
For most use cases, we recommend `@tool` or `create_tool_from_function`. Both automatically generate the `parameters` JSON schema from your function’s type hints and [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) parameter descriptions, so you don’t need to write the schema by hand.
|
||||
:::
|
||||
|
||||
### @tool decorator
|
||||
|
||||
The `@tool` decorator converts a function into a Tool. It infers the name, description, and parameters from the function and automatically generates a JSON schema. Use `typing.Annotated` to add descriptions to individual parameters. When called without arguments (`@tool`), defaults are inferred from the function. When called with arguments (`@tool(name=..., outputs_to_state=...)`), you can customize any of the Tool fields.
|
||||
|
||||
```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
|
||||
|
||||
`create_tool_from_function` is the functional equivalent of `@tool` — useful when you’re working with a function you can’t decorate directly (e.g. a method from a library). It accepts the same optional parameters as `@tool` and generates the JSON schema in the same way.
|
||||
|
||||
```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>,
|
||||
)
|
||||
```
|
||||
|
||||
### Manual Initialization
|
||||
|
||||
Use this approach when you need full control over the JSON schema — for example, when the function signature alone isn’t enough to express the parameter constraints.
|
||||
|
||||
```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 tool’s 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.4-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.
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
:::tip
|
||||
The recommended way to use tools in Haystack is through the [`Agent`](../pipeline-components/agents-1/agent.mdx) component, which manages the full tool call loop automatically. The sections below also show how to wire `ChatGenerator` and `ToolInvoker` together manually for cases where you need fine-grained control over the loop.
|
||||
:::
|
||||
|
||||
### Passing Tools to Agent
|
||||
|
||||
The [`Agent`](../pipeline-components/agents-1/agent.mdx) component is the easiest way to use tools. It internally combines a Chat Generator and a `ToolInvoker`, runs the tool call loop for you, and exposes the final response and any state written by tools.
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.tools import tool
|
||||
from haystack.components.agents import Agent
|
||||
|
||||
|
||||
@tool(outputs_to_state={"calc_result": {"source": "result"}})
|
||||
def calculator(expression: Annotated[str, "math expression to evaluate"]) -> dict:
|
||||
"""Evaluate a basic math expression."""
|
||||
try:
|
||||
result = eval(expression, {"__builtins__": {}})
|
||||
return {"result": result}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
agent = Agent(
|
||||
system_prompt="You are a helpful assistant that can perform calculations using the calculator tool.",
|
||||
chat_generator=OpenAIChatGenerator(),
|
||||
tools=[calculator],
|
||||
state_schema={"calc_result": {"type": int}},
|
||||
)
|
||||
|
||||
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
|
||||
|
||||
print(response["messages"])
|
||||
print("Calc Result:", response.get("calc_result"))
|
||||
```
|
||||
|
||||
### Manual Tool Calling with ChatGenerator and ToolInvoker
|
||||
|
||||
:::note
|
||||
The following sections show the lower-level approach of driving tool calls yourself with `ChatGenerator` and `ToolInvoker`. This is useful when you need precise control over the loop — for example, to add custom logic between steps — but for most use cases the `Agent` component above is simpler.
|
||||
:::
|
||||
|
||||
#### 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-5.4-nano", 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-5.4-nano")
|
||||
|
||||
# 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 typing import Annotated
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.tools import tool
|
||||
|
||||
|
||||
@tool
|
||||
def weather(location: Annotated[str, "the city to get weather for"]) -> dict:
|
||||
"""Get the current weather for a location."""
|
||||
return {
|
||||
"temp": f"{random.randint(-10, 40)} °C",
|
||||
"humidity": f"{random.randint(0, 100)}%",
|
||||
}
|
||||
|
||||
|
||||
# Initialize the Chat Generator with the weather tool
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-5.4-nano", tools=[weather])
|
||||
|
||||
# Initialize the Tool Invoker with the weather tool
|
||||
tool_invoker = ToolInvoker(tools=[weather])
|
||||
|
||||
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-5.4-nano’, ‘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 tool’s response by passing it back to the Chat Generator. This generates a user-friendly and conversational message.
|
||||
|
||||
Building on the [previous example](#executing-tool-calls), we extend the `if` block to send all messages back to the Chat Generator:
|
||||
|
||||
```python
|
||||
# ... same setup as above (weather tool, chat_generator, tool_invoker)
|
||||
|
||||
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}")
|
||||
# pass all messages back to the Chat Generator for a final natural-language response
|
||||
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-5.4-nano’, ‘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-5.4-nano’, ‘index’: 0, ‘finish_reason’: ‘stop’, ‘usage’: {‘completion_tokens’: 19, ‘prompt_tokens’: 85, ‘total_tokens’: 104}}
|
||||
)]
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
📚 Tutorials:
|
||||
|
||||
- [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent)
|
||||
- [Creating a Multi-Agent System with Haystack](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system)
|
||||
- [Human-in-the-Loop with Haystack Agents](https://haystack.deepset.ai/tutorials/47_human_in_the_loop_agent)
|
||||
|
||||
🧑🍳 Cookbooks:
|
||||
|
||||
- [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
|
||||
@@ -0,0 +1,190 @@
|
||||
---
|
||||
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 |
|
||||
| **Package name** | `haystack-ai` |
|
||||
|
||||
</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
|
||||
|
||||
Here’s 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 typing import Annotated
|
||||
from haystack.tools import Toolset, tool
|
||||
|
||||
|
||||
@tool
|
||||
def add_numbers(
|
||||
a: Annotated[int, "first number"],
|
||||
b: Annotated[int, "second number"],
|
||||
) -> int:
|
||||
"""Add two numbers."""
|
||||
return a + b
|
||||
|
||||
|
||||
@tool
|
||||
def subtract_numbers(
|
||||
a: Annotated[int, "first number"],
|
||||
b: Annotated[int, "second number"],
|
||||
) -> int:
|
||||
"""Subtract b from a."""
|
||||
return a - b
|
||||
|
||||
|
||||
math_toolset = Toolset([add_numbers, subtract_numbers])
|
||||
```
|
||||
|
||||
### Adding New Tools to Toolset
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from haystack.tools import tool
|
||||
|
||||
|
||||
@tool
|
||||
def multiply_numbers(
|
||||
a: Annotated[int, "first number"],
|
||||
b: Annotated[int, "second number"],
|
||||
) -> int:
|
||||
"""Multiply two numbers."""
|
||||
return a * b
|
||||
|
||||
|
||||
math_toolset.add(multiply_numbers)
|
||||
|
||||
# or, you can merge toolsets together
|
||||
math_toolset.add(another_toolset)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can use `Toolset` wherever you can use Tools in Haystack.
|
||||
|
||||
:::tip
|
||||
The recommended way to use a `Toolset` in Haystack is with the [`Agent`](../pipeline-components/agents-1/agent.mdx) component, which manages the tool call loop for you. The examples below also show how to wire `ChatGenerator` and `ToolInvoker` together manually for cases where you need fine-grained control.
|
||||
:::
|
||||
|
||||
### With the Agent
|
||||
|
||||
```python
|
||||
from haystack.components.agents import Agent
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
|
||||
agent = Agent(
|
||||
system_prompt="You are a helpful assistant that can do math using the tools at your disposal.",
|
||||
chat_generator=OpenAIChatGenerator(model="gpt-5.4-nano"),
|
||||
tools=math_toolset,
|
||||
)
|
||||
|
||||
response = agent.run(messages=[ChatMessage.from_user("What is 4 + 2?")])
|
||||
|
||||
print(response["messages"][-1].text)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
4 + 2 equals 6.
|
||||
```
|
||||
|
||||
### With ChatGenerator and ToolInvoker
|
||||
|
||||
```python
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-5.4-nano", tools=math_toolset)
|
||||
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')],
|
||||
_meta={'model': 'gpt-5.4-nano', 'index': 0, 'finish_reason': 'tool_calls', 'usage': {'completion_tokens': 18, 'prompt_tokens': 75, 'total_tokens': 93}}
|
||||
)]
|
||||
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
|
||||
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component(
|
||||
"llm",
|
||||
OpenAIChatGenerator(model="gpt-5.4-nano", 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-5.4-nano"))
|
||||
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_msg = ChatMessage.from_user(text="What is 2+2?")
|
||||
|
||||
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.
|
||||
```
|
||||
Reference in New Issue
Block a user