mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
37 KiB
37 KiB
In [ ]:
%%capture --no-stderr
%pip install -U langgraph langchain-community langchain-openai scikit-learnIn [1]:
import requests
url = "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db"
response = requests.get(url)
with open("Chinook.db", "wb") as file:
file.write(response.content)In [2]:
from langchain_community.utilities import SQLDatabase
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
db.get_usable_table_names()Out [2]:
['Album', 'Artist', 'Customer', 'Employee', 'Genre', 'Invoice', 'InvoiceLine', 'MediaType', 'Playlist', 'PlaylistTrack', 'Track']
In [3]:
from langchain_openai import ChatOpenAI
model = ChatOpenAI(temperature=0, streaming=True, model="gpt-4o")In [4]:
from langchain_core.messages import HumanMessage, SystemMessageIn [5]:
# This tool is given to the agent to look up information about a customer
def get_customer_info(customer_id: int):
"""Look up customer info given their ID. ALWAYS make sure you have the customer ID before invoking this."""
return db.run(f"SELECT * FROM Customer WHERE CustomerID = {customer_id};")In [6]:
customer_prompt = """Your job is to help a user update their profile.
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.
If you are unable to help the user, you can """
def get_customer_messages(messages):
return [SystemMessage(content=customer_prompt)] + messages
customer_chain = get_customer_messages | model.bind_tools([get_customer_info])In [7]:
from langchain_community.vectorstores import SKLearnVectorStore
from langchain_openai import OpenAIEmbeddings
artists = db._execute("select * from Artist")
songs = db._execute("select * from Track")
artist_retriever = SKLearnVectorStore.from_texts(
[a["Name"] for a in artists], OpenAIEmbeddings(), metadatas=artists
).as_retriever()
song_retriever = SKLearnVectorStore.from_texts(
[a["Name"] for a in songs], OpenAIEmbeddings(), metadatas=songs
).as_retriever()In [8]:
def get_albums_by_artist(artist):
"""Get albums by an artist (or similar artists)."""
docs = artist_retriever.get_relevant_documents(artist)
artist_ids = ", ".join([str(d.metadata["ArtistId"]) for d in docs])
return db.run(
f"SELECT Title, Name FROM Album LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId WHERE Album.ArtistId in ({artist_ids});",
include_columns=True,
)In [9]:
def get_tracks_by_artist(artist):
"""Get songs by an artist (or similar artists)."""
docs = artist_retriever.invoke(artist)
artist_ids = ", ".join([str(d.metadata["ArtistId"]) for d in docs])
return db.run(
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});",
include_columns=True,
)In [10]:
def check_for_songs(song_title):
"""Check if a song exists by its name."""
return song_retriever.invoke(song_title)In [11]:
song_system_message = """Your job is to help a customer find any songs they are looking for.
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.
When looking up artists and songs, sometimes the artist/song will not be found. In that case, the tools will return information \
on similar songs and artists. This is intentional, it is not the tool messing up."""
def get_song_messages(messages):
return [SystemMessage(content=song_system_message)] + messages
song_recc_chain = get_song_messages | model.bind_tools(
[get_albums_by_artist, get_tracks_by_artist, check_for_songs]
)In [12]:
msgs = [HumanMessage(content="hi! can you help me find songs by amy whinehouse?")]
song_recc_chain.invoke(msgs)Out [12]:
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'}])In [13]:
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.pydantic_v1 import BaseModel, Field
class Router(BaseModel):
"""Call this if you are able to route the user to the appropriate representative."""
choice: str = Field(description="should be one of: music, customer")
system_message = """Your job is to help as a customer service representative for a music store.
You should interact politely with customers to try to figure out how you can help. You can help in a few ways:
- Updating user information: if a customer wants to update the information in the user database. Call the router with `customer`
- Recommending music: if a customer wants to find some music or information about music. Call the router with `music`
If the user is asking or wants to ask about updating or accessing their information, send them to that route.
If the user is asking or wants to ask about music, send them to that route.
Otherwise, respond."""
def get_messages(messages):
return [SystemMessage(content=system_message)] + messagesIn [14]:
chain = get_messages | model.bind_tools([Router])In [15]:
msgs = [HumanMessage(content="hi! can you help me find a good song?")]
chain.invoke(msgs)Out [15]:
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'}])In [16]:
msgs = [HumanMessage(content="hi! what's the email you have for me?")]
chain.invoke(msgs)Out [16]:
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'}])In [17]:
from langchain_core.messages import AIMessage
def add_name(message, name):
_dict = message.dict()
_dict["name"] = name
return AIMessage(**_dict)In [18]:
import json
from langgraph.graph import END
def _get_last_ai_message(messages):
for m in messages[::-1]:
if isinstance(m, AIMessage):
return m
return None
def _is_tool_call(msg):
return hasattr(msg, "additional_kwargs") and "tool_calls" in msg.additional_kwargs
def _route(messages):
last_message = messages[-1]
if isinstance(last_message, AIMessage):
if not last_message.tool_calls:
return END
else:
if last_message.name == "general":
if len(last_message.tool_calls) > 1:
raise ValueError("Too many tools")
return last_message.tool_calls[0]["args"]["choice"]
else:
return "tools"
last_m = _get_last_ai_message(messages)
if last_m is None:
return "general"
if last_m.name == "music":
return "music"
elif last_m.name == "customer":
return "customer"
else:
return "general"In [19]:
from langgraph.prebuilt import ToolNode
tools = [get_albums_by_artist, get_tracks_by_artist, check_for_songs, get_customer_info]
tool_node = ToolNode(tools)In [20]:
def _filter_out_routes(messages):
ms = []
for m in messages:
if _is_tool_call(m):
if m.name == "general":
continue
ms.append(m)
return msIn [21]:
from functools import partial
general_node = _filter_out_routes | chain | partial(add_name, name="general")
music_node = _filter_out_routes | song_recc_chain | partial(add_name, name="music")
customer_node = _filter_out_routes | customer_chain | partial(add_name, name="customer")In [22]:
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import MessageGraph
memory = SqliteSaver.from_conn_string(":memory:")
graph = MessageGraph()
nodes = {
"general": "general",
"music": "music",
END: END,
"tools": "tools",
"customer": "customer",
}
# Define a new graph
workflow = MessageGraph()
workflow.add_node("general", general_node)
workflow.add_node("music", music_node)
workflow.add_node("customer", customer_node)
workflow.add_node("tools", tool_node)
workflow.add_conditional_edges("general", _route, nodes)
workflow.add_conditional_edges("tools", _route, nodes)
workflow.add_conditional_edges("music", _route, nodes)
workflow.add_conditional_edges("customer", _route, nodes)
workflow.set_conditional_entry_point(_route, nodes)
graph = workflow.compile()In [23]:
import uuid
from langchain_core.messages import HumanMessage
from langgraph.graph.graph import START
history = []
while True:
user = input("User (q/Q to quit): ")
if user in {"q", "Q"}:
print("AI: Byebye")
break
history.append(HumanMessage(content=user))
async for output in graph.astream(history):
for key, value in output.items():
print(f"Output from node '{key}':")
print("---")
print(value)
print("\n---\n")User (q/Q to quit): what music do you have?
Output from node 'general':
---
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'}]
---
Output from node 'music':
---
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'
---
User (q/Q to quit): how about shakira?
Output from node 'general':
---
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'}]
---
Output from node 'music':
---
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'}]
---
/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. warn_deprecated(
Output from node 'tools':
---
[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')]
---
Output from node 'music':
---
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'
---
User (q/Q to quit): hm cool
Output from node 'general':
---
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'}]
---
Output from node 'music':
---
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'}]
---
Output from node 'tools':
---
[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')]
---
Output from node 'music':
---
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'
---
User (q/Q to quit): q
AI: Byebye