diff --git a/index.toml b/index.toml
index 5a501b3..0ab71d8 100644
--- a/index.toml
+++ b/index.toml
@@ -265,4 +265,5 @@ title = "Newsletter Sending Agent with Experimental Haystack Tools"
notebook = "newsletter-agent.ipynb"
new = true
experimental = true
-topics = ["Function Calling", "Chat", "Agents"]
\ No newline at end of file
+topics = ["Function Calling", "Chat", "Agents"]
+discuss = "https://github.com/deepset-ai/haystack-experimental/discussions/98"
\ No newline at end of file
diff --git a/notebooks/newsletter-agent.ipynb b/notebooks/newsletter-agent.ipynb
index 4ef889f..da26b5f 100644
--- a/notebooks/newsletter-agent.ipynb
+++ b/notebooks/newsletter-agent.ipynb
@@ -1,38 +1,35 @@
{
- "nbformat": 4,
- "nbformat_minor": 0,
- "metadata": {
- "colab": {
- "provenance": []
- },
- "kernelspec": {
- "name": "python3",
- "display_name": "Python 3"
- },
- "language_info": {
- "name": "python"
- }
- },
"cells": [
{
"cell_type": "markdown",
- "source": [
- "# ๐๏ธ Newsletter Agent with Haystack Tools\n",
- "\n",
- "๐งโ๐ณ **Demo by Stefano Fiorucci ([X](https://x.com/theanakin87), [LinkedIn](https://www.linkedin.com/in/stefano-fiorucci/)) and Tuana Celik([X](https://x.com/tuanacelik), [LinkedIn](https://www.linkedin.com/in/tuanacelik/))**"
- ],
"metadata": {
"id": "rkwAPsgBdnFf"
- }
+ },
+ "source": [
+ "# ๐๏ธ Newsletter Seding Agent with Experimental Haystack Tools\n",
+ "\n",
+ "๐งโ๐ณ **Demo by Stefano Fiorucci ([X](https://x.com/theanakin87), [LinkedIn](https://www.linkedin.com/in/stefano-fiorucci/)) and Tuana Celik([X](https://x.com/tuanacelik), [LinkedIn](https://www.linkedin.com/in/tuanacelik/))**\n",
+ "\n",
+ "In this recipe, we will build a newsletter sending agent with 3 tools:\n",
+ "- A tool that fetches the top stories from Hacker News\n",
+ "- A tool that creates newsletters for a particular audience\n",
+ "- A tool that can send emails (with Gmail)\n",
+ "\n",
+ "At time of writing (16 October 2024) we are using the `Tool` datatypes and components from the **haystack-exerimental** package.\n",
+ "\n",
+ "## ๐บ Watch Along\n",
+ "\n",
+ ""
+ ]
},
{
"cell_type": "markdown",
- "source": [
- "### Install dependencies"
- ],
"metadata": {
"id": "UVEseyMhnfu_"
- }
+ },
+ "source": [
+ "### Install dependencies"
+ ]
},
{
"cell_type": "code",
@@ -47,6 +44,9 @@
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "q7eXbW9S0lrt"
+ },
"source": [
"#### **Experimental Features**\n",
"\n",
@@ -58,13 +58,15 @@
"The experimantal components and extensions we are using here are:\n",
"- The `Tool` and the extended `ChatMessage` datatypes.\n",
"- The `ToolInvoker` component."
- ],
- "metadata": {
- "id": "q7eXbW9S0lrt"
- }
+ ]
},
{
"cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "k0qnuVES2p4t"
+ },
+ "outputs": [],
"source": [
"from typing import List\n",
"from trafilatura import fetch_url, extract\n",
@@ -79,15 +81,13 @@
"from haystack import Pipeline\n",
"from haystack.components.builders import PromptBuilder\n",
"from haystack.components.generators import OpenAIGenerator"
- ],
- "metadata": {
- "id": "k0qnuVES2p4t"
- },
- "execution_count": null,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "uHElH_u323kd"
+ },
"source": [
"## Hacker News Fetcher Tool\n",
"\n",
@@ -98,13 +98,15 @@
"๐ [Hacker News Summaries with Custom Components](https://haystack.deepset.ai/cookbook/hackernews-custom-component-rag?utm_campaign=developer-relations&utm_source=tools-livestream)\n",
"\n",
"This tool expects `top_k` as input, and returns that many of the _current_ top stories on Hacker News ๐"
- ],
- "metadata": {
- "id": "uHElH_u323kd"
- }
+ ]
},
{
"cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "id": "8SrNAWvA27D3"
+ },
+ "outputs": [],
"source": [
"def hacker_news_fetcher(top_k: int = 3):\n",
" newest_list = requests.get(url='https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty')\n",
@@ -128,15 +130,15 @@
" print(f\"Couldn't download {url}, skipped\")\n",
"\n",
" return articles"
- ],
- "metadata": {
- "id": "8SrNAWvA27D3"
- },
- "execution_count": 27,
- "outputs": []
+ ]
},
{
"cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "id": "_mmDM7mE3GtG"
+ },
+ "outputs": [],
"source": [
"hacker_news_fetcher_tool = Tool(name=\"hacker_news_fetcher\",\n",
" description=\"Fetch the top k articles from hacker news\",\n",
@@ -150,15 +152,13 @@
" }\n",
" },\n",
" })"
- ],
- "metadata": {
- "id": "_mmDM7mE3GtG"
- },
- "execution_count": 28,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "-qcpJPsb3MFe"
+ },
"source": [
"## Newsletter generation Pipeline and Tool\n",
"\n",
@@ -169,17 +169,11 @@
"- `articles`: Content to base the newsletter off of\n",
"- `target_people`: The audience we want to target, for example \"engineers\" may be our target audience\n",
"- `n_words`: The number of words we want to limit our newsletter to"
- ],
- "metadata": {
- "id": "-qcpJPsb3MFe"
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "if not \"OPENAI_API_KEY\" in os.environ:\n",
- " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key: \")"
- ],
+ "execution_count": 5,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -187,7 +181,6 @@
"id": "SIHSxnol3a3o",
"outputId": "e2789bf0-7b09-4ba9-e459-5688b9775739"
},
- "execution_count": 5,
"outputs": [
{
"name": "stdout",
@@ -196,27 +189,15 @@
"Enter your OpenAI API key: ยทยทยทยทยทยทยทยทยทยท\n"
]
}
+ ],
+ "source": [
+ "if not \"OPENAI_API_KEY\" in os.environ:\n",
+ " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key: \")"
]
},
{
"cell_type": "code",
- "source": [
- "template = \"\"\"\n",
- "Create a entertaining newsletter for {{target_people}} based on the following articles.\n",
- "The newsletter should be well structured, with a unique angle and a maximum of {{n_words}} words.\n",
- "\n",
- "Articles:\n",
- "{% for article in articles %}\n",
- " {{ article }}\n",
- " ---\n",
- "{% endfor %}\n",
- "\"\"\"\n",
- "\n",
- "newsletter_pipe = Pipeline()\n",
- "newsletter_pipe.add_component(\"prompt_builder\", PromptBuilder(template=template))\n",
- "newsletter_pipe.add_component(\"llm\", OpenAIGenerator(model=\"gpt-4o-mini\"))\n",
- "newsletter_pipe.connect(\"prompt_builder\", \"llm\")"
- ],
+ "execution_count": 29,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -224,10 +205,8 @@
"id": "NhfQOF4v3KJu",
"outputId": "7804c046-c3b4-4872-b26b-fad0eb187628"
},
- "execution_count": 29,
"outputs": [
{
- "output_type": "execute_result",
"data": {
"text/plain": [
"\n",
@@ -238,13 +217,36 @@
" - prompt_builder.prompt -> llm.prompt (str)"
]
},
+ "execution_count": 29,
"metadata": {},
- "execution_count": 29
+ "output_type": "execute_result"
}
+ ],
+ "source": [
+ "template = \"\"\"\n",
+ "Create a entertaining newsletter for {{target_people}} based on the following articles.\n",
+ "The newsletter should be well structured, with a unique angle and a maximum of {{n_words}} words.\n",
+ "\n",
+ "Articles:\n",
+ "{% for article in articles %}\n",
+ " {{ article }}\n",
+ " ---\n",
+ "{% endfor %}\n",
+ "\"\"\"\n",
+ "\n",
+ "newsletter_pipe = Pipeline()\n",
+ "newsletter_pipe.add_component(\"prompt_builder\", PromptBuilder(template=template))\n",
+ "newsletter_pipe.add_component(\"llm\", OpenAIGenerator(model=\"gpt-4o-mini\"))\n",
+ "newsletter_pipe.connect(\"prompt_builder\", \"llm\")"
]
},
{
"cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "id": "8YsxYyP83SL9"
+ },
+ "outputs": [],
"source": [
"def newsletter_pipeline_func(articles: List[str], target_people: str = \"programmers\", n_words: int = 100):\n",
" result = newsletter_pipe.run({\"prompt_builder\": {\"articles\": articles, \"target_people\": target_people, \"n_words\": n_words}})\n",
@@ -275,15 +277,13 @@
" },\n",
" \"required\": [\"articles\"],\n",
" })"
- ],
- "metadata": {
- "id": "8YsxYyP83SL9"
- },
- "execution_count": 30,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "qNJLc2te1BOi"
+ },
"source": [
"## Send Email Tool\n",
"\n",
@@ -292,13 +292,15 @@
"> โ ๏ธ Note: To be able to use the gmail too, you have to create an app password for your Gmail account, which will be the sender. You can delete this after.\n",
"\n",
"To configure our `email` Tool, you have to provide the following information about the sender email account ๐"
- ],
- "metadata": {
- "id": "qNJLc2te1BOi"
- }
+ ]
},
{
"cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "id": "6btTAlL31DmE"
+ },
+ "outputs": [],
"source": [
"if not \"NAME\" in os.environ:\n",
" os.environ[\"NAME\"] = input(\"What's your name? \")\n",
@@ -306,27 +308,27 @@
" os.environ[\"SENDER_EMAIL\"] = getpass(\"Enter your Gmail e-mail: \")\n",
"if not \"GMAIL_APP_PASSWORD\" in os.environ:\n",
" os.environ[\"GMAIL_APP_PASSWORD\"] = getpass(\"Enter your Gmail App Password: \")"
- ],
- "metadata": {
- "id": "6btTAlL31DmE"
- },
- "execution_count": 31,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "PYZs8Wjbq6Gy"
+ },
"source": [
"Next, we create a `Tool` that expects the following input:\n",
"- `receiver`: The email address that we want to send an email to\n",
"- `body`: The body of the email\n",
"- `subject`: The subject line for the email."
- ],
- "metadata": {
- "id": "PYZs8Wjbq6Gy"
- }
+ ]
},
{
"cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "id": "DIYBOe5l1AL-"
+ },
+ "outputs": [],
"source": [
"import smtplib, ssl\n",
"from email.mime.text import MIMEText\n",
@@ -346,15 +348,15 @@
" server.login(sender_email, password)\n",
" server.sendmail(sender_email, receiver, msg.as_string())\n",
" return 'Email sent!'"
- ],
- "metadata": {
- "id": "DIYBOe5l1AL-"
- },
- "execution_count": 32,
- "outputs": []
+ ]
},
{
"cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "id": "MrB2O_Yq3Dw0"
+ },
+ "outputs": [],
"source": [
"email_tool = Tool(name=\"email\",\n",
" description=\"Send emails with specific content\",\n",
@@ -376,75 +378,22 @@
" }\n",
" },\n",
" })"
- ],
- "metadata": {
- "id": "MrB2O_Yq3Dw0"
- },
- "execution_count": 33,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "K4qpD_nO3wol"
+ },
"source": [
"## Newsletter Sending Chat Agent\n",
"\n",
"Now, we build a Newsletter creating chat agent which we can use to ask for newsletters, as well as sending them to given email addresses."
- ],
- "metadata": {
- "id": "K4qpD_nO3wol"
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "chat_generator = OpenAIChatGenerator(tools=[hacker_news_fetcher_tool, newsletter_tool, email_tool])\n",
- "\n",
- "tool_invoker = ToolInvoker(tools=[hacker_news_fetcher_tool, newsletter_tool, email_tool])\n",
- "\n",
- "messages = [\n",
- " ChatMessage.from_system(\n",
- " \"\"\"Prepare a tool call if needed, otherwise use your knowledge to respond to the user.\n",
- " If the invocation of a tool requires the result of another tool, prepare only one call at a time.\n",
- "\n",
- " Each time you receive the result of a tool call, ask yourself: \"Am I done with the task?\".\n",
- " If not and you need to invoke another tool, prepare the next tool call.\n",
- " If you are done, respond with just the final result.\"\"\"\n",
- " )\n",
- " ]\n",
- "\n",
- "while True:\n",
- " user_input = input(\"\\n\\nwaiting for input (type 'exit' or 'quit' to stop)\\n๐ง: \")\n",
- " if user_input.lower() == \"exit\" or user_input.lower() == \"quit\":\n",
- " break\n",
- " messages.append(ChatMessage.from_user(user_input))\n",
- "\n",
- " while True:\n",
- " print(\"โ iterating...\")\n",
- "\n",
- " replies = chat_generator.run(messages=messages)[\"replies\"]\n",
- " messages.extend(replies)\n",
- "\n",
- " # Check for tool calls and handle them\n",
- " if not replies[0].tool_calls:\n",
- " break\n",
- " tool_calls = replies[0].tool_calls\n",
- "\n",
- " # Print tool calls for debugging\n",
- " for tc in tool_calls:\n",
- " print(\"\\n TOOL CALL:\")\n",
- " print(f\"\\t{tc.id}\")\n",
- " print(f\"\\t{tc.tool_name}\")\n",
- " for k,v in tc.arguments.items():\n",
- " v_truncated = str(v)[:50]\n",
- " print(f\"\\t{k}: {v_truncated}{'' if len(v_truncated) == len(str(v)) else '...'}\")\n",
- "\n",
- " tool_messages = tool_invoker.run(messages=replies)[\"tool_messages\"]\n",
- " messages.extend(tool_messages)\n",
- "\n",
- "\n",
- " # Print the final AI response after all tool calls are resolved\n",
- " print(f\"๐ค: {messages[-1].text}\")"
- ],
+ "execution_count": 34,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -452,7 +401,6 @@
"id": "Bg3K8r6f33fF",
"outputId": "817b509b-3f00-431c-9d96-d325d7e5bb49"
},
- "execution_count": 34,
"outputs": [
{
"name": "stdout",
@@ -535,62 +483,116 @@
"๐ง: exit\n"
]
}
+ ],
+ "source": [
+ "chat_generator = OpenAIChatGenerator(tools=[hacker_news_fetcher_tool, newsletter_tool, email_tool])\n",
+ "\n",
+ "tool_invoker = ToolInvoker(tools=[hacker_news_fetcher_tool, newsletter_tool, email_tool])\n",
+ "\n",
+ "messages = [\n",
+ " ChatMessage.from_system(\n",
+ " \"\"\"Prepare a tool call if needed, otherwise use your knowledge to respond to the user.\n",
+ " If the invocation of a tool requires the result of another tool, prepare only one call at a time.\n",
+ "\n",
+ " Each time you receive the result of a tool call, ask yourself: \"Am I done with the task?\".\n",
+ " If not and you need to invoke another tool, prepare the next tool call.\n",
+ " If you are done, respond with just the final result.\"\"\"\n",
+ " )\n",
+ " ]\n",
+ "\n",
+ "while True:\n",
+ " user_input = input(\"\\n\\nwaiting for input (type 'exit' or 'quit' to stop)\\n๐ง: \")\n",
+ " if user_input.lower() == \"exit\" or user_input.lower() == \"quit\":\n",
+ " break\n",
+ " messages.append(ChatMessage.from_user(user_input))\n",
+ "\n",
+ " while True:\n",
+ " print(\"โ iterating...\")\n",
+ "\n",
+ " replies = chat_generator.run(messages=messages)[\"replies\"]\n",
+ " messages.extend(replies)\n",
+ "\n",
+ " # Check for tool calls and handle them\n",
+ " if not replies[0].tool_calls:\n",
+ " break\n",
+ " tool_calls = replies[0].tool_calls\n",
+ "\n",
+ " # Print tool calls for debugging\n",
+ " for tc in tool_calls:\n",
+ " print(\"\\n TOOL CALL:\")\n",
+ " print(f\"\\t{tc.id}\")\n",
+ " print(f\"\\t{tc.tool_name}\")\n",
+ " for k,v in tc.arguments.items():\n",
+ " v_truncated = str(v)[:50]\n",
+ " print(f\"\\t{k}: {v_truncated}{'' if len(v_truncated) == len(str(v)) else '...'}\")\n",
+ "\n",
+ " tool_messages = tool_invoker.run(messages=replies)[\"tool_messages\"]\n",
+ " messages.extend(tool_messages)\n",
+ "\n",
+ "\n",
+ " # Print the final AI response after all tool calls are resolved\n",
+ " print(f\"๐ค: {messages[-1].text}\")"
]
},
{
"cell_type": "markdown",
- "source": [
- "## Extras: Converting Tools"
- ],
"metadata": {
"id": "-IbTLr8ueNRh"
- }
+ },
+ "source": [
+ "## Extras: Converting Tools"
+ ]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "65DYH0JvePeO"
+ },
"source": [
"### Convert functions into Tools\n",
"\n",
"This feature is not yet released, so we need to install `haystack-experimental` from main."
- ],
- "metadata": {
- "id": "65DYH0JvePeO"
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "! pip install git+https://github.com/deepset-ai/haystack-experimental@main#egg=haystack-experimental"
- ],
+ "execution_count": null,
"metadata": {
"id": "NTp-02mLeO2V"
},
- "execution_count": null,
- "outputs": []
+ "outputs": [],
+ "source": [
+ "! pip install git+https://github.com/deepset-ai/haystack-experimental@main#egg=haystack-experimental"
+ ]
},
{
"cell_type": "code",
- "source": [
- "from typing import Annotated\n",
- "from pprint import pp"
- ],
+ "execution_count": null,
"metadata": {
"id": "RCQJJb1Oesaf"
},
- "execution_count": null,
- "outputs": []
+ "outputs": [],
+ "source": [
+ "from typing import Annotated\n",
+ "from pprint import pp"
+ ]
},
{
"cell_type": "markdown",
- "source": [
- "Writing the JSON schema is not fun... ๐ค"
- ],
"metadata": {
"id": "xVX72INMhGCS"
- }
+ },
+ "source": [
+ "Writing the JSON schema is not fun... ๐ค"
+ ]
},
{
"cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "01FSg09De5bA"
+ },
+ "outputs": [],
"source": [
"def newsletter_pipeline_func(articles: List[str], target_people: str = \"programmers\", n_words: int = 100):\n",
" result = newsletter_pipe.run({\"prompt_builder\": {\"articles\": articles, \"target_people\": target_people, \"n_words\": n_words}})\n",
@@ -621,40 +623,20 @@
" },\n",
" \"required\": [\"articles\"],\n",
" })"
- ],
- "metadata": {
- "id": "01FSg09De5bA"
- },
- "execution_count": null,
- "outputs": []
+ ]
},
{
"cell_type": "markdown",
- "source": [
- "We can do this instead ๐"
- ],
"metadata": {
"id": "9laNlRiThU45"
- }
+ },
+ "source": [
+ "We can do this instead ๐"
+ ]
},
{
"cell_type": "code",
- "source": [
- "def newsletter_pipeline_func(\n",
- " articles: Annotated[List[str], \"The articles to include in the newsletter\"],\n",
- " target_people: Annotated[str, \"The target audience for the newsletter\"] = \"programmers\",\n",
- " n_words: Annotated[int, \"The number of words to summarize the newsletter to\"] = 100\n",
- " ):\n",
- " \"\"\"Generate a newsletter based on some articles\"\"\"\n",
- "\n",
- " result = newsletter_pipe.run({\"prompt_builder\": {\"articles\": articles, \"target_people\": target_people, \"n_words\": n_words}})\n",
- "\n",
- " return {\"reply\": result[\"llm\"][\"replies\"][0]}\n",
- "\n",
- "newsletter_tool = Tool.from_function(newsletter_pipeline_func)\n",
- "\n",
- "pp(newsletter_tool, width=200)"
- ],
+ "execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -662,11 +644,10 @@
"id": "Htmwg90cfZLO",
"outputId": "d4dc2485-8ddf-4309-c7d5-0e3823068108"
},
- "execution_count": null,
"outputs": [
{
- "output_type": "stream",
"name": "stdout",
+ "output_type": "stream",
"text": [
"Tool(name='newsletter_pipeline_func',\n",
" description='Generate a newsletter based on some articles',\n",
@@ -678,33 +659,54 @@
" function=)\n"
]
}
+ ],
+ "source": [
+ "def newsletter_pipeline_func(\n",
+ " articles: Annotated[List[str], \"The articles to include in the newsletter\"],\n",
+ " target_people: Annotated[str, \"The target audience for the newsletter\"] = \"programmers\",\n",
+ " n_words: Annotated[int, \"The number of words to summarize the newsletter to\"] = 100\n",
+ " ):\n",
+ " \"\"\"Generate a newsletter based on some articles\"\"\"\n",
+ "\n",
+ " result = newsletter_pipe.run({\"prompt_builder\": {\"articles\": articles, \"target_people\": target_people, \"n_words\": n_words}})\n",
+ "\n",
+ " return {\"reply\": result[\"llm\"][\"replies\"][0]}\n",
+ "\n",
+ "newsletter_tool = Tool.from_function(newsletter_pipeline_func)\n",
+ "\n",
+ "pp(newsletter_tool, width=200)"
]
},
{
"cell_type": "markdown",
+ "metadata": {
+ "id": "lnmBWMqQhm6f"
+ },
"source": [
"### Convert Pre-Existing Tools into Haystack Tools\n",
"\n",
"Haystack is quite flexible. This means if you have tools already defined elsewhere, you are able to convert them to Haystack tools. For example,\n",
" [LangChain has several interesting tools](https://python.langchain.com/docs/integrations/tools/) that we can seamlessly convert into Haystack tools.\n"
- ],
- "metadata": {
- "id": "lnmBWMqQhm6f"
- }
+ ]
},
{
"cell_type": "code",
- "source": [
- "!pip install langchain-community"
- ],
+ "execution_count": null,
"metadata": {
"id": "BCjAE8fyjhPg"
},
- "execution_count": null,
- "outputs": []
+ "outputs": [],
+ "source": [
+ "!pip install langchain-community"
+ ]
},
{
"cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "0vU0n12zjz2T"
+ },
+ "outputs": [],
"source": [
"from pydantic import create_model\n",
"from haystack_experimental.dataclasses.tool import _remove_title_from_schema\n",
@@ -737,22 +739,11 @@
" schema[\"properties\"][name][\"description\"] = description\n",
"\n",
" return Tool(name=tool_name, description=tool_description, parameters=schema, function=tool_function)"
- ],
- "metadata": {
- "id": "0vU0n12zjz2T"
- },
- "execution_count": null,
- "outputs": []
+ ]
},
{
"cell_type": "code",
- "source": [
- "from langchain_community.agent_toolkits import FileManagementToolkit\n",
- "toolkit = FileManagementToolkit(\n",
- " root_dir=\"/\"\n",
- ") # If you don't provide a root_dir, operations will default to the current working directory\n",
- "toolkit.get_tools()"
- ],
+ "execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -760,10 +751,8 @@
"id": "XbFgPSyXj6aD",
"outputId": "f78ac9f4-fbb7-438f-b461-2514d2a3715b"
},
- "execution_count": null,
"outputs": [
{
- "output_type": "execute_result",
"data": {
"text/plain": [
"[CopyFileTool(root_dir='/'),\n",
@@ -775,57 +764,44 @@
" ListDirectoryTool(root_dir='/')]"
]
},
+ "execution_count": 25,
"metadata": {},
- "execution_count": 25
+ "output_type": "execute_result"
}
+ ],
+ "source": [
+ "from langchain_community.agent_toolkits import FileManagementToolkit\n",
+ "toolkit = FileManagementToolkit(\n",
+ " root_dir=\"/\"\n",
+ ") # If you don't provide a root_dir, operations will default to the current working directory\n",
+ "toolkit.get_tools()"
]
},
{
"cell_type": "code",
- "source": [
- "langchain_listdir_tool = toolkit.get_tools()[-1]"
- ],
+ "execution_count": null,
"metadata": {
"id": "3fDROQavkoKo"
},
- "execution_count": null,
- "outputs": []
+ "outputs": [],
+ "source": [
+ "langchain_listdir_tool = toolkit.get_tools()[-1]"
+ ]
},
{
"cell_type": "code",
- "source": [
- "haystack_listdir_tool = convert_langchain_tool_to_haystack_tool(langchain_listdir_tool)"
- ],
+ "execution_count": null,
"metadata": {
"id": "oBI83cqYkuBY"
},
- "execution_count": null,
- "outputs": []
+ "outputs": [],
+ "source": [
+ "haystack_listdir_tool = convert_langchain_tool_to_haystack_tool(langchain_listdir_tool)"
+ ]
},
{
"cell_type": "code",
- "source": [
- "from haystack_experimental.components import OpenAIChatGenerator, ToolInvoker\n",
- "from haystack_experimental.dataclasses import ChatMessage\n",
- "\n",
- "chat_generator = OpenAIChatGenerator(model=\"gpt-4o-mini\", tools=[haystack_listdir_tool])\n",
- "tool_invoker = ToolInvoker(tools=[haystack_listdir_tool])\n",
- "\n",
- "user_message = ChatMessage.from_user(\"List the files in /content/sample_data\")\n",
- "\n",
- "replies = chat_generator.run(messages=[user_message])[\"replies\"]\n",
- "# print(f\"assistant messages: {replies}\")\n",
- "\n",
- "if replies[0].tool_calls:\n",
- "\n",
- " tool_messages = tool_invoker.run(messages=replies)[\"tool_messages\"]\n",
- " # print(f\"tool messages: {tool_messages}\")\n",
- "\n",
- " # we pass all the messages to the Chat Generator\n",
- " messages = [user_message] + replies + tool_messages\n",
- " final_replies = chat_generator.run(messages=messages)[\"replies\"]\n",
- " print(f\"{final_replies[0].text}\")"
- ],
+ "execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -833,11 +809,10 @@
"id": "DzyphA4NkxXY",
"outputId": "1d18566c-1a58-4eed-ce99-ed152a894d30"
},
- "execution_count": null,
"outputs": [
{
- "output_type": "stream",
"name": "stdout",
+ "output_type": "stream",
"text": [
"The files in the `/content/sample_data` directory are:\n",
"\n",
@@ -849,7 +824,43 @@
"6. mnist_test.csv\n"
]
}
+ ],
+ "source": [
+ "from haystack_experimental.components import OpenAIChatGenerator, ToolInvoker\n",
+ "from haystack_experimental.dataclasses import ChatMessage\n",
+ "\n",
+ "chat_generator = OpenAIChatGenerator(model=\"gpt-4o-mini\", tools=[haystack_listdir_tool])\n",
+ "tool_invoker = ToolInvoker(tools=[haystack_listdir_tool])\n",
+ "\n",
+ "user_message = ChatMessage.from_user(\"List the files in /content/sample_data\")\n",
+ "\n",
+ "replies = chat_generator.run(messages=[user_message])[\"replies\"]\n",
+ "# print(f\"assistant messages: {replies}\")\n",
+ "\n",
+ "if replies[0].tool_calls:\n",
+ "\n",
+ " tool_messages = tool_invoker.run(messages=replies)[\"tool_messages\"]\n",
+ " # print(f\"tool messages: {tool_messages}\")\n",
+ "\n",
+ " # we pass all the messages to the Chat Generator\n",
+ " messages = [user_message] + replies + tool_messages\n",
+ " final_replies = chat_generator.run(messages=messages)[\"replies\"]\n",
+ " print(f\"{final_replies[0].text}\")"
]
}
- ]
-}
\ No newline at end of file
+ ],
+ "metadata": {
+ "colab": {
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}