The LangChain documentation refers to the ability to modify the state of an existing conversation using the Command class. One example in particular refers to a function get_user_preference which appears to retrieve a preference stored inside a dictionary called “user_preferences” due to the return preferences.get(pref_name, "Not set") line.
However, attempting to reconstruct this example using a custom state class which models the underlying structure conflicts with what the documentation says about retrieving an example user preference. (See code below).
from langchain.tools import tool, ToolRuntime
from langchain.messages import HumanMessage
`@tool
def get_last_user_message(runtime: ToolRuntime) -> str:
"""Get the most recent message from the user."""
messages = runtime.state\["messages"\]
# Find the last human message
for message in reversed(messages):
if isinstance(message, HumanMessage):
return message.content
return "No user messages found"`
# Access custom state fields
@tool
def get_user_preference(
pref_name: str,
runtime: ToolRuntime
) -> str:
"""Get a user preference value."""
preferences = runtime.state.get("user_preferences", {})
return preferences.get(pref_name, "Not set")
From the below example in the documentation, I can infer that “preferred_language” is part of the “user_preferences” dictionary, which I can model as in my code.
@tool
def set_language(language: str, runtime: ToolRuntime) -> Command:
"""Set the preferred response language."""
return Command(
update={
"preferred_language": language,
"messages": [
ToolMessage(
content=f"Language set to {language}.",
tool_call_id=runtime.tool_call_id,
)
],
}
)
Sample code example running an implementation with a dictionary of user preferences and using Command to update the state:
from langchain.agents import create_agent, AgentState
from langchain.tools import tool, ToolRuntime
from langchain.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
from typing_extensions import TypedDict
@tool
def get_last_user_message(runtime: ToolRuntime) -> str:
"""Get the most recent message from the user."""
messages = runtime.state["messages"]
for message in reversed(messages):
if isinstance(message, HumanMessage):
return message.content
return "No user messages found"
@tool
def get_user_preference(
pref_name: str,
runtime: ToolRuntime
) -> str:
"""Get a user preference value."""
preferences = runtime.state.get("user_preferences", {})
return preferences.get(pref_name, "Not set")
class CustomAgentState(AgentState):
user_id: str
user_preferences: dict
@tool
def set_user_preferences_language(new_language: str, runtime: ToolRuntime[None, CustomAgentState]) -> Command:
"""Set the user's preferred language in the conversation state."""
return Command(
update={
"user_preferences": new_language,
"messages": [
ToolMessage(
content=f"Preferred language set to {new_language}.",
tool_call_id=runtime.tool_call_id,
)
],
}
)
@tool
def inspect_state(runtime: ToolRuntime) -> str:
"""Inspect the current runtime state. This tool is called inspect_state."""
print("runtime state\n", runtime.state)
return runtime.state
agent = create_agent(
model="openai:gpt-5-nano",
tools=[get_last_user_message, get_user_preference, set_user_preferences_language],
state_schema=CustomAgentState,
checkpointer=InMemorySaver(),
)
messages = [
{
"role": "user",
"content": "Update the user's preferred language to English (U.K.). Then output the user's preferred language. Finally, inspect the current runtime state by using the inspect_state tool."
}
]
user_preferences = {
"preferred_language": "English",
"verbosity": "concise",
"tone": "friendly"
}
thread_config = {"configurable": {"thread_id": "1"}}
response = agent.invoke(
{
"messages": messages,
"user_id": "user_123",
"user_preferences": user_preferences,
},
thread_config,
)
print(response)
I am getting the following error in my Terminal when running this:
return preferences.get(pref_name, "Not set")
^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'get'
During task with name 'tools' and id 'c8c09c7d-6b03-1384-9ff1-a5c1dbfbccfc'
My issue is not simply that I am seeing this error, but that the documentation demonstrates reading nested state (e.g. runtime.state["user_preferences"]) while there appears to be no documented way to update a nested field in an object like CustomAgentState where then fields are more complex than simple top-level fields (the example in the documentation is limited to showing how top-level state fields can be updated).