Skip to content

State

Concepts: State.

State

State(task: str)

One run's record: everything that happened (history) and what the model sees next (context).

Pass a State to agent.run to keep it after the run, or to continue it after Ctrl+C. Every method is thread-safe, so tools may call them from worker threads.

Example
state = State("Find the bug in this repo")
agent.run(state)
print(state.answer, state.stopped_by, state.usage.cost)

Start a run with one user message.

Parameters:

Name Type Description Default
task str

What the agent should do. The first user message the model sees.

required

Raises:

Type Description
TypeError

task is not a string.

ValueError

task is empty or whitespace only.

task property

task: str

The task this State was created with.

answer property

answer: Any

The run's answer.

The value given to finish(answer) if there is one, otherwise the text of the latest model reply without tool calls, otherwise None. A think that fails does not change it.

turn property

turn: int

Number of turns so far: each think adds 1, and a think that fails does not count.

usage property

usage: Usage

Total tokens, requests and cost of every think, ask and compact on this State.

pending_calls property

pending_calls: tuple[ToolCall, ...]

Tool calls the model requested this turn that have no result yet, in request order.

A call leaves this tuple when it gets a result, is denied with deny, or is closed by an exception.

context_tokens property

context_tokens: int

Estimated token count of the context, including the system prompt and tool definitions.

context_used property

context_used: float

Fraction of the model's context window in use: context_tokens / context_window.

0.0 before the first run or think (the window size is unknown until then). Can go above 1.0.

stopped_by property

stopped_by: str | None

Why the last loop stopped: "finish", "limit", or the name of the until function that returned true (e.g. "is_answered"). None while running, or if the run ended with an exception.

stopped_limit property

stopped_limit: int | None

The limit the loop reached if stopped_by == "limit", otherwise None.

history property

history: tuple[HistoryEntry, ...]

Everything that happened in this run, oldest first: messages, replies, tool results, errors and context changes. Entries are only ever added, so shrinking the context never removes anything from here.

Returns a copy.

context property

context: tuple[Message, ...]

The messages the model will see at the next think. Returns a copy.

Results of this turn's tool calls go in together once the last call has a result. Messages added in the meantime, or while think waits on the model, go in after them.

data property

data: dict[str, Any]

A dict for your own data. The model never sees it.

Single reads and writes (d[k], d[k] = v, get, setdefault, pop) are thread-safe. For an update that takes several steps, or to loop over it, hold state.lock.

Example
with state.lock:
    state.data["calls"] = state.data.get("calls", 0) + 1

lock property

lock: RLock

The reentrant lock (threading.RLock) every State method uses. Hold it to make several changes as one.

is_answered

is_answered() -> bool

Whether the model's last reply is a final answer.

True when the last message in the context is a model reply without tool calls. A message added after it (a notice, a user message) makes it false, and so do pending calls.

Use it as a stop condition: @loop(until=State.is_answered, limit=50).

wants_tools

wants_tools() -> bool

Whether the model requested tool calls that have not run yet (pending_calls is not empty).

is_finished

is_finished() -> bool

Whether finish() was called. A finished State stops its loop before the next turn.

add_user_message

add_user_message(text: str) -> None

Add a message from the human, for example the next request in a chat loop.

While tool calls are pending, or while think waits on the model, the message is held and goes into the context right after the results or the reply. is_answered() is false until the model answers it.

Parameters:

Name Type Description Default
text str

The message.

required

Raises:

Type Description
ValueError

text is empty or whitespace only.

add_notice

add_notice(text: str) -> None

Tell the model something from your loop or a tool, for example "The tests failed".

The model sees it as a user message starting with "[notice] " (added if missing). It is held while calls are pending, like add_user_message.

Parameters:

Name Type Description Default
text str

The notice.

required

Raises:

Type Description
ValueError

text is empty or whitespace only.

finish

finish(answer: Any = None) -> None

End the run. The loop stops before its next turn with stopped_by == "finish".

Tools may call it (a submit tool, for example), even while other calls are pending. Calling it again is allowed; the last answer given wins.

Parameters:

Name Type Description Default
answer Any

If not None, becomes state.answer. Any value, not only a string.

None

deny

deny(call: ToolCall, reason: str) -> None

Refuse a pending tool call. The model gets reason as that call's error result.

Parameters:

Name Type Description Default
call ToolCall

One of state.pending_calls.

required
reason str

What the model is told, for example "The user denied it".

required

Raises:

Type Description
ValueError

call is not pending, or reason is empty or whitespace only.

Example
for call in state.pending_calls:
    if call.name == "delete_file":
        state.deny(call, "Deleting files is not allowed")
agent.use_tools(state)

start_from

start_from(summary: str) -> None

Replace the context with two messages: the original task and summary. History is kept.

Parameters:

Name Type Description Default
summary str

What the model should know about the work so far.

required

Raises:

Type Description
TypeError

summary is not a string.

ValueError

Tool calls are pending, or summary is empty or whitespace only.

clear_tool_results

clear_tool_results(keep_last: int = 5) -> None

Shrink the context by blanking old tool results. History keeps the full results.

Every tool result except the last keep_last is replaced with "(cleared: kept in history)". Results that are already cleared still count toward keep_last.

Parameters:

Name Type Description Default
keep_last int

How many of the most recent tool results to keep.

5

Raises:

Type Description
ValueError

Tool calls are pending, or keep_last is not an integer >= 0.