How to add runtime configuration to your graph¶
Sometimes you want to be able to configure your agent when calling it. Examples of this include configuring which LLM to use. Below we walk through an example of doing so.
Base¶
First, let's create a very simple graph
In [7]:
Copied!
import operator
from typing import Annotated, Sequence, TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import END, StateGraph, START
model = ChatAnthropic(model_name="claude-2.1")
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
def _call_model(state):
response = model.invoke(state["messages"])
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
import operator
from typing import Annotated, Sequence, TypedDict
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import END, StateGraph, START
model = ChatAnthropic(model_name="claude-2.1")
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
def _call_model(state):
response = model.invoke(state["messages"])
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
In [8]:
Copied!
app.invoke({"messages": [HumanMessage(content="hi")]})
app.invoke({"messages": [HumanMessage(content="hi")]})
Out[8]:
{'messages': [HumanMessage(content='hi'), AIMessage(content='Hello!', response_metadata={'id': 'msg_01YZj7CVCUSc76faX4VM9i5d', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-d343db34-598c-46a2-93d6-ffa886d9b264-0')]}
Configure the graph¶
Great! Now let's suppose that we want to extend this example so the user is able to choose from multiple llms. We can easily do that by passing in a config. This config is meant to contain things are not part of the input (and therefore that we don't want to track as part of the state).
In [11]:
Copied!
from langchain_openai import ChatOpenAI
openai_model = ChatOpenAI()
models = {
"anthropic": model,
"openai": openai_model,
}
def _call_model(state, config):
m = models[config["configurable"].get("model", "anthropic")]
response = m.invoke(state["messages"])
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
from langchain_openai import ChatOpenAI
openai_model = ChatOpenAI()
models = {
"anthropic": model,
"openai": openai_model,
}
def _call_model(state, config):
m = models[config["configurable"].get("model", "anthropic")]
response = m.invoke(state["messages"])
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
If we call it with no configuration, it will use the default as we defined it (Anthropic).
In [12]:
Copied!
app.invoke({"messages": [HumanMessage(content="hi")]})
app.invoke({"messages": [HumanMessage(content="hi")]})
Out[12]:
{'messages': [HumanMessage(content='hi'), AIMessage(content='Hello!', response_metadata={'id': 'msg_01EedReFyXmonWXPKhYre7Jb', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-1c6feaa0-bd6f-433a-8264-209d72c85db7-0')]}
We can also call it with a config to get it to use a different model.
In [13]:
Copied!
config = {"configurable": {"model": "openai"}}
app.invoke({"messages": [HumanMessage(content="hi")]}, config=config)
config = {"configurable": {"model": "openai"}}
app.invoke({"messages": [HumanMessage(content="hi")]}, config=config)
Out[13]:
{'messages': [HumanMessage(content='hi'), AIMessage(content='Hello! How can I assist you today?', response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 8, 'total_tokens': 17}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_3b956da36b', 'finish_reason': 'stop', 'logprobs': None}, id='run-d41ffb62-e164-45a1-862c-d288c6ad100a-0')]}
We can also adapt our graph to take in more configuration! Like a system message for example.
In [18]:
Copied!
from langchain_core.messages import SystemMessage
def _call_model(state, config):
m = models[config["configurable"].get("model", "anthropic")]
messages = state["messages"]
if "system_message" in config["configurable"]:
messages = [
SystemMessage(content=config["configurable"]["system_message"])
] + messages
response = m.invoke(messages)
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
from langchain_core.messages import SystemMessage
def _call_model(state, config):
m = models[config["configurable"].get("model", "anthropic")]
messages = state["messages"]
if "system_message" in config["configurable"]:
messages = [
SystemMessage(content=config["configurable"]["system_message"])
] + messages
response = m.invoke(messages)
return {"messages": [response]}
# Define a new graph
workflow = StateGraph(AgentState)
workflow.add_node("model", _call_model)
workflow.add_edge(START, "model")
workflow.add_edge("model", END)
app = workflow.compile()
In [19]:
Copied!
app.invoke({"messages": [HumanMessage(content="hi")]})
app.invoke({"messages": [HumanMessage(content="hi")]})
Out[19]:
{'messages': [HumanMessage(content='hi'), AIMessage(content='Hello!', response_metadata={'id': 'msg_01Ts56eVLSrUbzVMbzLnXc3M', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 10, 'output_tokens': 6}}, id='run-f75a4389-b72e-4d47-8f3e-bedc6a060f66-0')]}
In [20]:
Copied!
config = {"configurable": {"system_message": "respond in italian"}}
app.invoke({"messages": [HumanMessage(content="hi")]}, config=config)
config = {"configurable": {"system_message": "respond in italian"}}
app.invoke({"messages": [HumanMessage(content="hi")]}, config=config)
Out[20]:
{'messages': [HumanMessage(content='hi'), AIMessage(content='Ciao!', response_metadata={'id': 'msg_01RzFCii8WhbbkFm16nUquxk', 'model': 'claude-2.1', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 14, 'output_tokens': 7}}, id='run-9492f0e4-f223-41c2-81a6-6f0cb6a14fe6-0')]}
In [ ]:
Copied!