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,132 @@
|
||||
---
|
||||
title: "ComponentTool"
|
||||
id: componenttool
|
||||
slug: "/componenttool"
|
||||
description: "This wrapper allows using Haystack components to be used as tools by LLMs."
|
||||
---
|
||||
|
||||
# ComponentTool
|
||||
|
||||
This wrapper allows using Haystack components to be used as tools by LLMs.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| **Mandatory init variables** | `component`: The Haystack component to wrap |
|
||||
| **API reference** | [Tools](/reference/tools-api) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/component_tool.py |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
`ComponentTool` is a Tool that wraps Haystack components, allowing them to be used as tools by LLMs. ComponentTool automatically generates LLM-compatible tool schemas from component input sockets, which are derived from the component's `run` method signature and type hints.
|
||||
|
||||
It does input type conversion and offers support for components with run methods that have the following input types:
|
||||
|
||||
- Basic types (str, int, float, bool, dict)
|
||||
- Dataclasses (both simple and nested structures)
|
||||
- Lists of basic types (such as List[str])
|
||||
- Lists of dataclasses (such as List[Document])
|
||||
- Parameters with mixed types (such as List[Document], str...)
|
||||
|
||||
### Parameters
|
||||
|
||||
- `component` is mandatory and needs to be a Haystack component, either an existing one or a custom component.
|
||||
- `name` is optional and defaults to the name of the component written in snake case, for example, "serper_dev_web_search" for SerperDevWebSearch.
|
||||
- `description` is optional and defaults to the component’s docstring. It’s the description that explains to the LLM what the tool can be used for.
|
||||
|
||||
## Usage
|
||||
|
||||
Install the additional dependencies `docstring-parser` and `jsonschema` package to use the `ComponentTool`:
|
||||
|
||||
```shell
|
||||
pip install docstring-parser jsonschema
|
||||
```
|
||||
|
||||
### In a pipeline
|
||||
|
||||
You can create a `ComponentTool` from an existing `SerperDevWebSearch` component and let an `OpenAIChatGenerator` use it as a tool in a pipeline.
|
||||
|
||||
```python
|
||||
from haystack import component, Pipeline
|
||||
from haystack.tools import ComponentTool
|
||||
from haystack.components.websearch import SerperDevWebSearch
|
||||
from haystack.utils import Secret
|
||||
from haystack.components.tools.tool_invoker import ToolInvoker
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
## Create a SerperDev search component
|
||||
search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3)
|
||||
|
||||
## Create a tool from the component
|
||||
tool = ComponentTool(
|
||||
component=search,
|
||||
name="web_search", # Optional: defaults to "serper_dev_web_search"
|
||||
description="Search the web for current information on any topic", # Optional: defaults to component docstring
|
||||
)
|
||||
|
||||
## Create pipeline with OpenAIChatGenerator and ToolInvoker
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=[tool]))
|
||||
pipeline.add_component("tool_invoker", ToolInvoker(tools=[tool]))
|
||||
|
||||
## Connect components
|
||||
pipeline.connect("llm.replies", "tool_invoker.messages")
|
||||
|
||||
message = ChatMessage.from_user(
|
||||
"Use the web search tool to find information about Nikola Tesla",
|
||||
)
|
||||
|
||||
## Run pipeline
|
||||
result = pipeline.run({"llm": {"messages": [message]}})
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
### With the Agent Component
|
||||
|
||||
You can use `ComponentTool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
|
||||
|
||||
```python
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.tools import ComponentTool
|
||||
from haystack.components.agents import Agent
|
||||
from haystack.components.websearch import SerperDevWebSearch
|
||||
from typing import List
|
||||
|
||||
## Create a SerperDev search component
|
||||
search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3)
|
||||
|
||||
## Create a tool from the component
|
||||
search_tool = ComponentTool(
|
||||
component=search,
|
||||
name="web_search", # Optional: defaults to "serper_dev_web_search"
|
||||
description="Search the web for current information on any topic", # Optional: defaults to component docstring
|
||||
)
|
||||
|
||||
## Agent Setup
|
||||
agent = Agent(
|
||||
chat_generator=OpenAIChatGenerator(),
|
||||
tools=[search_tool],
|
||||
exit_conditions=["text"],
|
||||
)
|
||||
|
||||
## Run the Agent
|
||||
agent.warm_up()
|
||||
response = agent.run(
|
||||
messages=[ChatMessage.from_user("Find information about Nikola Tesla")],
|
||||
)
|
||||
|
||||
## Output
|
||||
print(response["messages"][-1].text)
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
🧑🍳 Cookbook: [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
|
||||
|
||||
📓 Tutorial: [Build a Tool-Calling Agent](https://haystack.deepset.ai/tutorials/43_building_a_tool_calling_agent)
|
||||
@@ -0,0 +1,188 @@
|
||||
---
|
||||
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)
|
||||
|
||||
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
|
||||
agent.warm_up()
|
||||
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,156 @@
|
||||
---
|
||||
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
|
||||
|
||||
:::note
|
||||
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"],
|
||||
)
|
||||
agent.warm_up()
|
||||
|
||||
response = agent.run(messages=[ChatMessage.from_user("What is the time in New York?")])
|
||||
print(response["messages"][-1].text)
|
||||
```
|
||||
@@ -0,0 +1,245 @@
|
||||
---
|
||||
title: "PipelineTool"
|
||||
id: pipelinetool
|
||||
slug: "/pipelinetool"
|
||||
description: "Wraps a Haystack pipeline so an LLM can call it as a tool."
|
||||
---
|
||||
|
||||
# PipelineTool
|
||||
|
||||
Wraps a Haystack pipeline so an LLM can call it as a tool.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| **Mandatory init variables** | `pipeline`: The Haystack pipeline to wrap <br /> <br />`name`: The name of the tool <br /> <br />`description`: Description of the tool |
|
||||
| **API reference** | [Tools](/reference/tools-api) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/pipeline_tool.py |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
`PipelineTool` lets you wrap a whole Haystack pipeline and expose it as a tool that an LLM can call.
|
||||
It replaces the older workflow of first wrapping a pipeline in a `SuperComponent` and then passing that to
|
||||
`ComponentTool`.
|
||||
|
||||
`PipelineTool` builds the tool parameter schema from the 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.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
You can create a `PipelineTool` from any existing Haystack pipeline:
|
||||
|
||||
```python
|
||||
from haystack import Document, Pipeline
|
||||
from haystack.tools import PipelineTool
|
||||
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
|
||||
from haystack.components.rankers.sentence_transformers_similarity import (
|
||||
SentenceTransformersSimilarityRanker,
|
||||
)
|
||||
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
||||
|
||||
## Create your pipeline
|
||||
document_store = InMemoryDocumentStore()
|
||||
## Add some example documents
|
||||
document_store.write_documents(
|
||||
[
|
||||
Document(
|
||||
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
|
||||
),
|
||||
Document(
|
||||
content="Alternating current (AC) is an electric current which periodically reverses direction.",
|
||||
),
|
||||
Document(
|
||||
content="Thomas Edison promoted direct current (DC) and competed with AC in the War of Currents.",
|
||||
),
|
||||
],
|
||||
)
|
||||
retrieval_pipeline = Pipeline()
|
||||
retrieval_pipeline.add_component(
|
||||
"bm25_retriever",
|
||||
InMemoryBM25Retriever(document_store=document_store),
|
||||
)
|
||||
retrieval_pipeline.add_component(
|
||||
"ranker",
|
||||
SentenceTransformersSimilarityRanker(model="cross-encoder/ms-marco-MiniLM-L-6-v2"),
|
||||
)
|
||||
retrieval_pipeline.connect("bm25_retriever.documents", "ranker.documents")
|
||||
|
||||
## Wrap the pipeline as a tool
|
||||
retrieval_tool = PipelineTool(
|
||||
pipeline=retrieval_pipeline,
|
||||
input_mapping={"query": ["bm25_retriever.query", "ranker.query"]},
|
||||
output_mapping={"ranker.documents": "documents"},
|
||||
name="retrieval_tool",
|
||||
description="Search short articles about Nikola Tesla, AC electricity, and related inventors",
|
||||
)
|
||||
```
|
||||
|
||||
### In a pipeline
|
||||
|
||||
Create a `PipelineTool` from a retrieval pipeline and let an `OpenAIChatGenerator` use it as a tool in a pipeline.
|
||||
|
||||
```python
|
||||
from haystack import Document, Pipeline
|
||||
from haystack.tools import PipelineTool
|
||||
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
||||
from haystack.components.embedders.sentence_transformers_text_embedder import (
|
||||
SentenceTransformersTextEmbedder,
|
||||
)
|
||||
from haystack.components.embedders.sentence_transformers_document_embedder import (
|
||||
SentenceTransformersDocumentEmbedder,
|
||||
)
|
||||
from haystack.components.retrievers import InMemoryEmbeddingRetriever
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools.tool_invoker import ToolInvoker
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
## Initialize a document store and add some documents
|
||||
document_store = InMemoryDocumentStore()
|
||||
document_embedder = SentenceTransformersDocumentEmbedder(
|
||||
model="sentence-transformers/all-MiniLM-L6-v2",
|
||||
)
|
||||
documents = [
|
||||
Document(
|
||||
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
|
||||
),
|
||||
Document(
|
||||
content="He is best known for his contributions to the design of the modern alternating current (AC) electricity supply system.",
|
||||
),
|
||||
]
|
||||
document_embedder.warm_up()
|
||||
docs_with_embeddings = document_embedder.run(documents=documents)["documents"]
|
||||
document_store.write_documents(docs_with_embeddings)
|
||||
|
||||
## Build a simple retrieval pipeline
|
||||
retrieval_pipeline = Pipeline()
|
||||
retrieval_pipeline.add_component(
|
||||
"embedder",
|
||||
SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
|
||||
)
|
||||
retrieval_pipeline.add_component(
|
||||
"retriever",
|
||||
InMemoryEmbeddingRetriever(document_store=document_store),
|
||||
)
|
||||
retrieval_pipeline.connect("embedder.embedding", "retriever.query_embedding")
|
||||
|
||||
## Wrap the pipeline as a tool
|
||||
retriever_tool = PipelineTool(
|
||||
pipeline=retrieval_pipeline,
|
||||
input_mapping={"query": ["embedder.text"]},
|
||||
output_mapping={"retriever.documents": "documents"},
|
||||
name="document_retriever",
|
||||
description="For any questions about Nikola Tesla, always use this tool",
|
||||
)
|
||||
|
||||
## Create pipeline with OpenAIChatGenerator and ToolInvoker
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component(
|
||||
"llm",
|
||||
OpenAIChatGenerator(model="gpt-4o-mini", tools=[retriever_tool]),
|
||||
)
|
||||
pipeline.add_component("tool_invoker", ToolInvoker(tools=[retriever_tool]))
|
||||
|
||||
## Connect components
|
||||
pipeline.connect("llm.replies", "tool_invoker.messages")
|
||||
|
||||
message = ChatMessage.from_user(
|
||||
"Use the document retriever tool to find information about Nikola Tesla",
|
||||
)
|
||||
|
||||
## Run pipeline
|
||||
result = pipeline.run({"llm": {"messages": [message]}})
|
||||
|
||||
print(result)
|
||||
```
|
||||
|
||||
### With the Agent Component
|
||||
|
||||
Use `PipelineTool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. The `Agent` includes a `ToolInvoker` and your chosen ChatGenerator to execute tool calls and process tool results.
|
||||
|
||||
```python
|
||||
from haystack import Document, Pipeline
|
||||
from haystack.tools import PipelineTool
|
||||
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
||||
from haystack.components.embedders.sentence_transformers_text_embedder import (
|
||||
SentenceTransformersTextEmbedder,
|
||||
)
|
||||
from haystack.components.embedders.sentence_transformers_document_embedder import (
|
||||
SentenceTransformersDocumentEmbedder,
|
||||
)
|
||||
from haystack.components.retrievers import InMemoryEmbeddingRetriever
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.agents import Agent
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
## Initialize a document store and add some documents
|
||||
document_store = InMemoryDocumentStore()
|
||||
document_embedder = SentenceTransformersDocumentEmbedder(
|
||||
model="sentence-transformers/all-MiniLM-L6-v2",
|
||||
)
|
||||
documents = [
|
||||
Document(
|
||||
content="Nikola Tesla was a Serbian-American inventor and electrical engineer.",
|
||||
),
|
||||
Document(
|
||||
content="He is best known for his contributions to the design of the modern alternating current (AC) electricity supply system.",
|
||||
),
|
||||
]
|
||||
document_embedder.warm_up()
|
||||
docs_with_embeddings = document_embedder.run(documents=documents)["documents"]
|
||||
document_store.write_documents(docs_with_embeddings)
|
||||
|
||||
## Build a simple retrieval pipeline
|
||||
retrieval_pipeline = Pipeline()
|
||||
retrieval_pipeline.add_component(
|
||||
"embedder",
|
||||
SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
|
||||
)
|
||||
retrieval_pipeline.add_component(
|
||||
"retriever",
|
||||
InMemoryEmbeddingRetriever(document_store=document_store),
|
||||
)
|
||||
retrieval_pipeline.connect("embedder.embedding", "retriever.query_embedding")
|
||||
|
||||
## Wrap the pipeline as a tool
|
||||
retriever_tool = PipelineTool(
|
||||
pipeline=retrieval_pipeline,
|
||||
input_mapping={"query": ["embedder.text"]},
|
||||
output_mapping={"retriever.documents": "documents"},
|
||||
name="document_retriever",
|
||||
description="For any questions about Nikola Tesla, always use this tool",
|
||||
)
|
||||
|
||||
## Create an Agent with the tool
|
||||
agent = Agent(
|
||||
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
|
||||
tools=[retriever_tool],
|
||||
)
|
||||
|
||||
## Let the Agent handle a query
|
||||
result = agent.run([ChatMessage.from_user("Who was Nikola Tesla?")])
|
||||
|
||||
## Print result of the tool call
|
||||
print("Tool Call Result:")
|
||||
print(result["messages"][2].tool_call_result.result)
|
||||
print("")
|
||||
|
||||
## Print answer
|
||||
print("Answer:")
|
||||
print(result["messages"][-1].text)
|
||||
```
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
:::note[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"],
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
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'.
|
||||
```
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
:::note[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"],
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
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!
|
||||
```
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
:::note[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"],
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
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).
|
||||
```
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
:::note[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"],
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
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).
|
||||
```
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
:::note[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]}},
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
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,433 @@
|
||||
---
|
||||
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
|
||||
```
|
||||
|
||||
- `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.
|
||||
|
||||
Keep in mind that the accurate definitions of `name` and `description` are important for the Language Model to prepare the call correctly.
|
||||
|
||||
`Tool` exposes a `tool_spec` property, returning the tool specification to be used by Language Models.
|
||||
|
||||
It also has an `invoke` method that executes the underlying function with the provided parameters.
|
||||
|
||||
## Tool Initialization
|
||||
|
||||
Here is how to initialize a Tool to work with a specific function:
|
||||
|
||||
```python
|
||||
from haystack.tools import Tool
|
||||
|
||||
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
|
||||
"required": ["a", "b"],
|
||||
}
|
||||
|
||||
add_tool = Tool(
|
||||
name="addition_tool",
|
||||
description="This tool adds two numbers",
|
||||
parameters=parameters,
|
||||
function=add,
|
||||
)
|
||||
|
||||
print(add_tool.tool_spec)
|
||||
|
||||
print(add_tool.invoke(a=15, b=10))
|
||||
```
|
||||
|
||||
```
|
||||
{'name': 'addition_tool',
|
||||
'description': 'This tool adds two numbers',
|
||||
'parameters':{'type': 'object',
|
||||
'properties':{'a':{'type': 'integer'}, 'b':{'type': 'integer'}},
|
||||
'required':['a', 'b']}}
|
||||
|
||||
25
|
||||
```
|
||||
|
||||
### @tool decorator
|
||||
|
||||
The `@tool` decorator simplifies converting a function into a Tool. It infers Tool name, description, and parameters from the function and automatically generates a JSON schema. It uses Python's `typing.Annotated` for the description of parameters. If you need to customize Tool name and description, use `create_tool_from_function` instead.
|
||||
|
||||
```python
|
||||
from typing import Annotated, Literal
|
||||
from haystack.tools import tool
|
||||
|
||||
|
||||
@tool
|
||||
def get_weather(
|
||||
city: Annotated[str, "the city for which to get the weather"] = "Munich",
|
||||
unit: Annotated[
|
||||
Literal["Celsius", "Fahrenheit"],
|
||||
"the unit for the temperature",
|
||||
] = "Celsius",
|
||||
):
|
||||
"""A simple function to get the current weather for a location."""
|
||||
return f"Weather report for {city}: 20 {unit}, sunny"
|
||||
|
||||
|
||||
print(get_weather)
|
||||
```
|
||||
|
||||
```
|
||||
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
|
||||
parameters={
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
|
||||
'unit': {
|
||||
'type': 'string',
|
||||
'enum': ['Celsius', 'Fahrenheit'],
|
||||
'description': 'the unit for the temperature',
|
||||
'default': 'Celsius',
|
||||
},
|
||||
}
|
||||
},
|
||||
function=<function get_weather at 0x7f7b3a8a9b80>)
|
||||
```
|
||||
|
||||
### create_tool_from_function
|
||||
|
||||
The `create_tool_from_function` method provides more flexibility than the`@tool` decorator and allows setting Tool name and description. It infers the Tool parameters automatically and generates a JSON schema automatically in the same way as the `@tool` decorator.
|
||||
|
||||
```python
|
||||
from typing import Annotated, Literal
|
||||
from haystack.tools import create_tool_from_function
|
||||
|
||||
|
||||
def get_weather(
|
||||
city: Annotated[str, "the city for which to get the weather"] = "Munich",
|
||||
unit: Annotated[
|
||||
Literal["Celsius", "Fahrenheit"],
|
||||
"the unit for the temperature",
|
||||
] = "Celsius",
|
||||
):
|
||||
"""A simple function to get the current weather for a location."""
|
||||
return f"Weather report for {city}: 20 {unit}, sunny"
|
||||
|
||||
|
||||
tool = create_tool_from_function(get_weather)
|
||||
|
||||
print(tool)
|
||||
```
|
||||
|
||||
```
|
||||
Tool(name='get_weather', description='A simple function to get the current weather for a location.',
|
||||
parameters={
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'city': {'type': 'string', 'description': 'the city for which to get the weather', 'default': 'Munich'},
|
||||
'unit': {
|
||||
'type': 'string',
|
||||
'enum': ['Celsius', 'Fahrenheit'],
|
||||
'description': 'the unit for the temperature',
|
||||
'default': 'Celsius',
|
||||
},
|
||||
}
|
||||
},
|
||||
function=<function get_weather at 0x7f7b3a8a9b80>)
|
||||
```
|
||||
|
||||
## Toolset
|
||||
|
||||
A Toolset groups multiple Tool instances into a single manageable unit. It simplifies the passing of tools to components like Chat Generators or `ToolInvoker`, and supports filtering, serialization, and reuse.
|
||||
|
||||
```python
|
||||
from haystack.tools import Toolset
|
||||
|
||||
math_toolset = Toolset([add_tool, subtract_tool])
|
||||
```
|
||||
|
||||
See more details and examples on the [Toolset documentation page](toolset.mdx).
|
||||
|
||||
## Usage
|
||||
|
||||
To better understand this section, make sure you are also familiar with Haystack's [`ChatMessage`](../concepts/data-classes/chatmessage.mdx) data class.
|
||||
|
||||
### Passing Tools to a Chat Generator
|
||||
|
||||
Using the `tools` parameter, you can pass tools as a list of Tool instances or a single Toolset during initialization or in the `run` method. Tools passed at runtime override those set at initialization.
|
||||
|
||||
:::note[Chat Generators support]
|
||||
|
||||
Not all Chat Generators currently support tools, but we are actively expanding tool support across more models.
|
||||
|
||||
Look out for the `tools` parameter in a specific Chat Generator's `__init__` and `run` methods.
|
||||
:::
|
||||
|
||||
```python
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
|
||||
## Initialize the Chat Generator with the addition tool
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[add_tool])
|
||||
|
||||
## here we expect the Tool to be invoked
|
||||
res = chat_generator.run([ChatMessage.from_user("10 + 238")])
|
||||
print(res)
|
||||
|
||||
## here the model can respond without using the Tool
|
||||
res = chat_generator.run([ChatMessage.from_user("What is the habitat of a lion?")])
|
||||
print(res)
|
||||
```
|
||||
|
||||
```
|
||||
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
|
||||
_content=[ToolCall(tool_name='addition_tool',
|
||||
arguments={'a':10, 'b':238},
|
||||
id='call_rbYtbCdW0UbWMfy2x0sgF1Ap'
|
||||
)],
|
||||
_meta={...})]}
|
||||
|
||||
|
||||
{'replies':[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>,
|
||||
_content=[TextContent(text='Lions primarily inhabit grasslands, savannas, and open woodlands. ...'
|
||||
)],
|
||||
_meta={...})]}
|
||||
```
|
||||
|
||||
The same result of the previous run can be achieved by passing tools at runtime:
|
||||
|
||||
```python
|
||||
## Initialize the Chat Generator without tools
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini")
|
||||
|
||||
## pass tools in the run method
|
||||
res_w_tool_call = chat_generator.run(
|
||||
[ChatMessage.from_user("10 + 238")],
|
||||
tools=math_toolset,
|
||||
)
|
||||
print(res_w_tool_call)
|
||||
```
|
||||
|
||||
### Executing Tool Calls
|
||||
|
||||
To execute prepared tool calls, you can use the [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx) component. This component acts as the execution engine for tools, processing the calls prepared by the Language Model.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```python
|
||||
import random
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.tools import Tool
|
||||
|
||||
## Define a dummy weather toolimport random
|
||||
|
||||
|
||||
def dummy_weather(location: str):
|
||||
return {
|
||||
"temp": f"{random.randint(-10, 40)} °C",
|
||||
"humidity": f"{random.randint(0, 100)}%",
|
||||
}
|
||||
|
||||
|
||||
weather_tool = Tool(
|
||||
name="weather",
|
||||
description="A tool to get the weather",
|
||||
function=dummy_weather,
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"location": {"type": "string"}},
|
||||
"required": ["location"],
|
||||
},
|
||||
)
|
||||
|
||||
## Initialize the Chat Generator with the weather tool
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
|
||||
|
||||
## Initialize the Tool Invoker with the weather tool
|
||||
tool_invoker = ToolInvoker(tools=[weather_tool])
|
||||
|
||||
user_message = ChatMessage.from_user("What is the weather in Berlin?")
|
||||
|
||||
replies = chat_generator.run(messages=[user_message])["replies"]
|
||||
print(f"assistant messages: {replies}")
|
||||
|
||||
## If the assistant message contains a tool call, run the tool invoker
|
||||
if replies[0].tool_calls:
|
||||
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
|
||||
print(f"tool messages: {tool_messages}")
|
||||
```
|
||||
|
||||
```
|
||||
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
|
||||
arguments={'location': 'Berlin'}, id='call_YEvCEAmlvc42JGXV84NU8wtV')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||||
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
|
||||
63}})]
|
||||
|
||||
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '22 °C',
|
||||
'humidity': '35%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
|
||||
id='call_YEvCEAmlvc42JGXV84NU8wtV'), error=False)], _meta={})]
|
||||
```
|
||||
|
||||
### Processing Tool Results with the Chat Generator
|
||||
|
||||
In some cases, the raw output from a tool may not be immediately suitable for the end user.
|
||||
|
||||
You can refine the tool’s response by passing it back to the Chat Generator. This generates a user-friendly and conversational message.
|
||||
|
||||
In this example, we’ll pass the tool’s response back to the Chat Generator for final processing:
|
||||
|
||||
```python
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.tools import Tool
|
||||
|
||||
## Define a dummy weather toolimport random
|
||||
|
||||
|
||||
def dummy_weather(location: str):
|
||||
return {
|
||||
"temp": f"{random.randint(-10, 40)} °C",
|
||||
"humidity": f"{random.randint(0, 100)}%",
|
||||
}
|
||||
|
||||
|
||||
weather_tool = Tool(
|
||||
name="weather",
|
||||
description="A tool to get the weather",
|
||||
function=dummy_weather,
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"location": {"type": "string"}},
|
||||
"required": ["location"],
|
||||
},
|
||||
)
|
||||
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=[weather_tool])
|
||||
tool_invoker = ToolInvoker(tools=[weather_tool])
|
||||
|
||||
user_message = ChatMessage.from_user("What is the weather in Berlin?")
|
||||
|
||||
replies = chat_generator.run(messages=[user_message])["replies"]
|
||||
print(f"assistant messages: {replies}")
|
||||
|
||||
if replies[0].tool_calls:
|
||||
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
|
||||
print(f"tool messages: {tool_messages}")
|
||||
# we pass all the messages to the Chat Generator
|
||||
messages = [user_message] + replies + tool_messages
|
||||
final_replies = chat_generator.run(messages=messages)["replies"]
|
||||
print(f"final assistant messages: {final_replies}")
|
||||
```
|
||||
|
||||
```
|
||||
assistant messages:[ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='weather',
|
||||
arguments={'location': 'Berlin'}, id='call_jHX0RCDHRKX7h8V9RrNs6apy')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||||
'index':0, 'finish_reason': 'tool_calls', 'usage':{'completion_tokens':13, 'prompt_tokens':50, 'total_tokens':
|
||||
63}})]
|
||||
|
||||
tool messages: [ChatMessage(_role=<ChatRole.TOOL: 'tool'>, _content=[ToolCallResult(result="{'temp': '2 °C',
|
||||
'humidity': '15%'}", origin=ToolCall(tool_name='weather', arguments={'location': 'Berlin'},
|
||||
id='call_jHX0RCDHRKX7h8V9RrNs6apy'), error=False)], _meta={})]
|
||||
|
||||
final assistant messages: [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[TextContent(text='The
|
||||
current weather in Berlin is 2 °C with a humidity level of 15%.')], _meta={'model': 'gpt-4o-mini-2024-07-18',
|
||||
'index': 0, 'finish_reason': 'stop', 'usage': {'completion_tokens': 19, 'prompt_tokens': 85, 'total_tokens':
|
||||
104}})]
|
||||
```
|
||||
|
||||
### Passing Tools to Agent
|
||||
|
||||
You can also use `Tool` with the [Agent](../pipeline-components/agents-1/agent.mdx) component. Internally, the `Agent` component includes a `ToolInvoker` and the ChatGenerator of your choice to execute tool calls and process tool results.
|
||||
|
||||
```python
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.tools.tool import Tool
|
||||
from haystack.components.agents import Agent
|
||||
from typing import List
|
||||
|
||||
|
||||
## Tool Function
|
||||
def calculate(expression: str) -> dict:
|
||||
try:
|
||||
result = eval(expression, {"__builtins__": {}})
|
||||
return {"result": result}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
## Tool Definition
|
||||
calculator_tool = Tool(
|
||||
name="calculator",
|
||||
description="Evaluate basic math expressions.",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
"type": "string",
|
||||
"description": "Math expression to evaluate",
|
||||
},
|
||||
},
|
||||
"required": ["expression"],
|
||||
},
|
||||
function=calculate,
|
||||
outputs_to_state={"calc_result": {"source": "result"}},
|
||||
)
|
||||
|
||||
## Agent Setup
|
||||
agent = Agent(
|
||||
chat_generator=OpenAIChatGenerator(),
|
||||
tools=[calculator_tool],
|
||||
exit_conditions=["calculator"],
|
||||
state_schema={
|
||||
"calc_result": {"type": int},
|
||||
},
|
||||
)
|
||||
|
||||
## Run the Agent
|
||||
agent.warm_up()
|
||||
response = agent.run(messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")])
|
||||
|
||||
## Output
|
||||
print(response["messages"])
|
||||
print("Calc Result:", response.get("calc_result"))
|
||||
```
|
||||
|
||||
## Additional References
|
||||
|
||||
🧑🍳 Cookbooks:
|
||||
|
||||
- [Build a GitHub Issue Resolver Agent](https://haystack.deepset.ai/cookbook/github_issue_resolver_agent)
|
||||
- [Newsletter Sending Agent with Haystack Tools](https://haystack.deepset.ai/cookbook/newsletter-agent)
|
||||
- [Create a Swarm of Agents](https://haystack.deepset.ai/cookbook/swarm)
|
||||
@@ -0,0 +1,205 @@
|
||||
---
|
||||
title: "Toolset"
|
||||
id: toolset
|
||||
slug: "/toolset"
|
||||
description: "Group multiple Tools into a single unit."
|
||||
---
|
||||
|
||||
# Toolset
|
||||
|
||||
Group multiple Tools into a single unit.
|
||||
|
||||
<div className="key-value-table">
|
||||
|
||||
| | |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------- |
|
||||
| **Mandatory init variables** | `tools`: A list of tools |
|
||||
| **API reference** | [Toolset](/reference/tools-api#toolset) |
|
||||
| **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/toolset.py |
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||
A `Toolset` groups multiple Tool instances into a single manageable unit. It simplifies passing tools to components like Chat Generators, [`ToolInvoker`](../pipeline-components/tools/toolinvoker.mdx), or [`Agent`](../pipeline-components/agents-1/agent.mdx), and supports filtering, serialization, and reuse.
|
||||
|
||||
Additionally, by subclassing `Toolset`, you can create implementations that dynamically load tools from external sources like OpenAPI URLs, MCP servers, or other resources.
|
||||
|
||||
### Initializing Toolset
|
||||
|
||||
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 haystack.tools import Tool, Toolset
|
||||
|
||||
|
||||
## Define math functions
|
||||
def add_numbers(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
|
||||
def subtract_numbers(a: int, b: int) -> int:
|
||||
return a - b
|
||||
|
||||
|
||||
## Create tools with proper schemas
|
||||
add_tool = Tool(
|
||||
name="add",
|
||||
description="Add two numbers",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
|
||||
"required": ["a", "b"],
|
||||
},
|
||||
function=add_numbers,
|
||||
)
|
||||
|
||||
subtract_tool = Tool(
|
||||
name="subtract",
|
||||
description="Subtract b from a",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
|
||||
"required": ["a", "b"],
|
||||
},
|
||||
function=subtract_numbers,
|
||||
)
|
||||
|
||||
## Create a toolset with the math tools
|
||||
math_toolset = Toolset([add_tool, subtract_tool])
|
||||
```
|
||||
|
||||
### Adding New Tools to Toolset
|
||||
|
||||
```python
|
||||
def multiply_numbers(a: int, b: int) -> int:
|
||||
return a * b
|
||||
|
||||
|
||||
multiply_tool = Tool(
|
||||
name="multiply",
|
||||
description="Multiply two numbers",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
|
||||
"required": ["a", "b"],
|
||||
},
|
||||
function=multiply_numbers,
|
||||
)
|
||||
|
||||
math_toolset.add(multiply_tool)
|
||||
|
||||
## or, you can merge toolsets together
|
||||
math_toolset.add(another_toolset)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can use `Toolset` wherever you can use Tools in Haystack.
|
||||
|
||||
### With ChatGenerator and ToolInvoker
|
||||
|
||||
```python
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
## Create a toolset with the math tools
|
||||
math_toolset = Toolset([add_tool, subtract_tool])
|
||||
|
||||
chat_generator = OpenAIChatGenerator(model="gpt-4o-mini", tools=math_toolset)
|
||||
|
||||
## Initialize the Tool Invoker with the weather tool
|
||||
tool_invoker = ToolInvoker(tools=math_toolset)
|
||||
|
||||
user_message = ChatMessage.from_user("What is 10 minus 5?")
|
||||
|
||||
replies = chat_generator.run(messages=[user_message])["replies"]
|
||||
print(f"assistant message: {replies}")
|
||||
|
||||
## If the assistant message contains a tool call, run the tool invoker
|
||||
if replies[0].tool_calls:
|
||||
tool_messages = tool_invoker.run(messages=replies)["tool_messages"]
|
||||
print(f"tool result: {tool_messages[0].tool_call_result.result}")
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
assistant message: [ChatMessage(_role=<ChatRole.ASSISTANT: 'assistant'>, _content=[ToolCall(tool_name='subtract', arguments={'a': 10, 'b': 5}, id='call_awGa5q7KtQ9BrMGPTj6IgEH1')], _name=None, _meta={'model': 'gpt-4o-mini-2024-07-18', 'index': 0, 'finish_reason': 'tool_calls', 'usage': {'completion_tokens': 18, 'prompt_tokens': 75, 'total_tokens': 93, 'completion_tokens_details': CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), 'prompt_tokens_details': PromptTokensDetails(audio_tokens=0, cached_tokens=0)}})]
|
||||
tool result: 5
|
||||
```
|
||||
|
||||
### In a Pipeline
|
||||
|
||||
```python
|
||||
from haystack import Pipeline
|
||||
from haystack.components.converters import OutputAdapter
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
from haystack.components.tools import ToolInvoker
|
||||
from haystack.dataclasses import ChatMessage
|
||||
|
||||
math_toolset = Toolset([add_tool, subtract_tool])
|
||||
|
||||
pipeline = Pipeline()
|
||||
pipeline.add_component(
|
||||
"llm",
|
||||
OpenAIChatGenerator(model="gpt-4o-mini", tools=math_toolset),
|
||||
)
|
||||
pipeline.add_component("tool_invoker", ToolInvoker(tools=math_toolset))
|
||||
pipeline.add_component(
|
||||
"adapter",
|
||||
OutputAdapter(
|
||||
template="{{ initial_msg + initial_tool_messages + tool_messages }}",
|
||||
output_type=list[ChatMessage],
|
||||
unsafe=True,
|
||||
),
|
||||
)
|
||||
pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
|
||||
pipeline.connect("llm.replies", "tool_invoker.messages")
|
||||
pipeline.connect("llm.replies", "adapter.initial_tool_messages")
|
||||
pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
|
||||
pipeline.connect("adapter.output", "response_llm.messages")
|
||||
|
||||
user_input = "What is 2+2?"
|
||||
user_input_msg = ChatMessage.from_user(text=user_input)
|
||||
|
||||
result = pipeline.run(
|
||||
{
|
||||
"llm": {"messages": [user_input_msg]},
|
||||
"adapter": {"initial_msg": [user_input_msg]},
|
||||
},
|
||||
)
|
||||
|
||||
print(result["response_llm"]["replies"][0].text)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
2 + 2 equals 4.
|
||||
```
|
||||
|
||||
### With the Agent
|
||||
|
||||
```python
|
||||
from haystack.components.agents import Agent
|
||||
from haystack.dataclasses import ChatMessage
|
||||
from haystack.components.generators.chat import OpenAIChatGenerator
|
||||
|
||||
agent = Agent(
|
||||
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
|
||||
tools=math_toolset,
|
||||
)
|
||||
|
||||
agent.warm_up()
|
||||
response = agent.run(messages=[ChatMessage.from_user("What is 4 + 2?")])
|
||||
|
||||
print(response["messages"][-1].text)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
4 + 2 equals 6.
|
||||
```
|
||||
Reference in New Issue
Block a user