State & History¶
GlobalState¶
GlobalState is the built-in state model for orqest agents. It tracks two distinct things:
messages— an application-level conversation log (list[dict]withroleandcontentkeys). This is for your code to read and write.message_history— raw pydantic-aiModelMessageobjects (list[ModelMessage]). This is what gets passed to the LLM for context.
These two stores serve different purposes and don't need to stay in sync. messages is a human-readable log; message_history is the raw LLM conversation managed by call_model().
Adding and Reading Messages¶
from orqest.agents import GlobalState
state = GlobalState()
# Add messages to the app-level log
state.add_message("user", "What is quantum computing?")
state.add_message("assistant", "Quantum computing uses qubits...")
# Read the most recent message for a given role
latest_user = state.get_latest_message("user") # "What is quantum computing?"
latest_assistant = state.get_latest_message("assistant") # "Quantum computing uses qubits..."
missing = state.get_latest_message("system") # None
Custom State Types¶
You can subclass GlobalState to add domain-specific fields:
from pydantic import Field
from orqest.agents import GlobalState
class ResearchState(GlobalState):
topic: str = ""
sources: list[str] = Field(default_factory=list)
max_depth: int = 3
Or use any Pydantic BaseModel as your state type. If your state doesn't have a message_history attribute, call_model() gracefully skips history wiring — it checks with hasattr before reading or writing.
How call_model() Wires History¶
When you call self.call_model(prompt, state) inside _run_implementation():
- Read: Gets
state.message_history(empty list on first call) - Pass: Calls
self.agent.run(prompt, message_history=state.message_history) - Process: pydantic-ai runs any registered history processors (like
keep_recent_messages) - Store: After the LLM responds, stores the full updated history back:
state.message_history = result.all_messages()
On the next call with the same state, step 1 picks up the accumulated history — the LLM sees the full conversation.
History Processing¶
keep_recent_messages()¶
The default history processor truncates old messages while preserving conversation integrity:
- First message preserved — always kept, since it typically contains the initial context
- Sliding window — keeps the N most recent messages (configured by
truncated_history) - Turn integrity — if truncation would split a tool-call pair (a
ModelResponsewith a tool call followed by aModelRequestwith the tool return), the preceding response is included
# Default: keep 100 most recent messages
agent = MyAgent(..., truncated_history=100)
# Keep only 20 messages for a focused, short-context agent
agent = MyAgent(..., truncated_history=20)
Custom History Processors¶
Pass your own processors to replace or extend the default behavior:
def only_user_messages(messages):
"""Keep only user prompt messages."""
return [m for m in messages if hasattr(m, 'parts') and any(
hasattr(p, 'content') for p in m.parts
)]
agent = MyAgent(
...,
history_processors=[only_user_messages],
)
History processors are callables that take a list[ModelMessage] and return a list[ModelMessage]. They're passed directly to pydantic-ai's Agent constructor and run before each LLM call.
Note
When you provide custom history_processors, the default keep_recent_messages is not included. Add it explicitly if you want both: