{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d9d1a28b-c2a1-4246-b1c2-c58d6938f798",
   "metadata": {},
   "source": [
    "# Customer Support\n",
    "\n",
    "Here, we show an example of building a customer support chatbot.\n",
    "\n",
    "This customer support chatbot interacts with SQL database to answer questions.\n",
    "We will use a mock SQL database to get started: the [Chinook](https://www.sqlitetutorial.net/sqlite-sample-database/) database.\n",
    "This database is about sales from a music store: what songs and album exists, customer orders, things like that.\n",
    "\n",
    "This chatbot has two different states: \n",
    "1. Music: the user can inquire about different songs and albums present in the store\n",
    "2. Account: the user can ask questions about their account\n",
    "\n",
    "Under the hood, this is handled by two separate agents. \n",
    "Each has a specific prompt and tools related to their objective. \n",
    "There is also a generic agent who is responsible for routing between these two agents as needed.\n",
    "\n",
    "Note: This is a very simple example! For a more complete tutorial on building a customer support bot, check out the [Customer Support Tutorial](../customer-support/customer-support.ipynb) for more information."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35abc013-2613-4a49-a806-939dcf13ccf3",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U langgraph langchain-community langchain-openai scikit-learn"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9431e7f1-07fa-49d9-ac45-29613703dcc1",
   "metadata": {},
   "source": [
    "## Load the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3d1ef253-6b0c-4481-868c-e1fe84f2c8ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "\n",
    "url = \"https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db\"\n",
    "response = requests.get(url)\n",
    "\n",
    "with open(\"Chinook.db\", \"wb\") as file:\n",
    "    file.write(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "61f7ef9c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Album',\n",
       " 'Artist',\n",
       " 'Customer',\n",
       " 'Employee',\n",
       " 'Genre',\n",
       " 'Invoice',\n",
       " 'InvoiceLine',\n",
       " 'MediaType',\n",
       " 'Playlist',\n",
       " 'PlaylistTrack',\n",
       " 'Track']"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_community.utilities import SQLDatabase\n",
    "\n",
    "db = SQLDatabase.from_uri(\"sqlite:///Chinook.db\")\n",
    "db.get_usable_table_names()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cf668e4-8cb4-4de1-bc5e-c90284bf74bc",
   "metadata": {},
   "source": [
    "## Load an LLM\n",
    "\n",
    "We will load a language model to use.\n",
    "For this demo we will use OpenAI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d9ea4e80-30e6-4d46-b480-35f0be2fb055",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(temperature=0, streaming=True, model=\"gpt-4o\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73907422-7e05-431e-b06d-256c9ec1f6f6",
   "metadata": {},
   "source": [
    "## Load Other Modules\n",
    "\n",
    "Load other modules we will use.\n",
    "\n",
    "All of the tools our agents will use will be custom tools. As such, we will use the `@tool` decorator to create custom tools.\n",
    "\n",
    "We will pass in messages to the agent, so we load `HumanMessage` and `SystemMessage`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ea958e9f-ab1f-49b5-bd85-16332055297c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import HumanMessage, SystemMessage"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35271d4d-2a1c-41be-9359-a3a7c3fed3d9",
   "metadata": {},
   "source": [
    "## Define the Customer Agent\n",
    "\n",
    "This agent is responsible for looking up customer information.\n",
    "It will have a specific prompt as well a specific tool to look up information about that customer (after asking for their user id)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "975b039a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This tool is given to the agent to look up information about a customer\n",
    "def get_customer_info(customer_id: int):\n",
    "    \"\"\"Look up customer info given their ID. ALWAYS make sure you have the customer ID before invoking this.\"\"\"\n",
    "    return db.run(f\"SELECT * FROM Customer WHERE CustomerID = {customer_id};\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1d5fa446",
   "metadata": {},
   "outputs": [],
   "source": [
    "customer_prompt = \"\"\"Your job is to help a user update their profile.\n",
    "\n",
    "You only have certain tools you can use. These tools require specific input. If you don't know the required input, then ask the user for it.\n",
    "\n",
    "If you are unable to help the user, you can \"\"\"\n",
    "\n",
    "\n",
    "def get_customer_messages(messages):\n",
    "    return [SystemMessage(content=customer_prompt)] + messages\n",
    "\n",
    "\n",
    "customer_chain = get_customer_messages | model.bind_tools([get_customer_info])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "904a9485-3857-458e-8b9d-33bc33842bc9",
   "metadata": {},
   "source": [
    "## Define the Music Agent\n",
    "\n",
    "This agent is responsible for figuring out information about music. To do that, we will create a prompt and various tools for looking up information about music\n",
    "\n",
    "First, we will create indexes for looking up artists and track names.\n",
    "This will allow us to look up artists and tracks without having to spell their names exactly right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a8604a3b-b484-4b2b-a914-4236cb98c524",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.vectorstores import SKLearnVectorStore\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "\n",
    "artists = db._execute(\"select * from Artist\")\n",
    "songs = db._execute(\"select * from Track\")\n",
    "artist_retriever = SKLearnVectorStore.from_texts(\n",
    "    [a[\"Name\"] for a in artists], OpenAIEmbeddings(), metadatas=artists\n",
    ").as_retriever()\n",
    "song_retriever = SKLearnVectorStore.from_texts(\n",
    "    [a[\"Name\"] for a in songs], OpenAIEmbeddings(), metadatas=songs\n",
    ").as_retriever()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac7eb264-c572-4925-ad55-a1d52a18b1c0",
   "metadata": {},
   "source": [
    "First, let's create a tool for getting albums by artist."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0a2a2b74",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_albums_by_artist(artist):\n",
    "    \"\"\"Get albums by an artist (or similar artists).\"\"\"\n",
    "    docs = artist_retriever.get_relevant_documents(artist)\n",
    "    artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n",
    "    return db.run(\n",
    "        f\"SELECT Title, Name FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId WHERE Album.ArtistId in ({artist_ids});\",\n",
    "        include_columns=True,\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45e85066-f2fc-490e-992d-cd66c9cd6486",
   "metadata": {},
   "source": [
    "Next, lets create a tool for getting tracks by an artist"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "da533f50",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_tracks_by_artist(artist):\n",
    "    \"\"\"Get songs by an artist (or similar artists).\"\"\"\n",
    "    docs = artist_retriever.invoke(artist)\n",
    "    artist_ids = \", \".join([str(d.metadata[\"ArtistId\"]) for d in docs])\n",
    "    return db.run(\n",
    "        f\"SELECT Track.Name as SongName, Artist.Name as ArtistName FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId LEFT JOIN Track ON Track.AlbumId = Album.AlbumId WHERE Album.ArtistId in ({artist_ids});\",\n",
    "        include_columns=True,\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb0e50ab-b059-427c-924b-f8072d8db23c",
   "metadata": {},
   "source": [
    "Finally, let's create a tool for looking up songs by their name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b3c07010",
   "metadata": {},
   "outputs": [],
   "source": [
    "def check_for_songs(song_title):\n",
    "    \"\"\"Check if a song exists by its name.\"\"\"\n",
    "    return song_retriever.invoke(song_title)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88388ff8-38b5-4e4e-a24d-de8c3670bd2b",
   "metadata": {},
   "source": [
    "Create the chain to call the relevant tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "72a14d5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "song_system_message = \"\"\"Your job is to help a customer find any songs they are looking for. \n",
    "\n",
    "You only have certain tools you can use. If a customer asks you to look something up that you don't know how, politely tell them what you can help with.\n",
    "\n",
    "When looking up artists and songs, sometimes the artist/song will not be found. In that case, the tools will return information \\\n",
    "on similar songs and artists. This is intentional, it is not the tool messing up.\"\"\"\n",
    "\n",
    "\n",
    "def get_song_messages(messages):\n",
    "    return [SystemMessage(content=song_system_message)] + messages\n",
    "\n",
    "\n",
    "song_recc_chain = get_song_messages | model.bind_tools(\n",
    "    [get_albums_by_artist, get_tracks_by_artist, check_for_songs]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "cff15eb0-62c7-451d-a5f9-4576b24c879e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_aXa9rSRXvTCJabrMY6AqkSV8', 'function': {'arguments': '{\"artist\":\"Amy Winehouse\"}', 'name': 'get_tracks_by_artist'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ce0793330f'}, id='run-60633269-fd02-43e8-b434-28e3b8b69fb1-0', tool_calls=[{'name': 'get_tracks_by_artist', 'args': {'artist': 'Amy Winehouse'}, 'id': 'call_aXa9rSRXvTCJabrMY6AqkSV8'}])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "msgs = [HumanMessage(content=\"hi! can you help me find songs by amy whinehouse?\")]\n",
    "song_recc_chain.invoke(msgs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a42c293-0816-4f3c-b4a3-5b9f3a0665d1",
   "metadata": {},
   "source": [
    "## Define the Generic Agent\n",
    "\n",
    "We now define a generic agent that is responsible for handling initial inquiries and routing to the right sub agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "73e74268",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
    "from langchain_core.pydantic_v1 import BaseModel, Field\n",
    "\n",
    "\n",
    "class Router(BaseModel):\n",
    "    \"\"\"Call this if you are able to route the user to the appropriate representative.\"\"\"\n",
    "\n",
    "    choice: str = Field(description=\"should be one of: music, customer\")\n",
    "\n",
    "\n",
    "system_message = \"\"\"Your job is to help as a customer service representative for a music store.\n",
    "\n",
    "You should interact politely with customers to try to figure out how you can help. You can help in a few ways:\n",
    "\n",
    "- Updating user information: if a customer wants to update the information in the user database. Call the router with `customer`\n",
    "- Recommending music: if a customer wants to find some music or information about music. Call the router with `music`\n",
    "\n",
    "If the user is asking or wants to ask about updating or accessing their information, send them to that route.\n",
    "If the user is asking or wants to ask about music, send them to that route.\n",
    "Otherwise, respond.\"\"\"\n",
    "\n",
    "\n",
    "def get_messages(messages):\n",
    "    return [SystemMessage(content=system_message)] + messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ddf27314",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = get_messages | model.bind_tools([Router])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3c896f34",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_0aaFPPCWDiAoPyXQX2PS8TcJ', 'function': {'arguments': '{\"choice\":\"music\"}', 'name': 'Router'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ce0793330f'}, id='run-73d51d75-b7c5-49fe-b558-105ede7c75d1-0', tool_calls=[{'name': 'Router', 'args': {'choice': 'music'}, 'id': 'call_0aaFPPCWDiAoPyXQX2PS8TcJ'}])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "msgs = [HumanMessage(content=\"hi! can you help me find a good song?\")]\n",
    "chain.invoke(msgs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "40d86f59",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_Okla9DfMHIPs5TslS6KPaoBA', 'function': {'arguments': '{\"choice\":\"customer\"}', 'name': 'Router'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_4008e3b719'}, id='run-0d82d4f8-f4eb-4b16-add8-3e7fdffd6332-0', tool_calls=[{'name': 'Router', 'args': {'choice': 'customer'}, 'id': 'call_Okla9DfMHIPs5TslS6KPaoBA'}])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "msgs = [HumanMessage(content=\"hi! what's the email you have for me?\")]\n",
    "chain.invoke(msgs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "bd6ddd8b-7500-46a7-811d-3bcb937bda51",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage\n",
    "\n",
    "\n",
    "def add_name(message, name):\n",
    "    _dict = message.dict()\n",
    "    _dict[\"name\"] = name\n",
    "    return AIMessage(**_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "27494de5-8345-4c23-bc0e-81e0dd5d47d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "from langgraph.graph import END, START\n",
    "\n",
    "\n",
    "def _get_last_ai_message(messages):\n",
    "    for m in messages[::-1]:\n",
    "        if isinstance(m, AIMessage):\n",
    "            return m\n",
    "    return None\n",
    "\n",
    "\n",
    "def _is_tool_call(msg):\n",
    "    return hasattr(msg, \"additional_kwargs\") and \"tool_calls\" in msg.additional_kwargs\n",
    "\n",
    "\n",
    "def _route(messages):\n",
    "    last_message = messages[-1]\n",
    "    if isinstance(last_message, AIMessage):\n",
    "        if not last_message.tool_calls:\n",
    "            return END\n",
    "        else:\n",
    "            if last_message.name == \"general\":\n",
    "                if len(last_message.tool_calls) > 1:\n",
    "                    raise ValueError(\"Too many tools\")\n",
    "                return last_message.tool_calls[0][\"args\"][\"choice\"]\n",
    "            else:\n",
    "                return \"tools\"\n",
    "    last_m = _get_last_ai_message(messages)\n",
    "    if last_m is None:\n",
    "        return \"general\"\n",
    "    if last_m.name == \"music\":\n",
    "        return \"music\"\n",
    "    elif last_m.name == \"customer\":\n",
    "        return \"customer\"\n",
    "    else:\n",
    "        return \"general\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "8aec704a-46fe-4fb3-bdee-11c3bbffc370",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "tools = [get_albums_by_artist, get_tracks_by_artist, check_for_songs, get_customer_info]\n",
    "tool_node = ToolNode(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "4d5b75c6-73e0-4922-a765-a15be63f869e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def _filter_out_routes(messages):\n",
    "    ms = []\n",
    "    for m in messages:\n",
    "        if _is_tool_call(m):\n",
    "            if m.name == \"general\":\n",
    "                continue\n",
    "        ms.append(m)\n",
    "    return ms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "fd4dbf98-dbb3-411a-bad6-2bb334072aaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import partial\n",
    "\n",
    "general_node = _filter_out_routes | chain | partial(add_name, name=\"general\")\n",
    "music_node = _filter_out_routes | song_recc_chain | partial(add_name, name=\"music\")\n",
    "customer_node = _filter_out_routes | customer_chain | partial(add_name, name=\"customer\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "dcade924",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "from langgraph.graph import MessageGraph\n",
    "\n",
    "memory = MemorySaver()\n",
    "graph = MessageGraph()\n",
    "nodes = {\n",
    "    \"general\": \"general\",\n",
    "    \"music\": \"music\",\n",
    "    END: END,\n",
    "    \"tools\": \"tools\",\n",
    "    \"customer\": \"customer\",\n",
    "}\n",
    "# Define a new graph\n",
    "workflow = MessageGraph()\n",
    "workflow.add_node(\"general\", general_node)\n",
    "workflow.add_node(\"music\", music_node)\n",
    "workflow.add_node(\"customer\", customer_node)\n",
    "workflow.add_node(\"tools\", tool_node)\n",
    "workflow.add_conditional_edges(\"general\", _route, nodes)\n",
    "workflow.add_conditional_edges(\"tools\", _route, nodes)\n",
    "workflow.add_conditional_edges(\"music\", _route, nodes)\n",
    "workflow.add_conditional_edges(\"customer\", _route, nodes)\n",
    "workflow.add_conditional_edges(START, _route, nodes)\n",
    "graph = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "ac65d6d2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User (q/Q to quit):  what music do you have?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output from node 'general':\n",
      "---\n",
      "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_iste6NuKvZou8O9QudOectOU', 'function': {'arguments': '{\"choice\":\"music\"}', 'name': 'Router'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_4008e3b719'} name='general' id='run-9eb940ff-6592-43ae-aa34-22c3d630ac65-0' tool_calls=[{'name': 'Router', 'args': {'choice': 'music'}, 'id': 'call_iste6NuKvZou8O9QudOectOU'}]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'music':\n",
      "---\n",
      "content=\"I can help you find songs and albums by specific artists, or check if a particular song exists. Just let me know the name of the artist or song you're interested in!\" response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_4008e3b719'} name='music' id='run-91f560e7-ffa5-437f-afda-27490cbd1efe-0'\n",
      "\n",
      "---\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User (q/Q to quit):  how about shakira?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output from node 'general':\n",
      "---\n",
      "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_fH4oKyA3U9aQy3p31MYXv2VP', 'function': {'arguments': '{\"choice\":\"music\"}', 'name': 'Router'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_d576307f90'} name='general' id='run-6f2eee09-9e3e-4011-a045-196cc5baa1ee-0' tool_calls=[{'name': 'Router', 'args': {'choice': 'music'}, 'id': 'call_fH4oKyA3U9aQy3p31MYXv2VP'}]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'music':\n",
      "---\n",
      "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_qivZqsI8zQAqSDP2jsvHyR7T', 'function': {'arguments': '{\"artist\": \"Shakira\"}', 'name': 'get_albums_by_artist'}, 'type': 'function'}, {'index': 1, 'id': 'call_GER0B3vlAjxcvYOYq1NGlV4r', 'function': {'arguments': '{\"artist\": \"Shakira\"}', 'name': 'get_tracks_by_artist'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ce0793330f'} name='music' id='run-38e2eca8-771c-48a2-83be-023fd17fc6f6-0' tool_calls=[{'name': 'get_albums_by_artist', 'args': {'artist': 'Shakira'}, 'id': 'call_qivZqsI8zQAqSDP2jsvHyR7T'}, {'name': 'get_tracks_by_artist', 'args': {'artist': 'Shakira'}, 'id': 'call_GER0B3vlAjxcvYOYq1NGlV4r'}]\n",
      "\n",
      "---\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/wfh/code/lc/langchain/libs/core/langchain_core/_api/deprecation.py:139: LangChainDeprecationWarning: The method `BaseRetriever.get_relevant_documents` was deprecated in langchain-core 0.1.46 and will be removed in 0.3.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output from node 'tools':\n",
      "---\n",
      "[ToolMessage(content=\"[{'Title': 'Supernatural', 'Name': 'Santana'}, {'Title': 'Santana - As Years Go By', 'Name': 'Santana'}, {'Title': 'Santana Live', 'Name': 'Santana'}, {'Title': 'Lulu Santos - RCA 100 Anos De Música - Álbum 01', 'Name': 'Lulu Santos'}, {'Title': 'Lulu Santos - RCA 100 Anos De Música - Álbum 02', 'Name': 'Lulu Santos'}]\", name='get_albums_by_artist', id='14ad12f3-afa1-4375-a89d-e878babf2d95', tool_call_id='call_qivZqsI8zQAqSDP2jsvHyR7T'), ToolMessage(content='[{\\'SongName\\': \\'(Da Le) Yaleo\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Love Of My Life\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Put Your Lights On\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Africa Bamba\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Smooth\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Do You Like The Way\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Maria Maria\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Migra\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Corazon Espinado\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Wishing It Was\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'El Farol\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Primavera\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'The Calling\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Jingo\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'El Corazon Manda\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'La Puesta Del Sol\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Persuasion\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'As The Years Go by\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Soul Sacrifice\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Fried Neckbones And Home Fries\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Santana Jam\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Evil Ways\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \"We\\'ve Got To Get Together/Jingo\", \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Rock Me\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \"Just Ain\\'t Good Enough\", \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Funky Piano\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'The Way You Do To Mer\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Assim Caminha A Humanidade\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Um Pro Outro\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Casa\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Condição\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Satisfação\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Brumário\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Sábado À Noite\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'A Cura\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Atrás Do Trio Elétrico\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Tudo Bem\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Toda Forma De Amor\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Sereia\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Se Você Pensa\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Lá Vem O Sol (Here Comes The Sun)\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Honolulu\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Dancin´Days\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Aviso Aos Navegantes\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Hyperconectividade\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'O Descobridor Dos Sete Mares\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Um Certo Alguém\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Fullgás\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Aquilo\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Senta A Pua\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Ro-Que-Se-Da-Ne\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Tudo Igual\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Fogo De Palha\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Assaltaram A Gramática\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'O Último Romântico (Ao Vivo)\\', \\'ArtistName\\': \\'Lulu Santos\\'}]', name='get_tracks_by_artist', id='4ddcebc3-8e4e-42d4-8ae4-3ce3a62b548c', tool_call_id='call_GER0B3vlAjxcvYOYq1NGlV4r')]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'music':\n",
      "---\n",
      "content=\"It seems I couldn't find specific albums or songs by Shakira, but I did find some related artists and their works. Here are some albums and songs by Santana and Lulu Santos:\\n\\n### Albums:\\n- **Santana:**\\n  - Supernatural\\n  - Santana - As Years Go By\\n  - Santana Live\\n\\n- **Lulu Santos:**\\n  - Lulu Santos - RCA 100 Anos De Música - Álbum 01\\n  - Lulu Santos - RCA 100 Anos De Música - Álbum 02\\n\\n### Songs:\\n- **Santana:**\\n  - (Da Le) Yaleo\\n  - Love Of My Life\\n  - Put Your Lights On\\n  - Africa Bamba\\n  - Smooth\\n  - Maria Maria\\n  - Corazon Espinado\\n  - Jingo\\n  - Evil Ways\\n\\n- **Lulu Santos:**\\n  - Assim Caminha A Humanidade\\n  - Um Pro Outro\\n  - Casa\\n  - Condição\\n  - Satisfação\\n  - A Cura\\n  - Atrás Do Trio Elétrico\\n  - Toda Forma De Amor\\n  - Sereia\\n  - Se Você Pensa\\n\\nIf you have any other specific artists or songs in mind, feel free to let me know!\" response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_d576307f90'} name='music' id='run-3d90d7cc-e7f7-48dc-bffd-00765c3f5d13-0'\n",
      "\n",
      "---\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User (q/Q to quit):  hm cool\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output from node 'general':\n",
      "---\n",
      "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_Nc5d0TWNbpnVJFeYJdQFuFGd', 'function': {'arguments': '{\"choice\":\"music\"}', 'name': 'Router'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_4008e3b719'} name='general' id='run-19a3588e-f04c-48db-a54f-95d6b930ee3e-0' tool_calls=[{'name': 'Router', 'args': {'choice': 'music'}, 'id': 'call_Nc5d0TWNbpnVJFeYJdQFuFGd'}]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'music':\n",
      "---\n",
      "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_weMHkgu3GYaMwXZM6hnCZd0z', 'function': {'arguments': '{\"artist\": \"Shakira\"}', 'name': 'get_albums_by_artist'}, 'type': 'function'}, {'index': 1, 'id': 'call_rXlVbPiEHbt10CNDUJ5GA2ZQ', 'function': {'arguments': '{\"artist\": \"Shakira\"}', 'name': 'get_tracks_by_artist'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ce0793330f'} name='music' id='run-71bf8550-b5a8-423f-b463-83d9ff51391b-0' tool_calls=[{'name': 'get_albums_by_artist', 'args': {'artist': 'Shakira'}, 'id': 'call_weMHkgu3GYaMwXZM6hnCZd0z'}, {'name': 'get_tracks_by_artist', 'args': {'artist': 'Shakira'}, 'id': 'call_rXlVbPiEHbt10CNDUJ5GA2ZQ'}]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'tools':\n",
      "---\n",
      "[ToolMessage(content=\"[{'Title': 'Supernatural', 'Name': 'Santana'}, {'Title': 'Santana - As Years Go By', 'Name': 'Santana'}, {'Title': 'Santana Live', 'Name': 'Santana'}, {'Title': 'Lulu Santos - RCA 100 Anos De Música - Álbum 01', 'Name': 'Lulu Santos'}, {'Title': 'Lulu Santos - RCA 100 Anos De Música - Álbum 02', 'Name': 'Lulu Santos'}]\", name='get_albums_by_artist', id='52ada997-83f6-4500-b0a7-1104d4d38eb9', tool_call_id='call_weMHkgu3GYaMwXZM6hnCZd0z'), ToolMessage(content='[{\\'SongName\\': \\'(Da Le) Yaleo\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Love Of My Life\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Put Your Lights On\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Africa Bamba\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Smooth\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Do You Like The Way\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Maria Maria\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Migra\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Corazon Espinado\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Wishing It Was\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'El Farol\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Primavera\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'The Calling\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Jingo\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'El Corazon Manda\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'La Puesta Del Sol\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Persuasion\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'As The Years Go by\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Soul Sacrifice\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Fried Neckbones And Home Fries\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Santana Jam\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Evil Ways\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \"We\\'ve Got To Get Together/Jingo\", \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Rock Me\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \"Just Ain\\'t Good Enough\", \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Funky Piano\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'The Way You Do To Mer\\', \\'ArtistName\\': \\'Santana\\'}, {\\'SongName\\': \\'Assim Caminha A Humanidade\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Um Pro Outro\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Casa\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Condição\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Satisfação\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Brumário\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Sábado À Noite\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'A Cura\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Atrás Do Trio Elétrico\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Tudo Bem\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Toda Forma De Amor\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Sereia\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Se Você Pensa\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Lá Vem O Sol (Here Comes The Sun)\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Honolulu\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Dancin´Days\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Aviso Aos Navegantes\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Hyperconectividade\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'O Descobridor Dos Sete Mares\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Um Certo Alguém\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Fullgás\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Aquilo\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Senta A Pua\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Ro-Que-Se-Da-Ne\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Tudo Igual\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Fogo De Palha\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'Assaltaram A Gramática\\', \\'ArtistName\\': \\'Lulu Santos\\'}, {\\'SongName\\': \\'O Último Romântico (Ao Vivo)\\', \\'ArtistName\\': \\'Lulu Santos\\'}]', name='get_tracks_by_artist', id='7583e077-2fb4-44bf-bb00-b89d599ae84d', tool_call_id='call_rXlVbPiEHbt10CNDUJ5GA2ZQ')]\n",
      "\n",
      "---\n",
      "\n",
      "Output from node 'music':\n",
      "---\n",
      "content=\"It seems like I couldn't find specific albums or songs by Shakira. However, I did find some related artists and their works. Here are some albums and songs by Santana and Lulu Santos:\\n\\n### Albums:\\n1. **Santana**\\n   - Supernatural\\n   - Santana - As Years Go By\\n   - Santana Live\\n\\n2. **Lulu Santos**\\n   - RCA 100 Anos De Música - Álbum 01\\n   - RCA 100 Anos De Música - Álbum 02\\n\\n### Songs:\\n1. **Santana**\\n   - (Da Le) Yaleo\\n   - Love Of My Life\\n   - Put Your Lights On\\n   - Africa Bamba\\n   - Smooth\\n   - Maria Maria\\n   - Corazon Espinado\\n   - Jingo\\n   - Evil Ways\\n\\n2. **Lulu Santos**\\n   - Assim Caminha A Humanidade\\n   - Um Pro Outro\\n   - Casa\\n   - Condição\\n   - Satisfação\\n   - A Cura\\n   - Atrás Do Trio Elétrico\\n   - Toda Forma De Amor\\n   - Sereia\\n\\nIf you have any other artists or songs in mind, feel free to let me know!\" response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_d576307f90'} name='music' id='run-2418b45b-2762-49ca-b166-a0fee309ed9e-0'\n",
      "\n",
      "---\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User (q/Q to quit):  q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Byebye\n"
     ]
    }
   ],
   "source": [
    "import uuid\n",
    "\n",
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "from langgraph.graph.graph import START\n",
    "\n",
    "history = []\n",
    "while True:\n",
    "    user = input(\"User (q/Q to quit): \")\n",
    "    if user in {\"q\", \"Q\"}:\n",
    "        print(\"AI: Byebye\")\n",
    "        break\n",
    "    history.append(HumanMessage(content=user))\n",
    "    async for output in graph.astream(history):\n",
    "        for key, value in output.items():\n",
    "            print(f\"Output from node '{key}':\")\n",
    "            print(\"---\")\n",
    "            print(value)\n",
    "        print(\"\\n---\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
