Sagents.State (Sagents v0.4.2)
Copy MarkdownAgent state structure for managing agent execution context.
The state holds the complete context for an agent execution including:
- Message history (list of
LangChain.Messagestructs) - TODO list
- Metadata
Note: Files are managed separately by FileSystemServer and are not part of
the agent's internal state. The FileSystemServer provides persistent storage with
ETS and optional backend persistence (disk, database, S3, etc.).
agent_id Management (Automatic)
The agent_id field is a runtime identifier used for process registration and
coordination. You don't need to set it when creating states—the library
automatically injects it when you call Sagents.Agent.execute/2, Sagents.Agent.resume/3, or
start an AgentServer.
Why it's automatic
The agent_id flows from the Agent struct (which is configuration) to the State
(which is data). Making you synchronize them manually could be error-prone.
For middleware developers
When middleware receives state in hooks (before_model, after_model), the
agent_id will already be set. If you create new state structs in middleware,
copy the agent_id from the incoming state:
def after_model(state, config) do
updated_state = State.new!(%{
agent_id: state.agent_id, # Copy from incoming state
messages: new_messages
})
{:ok, updated_state}
endState Merging
State merging follows specific rules:
- messages: Appends new messages to existing list
- todos: Replaces with new todos (merge handled by TodoList middleware)
- metadata: Deep merges metadata maps
- agent_id: Uses right if present, otherwise left (runtime identifier, not data)
Summary
Functions
Add a message to the state.
Add multiple messages to the state.
Replace any stale interrupt placeholder tool results with error messages.
Remove a TODO by ID.
Deserializes state data from export_state/1.
Get metadata value.
Get a TODO by ID.
Get all TODOs with a specific status.
Merge two states together.
Create a new agent state.
Create a new agent state, raising on error.
Set metadata value.
Add or update a TODO item.
Replace a tool result in the state's messages by tool_call_id.
Reset the state to a clean slate.
Replace all messages.
Replace all TODOs.
Types
Functions
Add a message to the state.
Message must be a LangChain.Message struct.
Add multiple messages to the state.
Messages must be LangChain.Message structs.
Replace any stale interrupt placeholder tool results with error messages.
Called after loading state from the database. Interrupted tool results reference sub-agent processes that no longer exist, so they must be converted to error results before the LLM sees them.
This is idempotent — if there are no stale interrupts, it's a no-op.
Remove a TODO by ID.
Deserializes state data from export_state/1.
This is a convenience wrapper around StateSerializer.deserialize_state/2.
Important: The agent_id is NOT serialized (it's a runtime identifier),
so you MUST provide it when deserializing. This ensures the state can properly
interact with AgentServer and middleware that rely on the agent_id.
Examples
# Load from database
{:ok, state_data} = load_from_db(conversation_id)
# Deserialize with agent_id
{:ok, state} = State.from_serialized("my-agent-123", state_data["state"])Parameters
agent_id- The agent_id to use for this state (required)data- The serialized state map (the "state" field from export_state)
Returns
{:ok, state}- Successfully deserialized with agent_id set{:error, reason}- Deserialization failed
Get metadata value.
Get a TODO by ID.
Get all TODOs with a specific status.
Merge two states together.
This is used when combining state updates from tools, middleware, or subagents.
Merge Rules
- messages: Concatenates lists (left + right)
- todos: Uses right if present, otherwise left
- metadata: Deep merges maps
Examples
left = State.new!(%{messages: [%{role: "user", content: "hi"}]})
right = State.new!(%{messages: [%{role: "assistant", content: "hello"}]})
merged = State.merge_states(left, right)
# merged now has both messages
Create a new agent state.
Note: The agent_id field is optional when creating a state. The library
automatically injects it when the state is passed to Agent.execute or
AgentServer. This eliminates the need for manual agent_id synchronization.
Examples
# Create state without agent_id (recommended)
state = State.new!(%{messages: [message]})
# Library injects agent_id automatically
{:ok, result_state} = Agent.execute(agent, state)
Create a new agent state, raising on error.
Set metadata value.
Add or update a TODO item.
If a TODO with the same ID exists, it will be replaced at its current position. If the TODO ID doesn't exist, it will be appended to the end of the list.
Replace a tool result in the state's messages by tool_call_id.
Delegates to LangChain.Message.replace_tool_result/3.
Reset the state to a clean slate.
Clears:
- All messages
- All TODOs
- All metadata
Note: This function only resets the Agent's state structure. File state is managed separately by FileSystemServer and must be reset through AgentServer.reset/1 which coordinates the full reset process.
Examples
state = State.new!(%{
messages: [msg1, msg2],
todos: [todo1],
metadata: %{config: "value"}
})
reset_state = State.reset(state)
# reset_state has:
# - messages: []
# - todos: []
# - metadata: %{} (cleared)
Replace all messages.
Useful for:
- Thread restoration (restoring persisted messages)
- Testing scenarios (setting sample messages)
- Bulk message updates
Parameters
state- The current State structmessages- List of Message structs
Examples
messages = [
Message.new_user!("Hello")
]
state = State.set_messages(state, messages)
Replace all TODOs.