mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-21 07:02:25 +02:00
- fix bug when using at=END_OF_RUN together with interrupt_before - fix bug when calling update_state after only node has run - update some notebooks to new streaming format (ie. no __end__ node)
67 KiB
67 KiB
In [1]:
!pip install --quiet -U langchain langchain_openai tavily-python[1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m A new release of pip is available: [0m[31;49m23.3.1[0m[39;49m -> [0m[32;49m24.0[0m [1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m To update, run: [0m[32;49mpip install --upgrade pip[0m
In [2]:
import os
import getpass
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
os.environ["TAVILY_API_KEY"] = getpass.getpass("Tavily API Key:")OpenAI API Key: ········ Tavily API Key: ········
In [ ]:
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass("LangSmith API Key:")In [1]:
from langchain_community.tools.tavily_search import TavilySearchResults
tools = [TavilySearchResults(max_results=1)]In [2]:
from langgraph.prebuilt import ToolExecutor
tool_executor = ToolExecutor(tools)In [3]:
from langchain_openai import ChatOpenAI
# We will set streaming=True so that we can stream tokens
# See the streaming section for more information on this.
model = ChatOpenAI(temperature=0, streaming=True)In [4]:
from langchain_core.utils.function_calling import convert_to_openai_function
functions = [convert_to_openai_function(t) for t in tools]
model = model.bind_functions(functions)In [5]:
from langgraph.prebuilt import ToolInvocation
import json
from langchain_core.messages import FunctionMessage
# Define the function that determines whether to continue or not
def should_continue(messages):
last_message = messages[-1]
# If there is no function call, then we finish
if "function_call" not in last_message.additional_kwargs:
return "end"
# Otherwise if there is, we continue
else:
return "continue"
# Define the function that calls the model
def call_model(messages):
response = model.invoke(messages)
# We return a list, because this will get added to the existing list
return response
# Define the function to execute tools
def call_tool(messages):
# Based on the continue condition
# we know the last message involves a function call
last_message = messages[-1]
# We construct an ToolInvocation from the function_call
action = ToolInvocation(
tool=last_message.additional_kwargs["function_call"]["name"],
tool_input=json.loads(
last_message.additional_kwargs["function_call"]["arguments"]
),
)
# We call the tool_executor and get back a response
response = tool_executor.invoke(action)
# We use the response to create a FunctionMessage
function_message = FunctionMessage(content=str(response), name=action.tool)
# We return a list, because this will get added to the existing list
return function_messageIn [6]:
from langgraph.graph import MessageGraph, END
# Define a new graph
workflow = MessageGraph()
# Define the two nodes we will cycle between
workflow.add_node("agent", call_model)
workflow.add_node("action", call_tool)
# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.set_entry_point("agent")
# We now add a conditional edge
workflow.add_conditional_edges(
# First, we define the start node. We use `agent`.
# This means these are the edges taken after the `agent` node is called.
"agent",
# Next, we pass in the function that will determine which node is called next.
should_continue,
# Finally we pass in a mapping.
# The keys are strings, and the values are other nodes.
# END is a special node marking that the graph should finish.
# What will happen is we will call `should_continue`, and then the output of that
# will be matched against the keys in this mapping.
# Based on which one it matches, that node will then be called.
{
# If `tools`, then we call the tool node.
"continue": "action",
# Otherwise we finish.
"end": END,
},
)
# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge("action", "agent")In [7]:
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string(":memory:")In [8]:
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable
app = workflow.compile(checkpointer=memory, interrupt_before=["action"])In [9]:
from IPython.display import Image
Image(app.get_graph().draw_png())Out [9]:
In [10]:
from langchain_core.messages import HumanMessage
thread = {"configurable": {"thread_id": "2"}}
inputs = [HumanMessage(content="hi! I'm bob")]
for event in app.stream(inputs, thread):
for v in event.values():
print(v)content='Hello Bob! How can I assist you today?' id='76cf2bad-06c7-4210-9258-9595d7de6dea'
In [11]:
inputs = [HumanMessage(content="what is my name?")]
for event in app.stream(inputs, thread):
for v in event.values():
print(v)content='Your name is Bob. How can I help you, Bob?' id='7dbe3b16-df76-4053-93a3-1db95e51954f'
In [12]:
inputs = [HumanMessage(content="what's the weather in sf now?")]
for event in app.stream(inputs, thread):
for v in event.values():
print(v)content='' additional_kwargs={'function_call': {'arguments': '{"query":"weather in San Francisco now"}', 'name': 'tavily_search_results_json'}} id='aef7d52c-9127-409d-8428-b6a6c33ee6b4'
In [13]:
for event in app.stream(None, thread):
for v in event.values():
print(v)content='[{\'url\': \'https://weather.com/weather/hourbyhour/l/USCA0987:1:US\', \'content\': "recents\\nSpecialty Forecasts\\nHourly Weather-San Francisco, CA\\nBeach Hazard Statement\\nSaturday, November 25\\n5 pm\\nClear\\n6 pm\\nClear\\n7 pm\\nClear\\n8 pm\\nMostly Clear\\n9 pm\\nPartly Cloudy\\n10 pm\\nPartly Cloudy\\n11 pm\\nPartly Cloudy\\nSunday, November 26\\n12 am\\nPartly Cloudy\\n1 am\\nMostly Cloudy\\n2 am\\nMostly Cloudy\\n3 am\\nMostly Cloudy\\n4 am\\nCloudy\\n5 am\\nCloudy\\n6 am\\nMostly Cloudy\\n7 am\\nMostly Cloudy\\n8 am\\nMostly Cloudy\\n9 am\\nMostly Cloudy\\n10 am\\nPartly Cloudy\\n11 am\\nPartly Cloudy\\n12 pm\\nPartly Cloudy\\n1 pm\\nPartly Cloudy\\n2 pm\\nPartly Cloudy\\n3 pm\\nPartly Cloudy\\n4 pm\\nPartly Cloudy\\n5 pm\\nPartly Cloudy\\n6 pm\\nPartly Cloudy\\n7 pm\\nPartly Cloudy\\n8 pm\\nPartly Cloudy\\n9 pm\\nMostly Clear\\n10 pm\\nMostly Clear\\n11 pm\\nMostly Clear\\nMonday, November 27\\n12 am\\nClear\\n1 am\\nMostly Clear\\n2 am\\nMostly Clear\\n3 am\\nPartly Cloudy\\n4 am\\nMostly Clear\\n5 am\\nMostly Clear\\n6 am\\nClear\\n7 am\\nClear\\n8 am\\nSunny\\n9 am\\nSunny\\n10 am\\nSunny\\n11 am\\nSunny\\n12 pm\\nMostly Sunny\\n1 pm\\nPartly Cloudy\\n2 pm\\nMostly Sunny\\n3 pm\\nMostly Sunny\\n4 pm\\nPartly Cloudy\\nRadar\\nSafety First!\\n Don\'t Miss\\nIrresistible\\nWeather Wonders\\nOur Amazing World\\nCelestial Symphony\\nFried Turkey Fail\\nLook At That!\\n Health & Activities\\nSeasonal Allergies and Pollen Count Forecast\\nNo pollen detected in your area\\nCold & Flu Forecast\\nFlu risk is low in your area\\nWe recognize our responsibility to use data and technology for good. Changes For Critters\\nHurricane Tracker\\nStay Safe\\nAir Quality Index\\nAir quality is considered satisfactory, and air pollution poses little or no risk.\\n Take control of your data.\\n"}]' name='tavily_search_results_json' id='7db116fc-37bc-4821-a444-3c4f7467b469'
content='The current weather in San Francisco is clear. The hourly forecast shows clear skies for the next few hours. If you need more detailed information, you can visit [this link](https://weather.com/weather/hourbyhour/l/USCA0987:1:US). Let me know if you need any more assistance!' id='ddfdbdbf-7fec-4c65-93d7-d12c4eb8f163'
In [ ]: