Data types¶
The data objects that the other classes take and return.
Message
dataclass
¶
Message(role: Literal['user', 'assistant'], content: tuple[Block, ...], tokens: int | None = None)
One message in the context (state.messages).
The system prompt is not a message; it goes in Request.system.
role
instance-attribute
¶
role: Literal['user', 'assistant']
"user" or "assistant". Notices and tool results are user messages.
content
instance-attribute
¶
content: tuple[Block, ...]
The blocks in order: text, tool calls (assistant only), tool results (user only) and provider-specific blocks such as thinking.
tokens
class-attribute
instance-attribute
¶
tokens: int | None = None
The token count of the whole context up to and including this message, from API usage. Set on assistant
replies only; None if unknown.
is_notice
property
¶
is_notice: bool
True for a user message whose first text starts with NOTICE_PREFIX (see Message.notice).
user
classmethod
¶
A user message: the text, then the images if any.
With images the text may be empty (Message.user("", image)); the empty text block is left out, because
providers reject it.
Raises:
| Type | Description |
|---|---|
TypeError
|
|
ToolCall
dataclass
¶
ToolCall(name: str, args: Mapping[str, Any], id: str)
One tool call the model requested. Also a block of an assistant message.
Calls are told apart by id, and two calls with the same id hash the same.
args
instance-attribute
¶
args: Mapping[str, Any]
The arguments the model gave. A read-only dict (a FrozenDict, frozen at every level): reading, **args,
dict(args) and json.dumps work, and changing it raises TypeError. A call is a record of what the
model asked, so copy the arguments (dict(call.args)) before changing them.
ToolSpec
dataclass
¶
ToolSpec(name: str, description: str, input_schema: dict[str, Any])
Request
dataclass
¶
Request(system: str | None, messages: tuple[Message, ...], tools: tuple[ToolSpec, ...] = (), tool_choice: Literal['auto', 'none'] = 'auto')
A request the Agent sends to the Model. Model settings (max_tokens etc.) belong to the Model object.
tools
class-attribute
instance-attribute
¶
tools: tuple[ToolSpec, ...] = ()
The tools shown to the model.
tool_choice
class-attribute
instance-attribute
¶
tool_choice: Literal['auto', 'none'] = 'auto'
"auto" lets the model call tools. "none" still sends the tool definitions but forbids calling
them.
Reply
dataclass
¶
Reply(message: Message, usage: Usage, context_tokens: int | None = None, stop_reason: str | None = None, model: str | None = None)
One reply from the Model.
message
instance-attribute
¶
message: Message
The assistant message. Blocks stay in the order the provider gave.
context_tokens
class-attribute
instance-attribute
¶
context_tokens: int | None = None
The token count of the whole context up to and including this reply (input + cache read + cache write +
output). None if unknown.
stop_reason
class-attribute
instance-attribute
¶
stop_reason: str | None = None
Exactly what the provider gave, e.g. "end_turn", "tool_use" or "max_tokens".
model
class-attribute
instance-attribute
¶
model: str | None = None
The model that answered, as the provider reported it. The requested name if the provider did not say.
None only for a custom Model that does not set it. It can differ from the requested name (an alias, a
router, a fallback); usage.cost is still computed from the requested Model's price.
Usage
dataclass
¶
Usage(input_tokens: int = 0, output_tokens: int = 0, cache_read_tokens: int = 0, cache_write_tokens: int = 0, requests: int = 0, cost: float | None = None)
Token usage. Every provider's counts are mapped to the same meanings, so total input is
input_tokens + cache_read_tokens + cache_write_tokens.
Adding two Usages (+) returns a new one. Its cost is None if either side has requests but an
unknown cost.
input_tokens
class-attribute
instance-attribute
¶
input_tokens: int = 0
Input tokens that did not go through the cache.
cache_read_tokens
class-attribute
instance-attribute
¶
cache_read_tokens: int = 0
Input tokens read from the cache.
cache_write_tokens
class-attribute
instance-attribute
¶
cache_write_tokens: int = 0
Input tokens newly written to the cache.
cost
class-attribute
instance-attribute
¶
cost: float | None = None
Estimated dollars. None when the price is unknown, which is different from 0.
cache_hit_rate
property
¶
cache_hit_rate: float | None
The fraction of total input read from the cache. None when there is no input.
Price
dataclass
¶
Price(input: float, output: float, cache_read: float | None = None, cache_write: float | None = None)
Dollars per million tokens. Cache prices default to the input price when not given.
Example
Anthropic("claude-sonnet-5", price=Price(input=3, output=15, cache_read=0.3))
input
instance-attribute
¶
input: float
Dollars per million input tokens that did not go through the cache.
cache_read
class-attribute
instance-attribute
¶
cache_read: float | None = None
Dollars per million tokens read from the cache. None uses input.
cache_write
class-attribute
instance-attribute
¶
cache_write: float | None = None
Dollars per million tokens written to the cache. None uses input.
History entries¶
The items of state.history. alpineagents.HistoryEntry is their union: check kind (or use isinstance or
match) to know which one an entry is, and so what its content holds. There are 11 classes. Model... entries are
about the model, ToolResultEntry about a tool call.
| Class | kind |
content |
|---|---|---|
MessageEntry |
user, notice |
str, or a tuple of TextBlock and Image when the message has images |
ModelRequestEntry |
model_request |
str, the model name that was asked (provider/name) |
ModelReplyEntry |
model_reply |
Reply |
ModelEventEntry |
model_event |
ModelEvent |
ToolResultEntry |
tool_result |
The result sent to the model: str, or a tuple of TextBlock and Image. outcome says how the call ended, including denied and cancelled |
ExchangeEntry |
ask, human |
An object with question and answer. An ask entry also has usage |
ContextChangeEntry |
context_change |
ContextChange: compact, clear_tool_results, import or restore |
RunStartEntry |
run_start |
AgentInfo |
StopEntry |
stop |
Stopped, or None for a stop that was cleared |
ExtraDataEntry |
extra_data |
The keys that changed with their new values. removed lists the deleted keys |
ErrorEntry |
error |
str such as "TimeoutError: ..." |
Every entry has kind, content, turn and at. The other fields are only on the classes where they mean
something: call on ToolResultEntry and ErrorEntry, for example. The subagent fields
are child_ids and usage on ToolResultEntry, and parent_id and parent_call_id on RunStartEntry.
Values in history are read-only. A ToolCall's args, a ModelEvent's data, an ExtraDataEntry's content and
an Exchange's answer are frozen dicts and lists: they work with indexing, json.dumps and ==, and a change
raises TypeError. dict(value) makes an editable copy.
MessageEntry
dataclass
¶
MessageEntry(*, kind: Literal['user', 'notice'], content: str | tuple[TextBlock | Image, ...], turn: int, at: datetime = _stamp())
A user message or a notice added to the State (State(messages=...) records its messages as one
context_change entry instead).
kind
instance-attribute
¶
kind: Literal['user', 'notice']
"user": from the person (Message.user). "notice": from your loop or a tool
(Message.notice).
content
instance-attribute
¶
The text, as a str when the message was only text. A notice starts with "[notice] ". A tuple of
TextBlock and Image when the message has images.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
message
property
¶
message: Message
The user message this entry added: Message.user(...) with the text and images.
from_message
classmethod
¶
from_message(message: Message, *, turn: int) -> MessageEntry
The entry for a user message: "notice" if it is a notice, else "user".
Raises:
| Type | Description |
|---|---|
ValueError
|
the message is not a user message, or has blocks other than text and images. |
ModelRequestEntry
dataclass
¶
ModelRequestEntry(*, kind: Literal['model_request'] = 'model_request', content: str, turn: int, at: datetime = _stamp())
think is sending a request to the model. A request with no model_reply after it (an error
entry follows instead) did not change the context. Only think writes one: ask and compact requests
do not, so history shows which model wrote each reply, even when the model changed inside one run.
kind
class-attribute
instance-attribute
¶
kind: Literal['model_request'] = 'model_request'
Always "model_request".
content
instance-attribute
¶
content: str
The model name that was asked, as the Agent's model gives it (provider/name).
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
ModelReplyEntry
dataclass
¶
ModelReplyEntry(*, kind: Literal['model_reply'] = 'model_reply', content: Reply, turn: int, at: datetime = _stamp())
A model reply from think.
kind
class-attribute
instance-attribute
¶
kind: Literal['model_reply'] = 'model_reply'
Always "model_reply".
content
instance-attribute
¶
content: Reply
The Reply, including reply.model, the model that answered.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
ModelEventEntry
dataclass
¶
ModelEventEntry(*, kind: Literal['model_event'] = 'model_event', content: ModelEvent, turn: int, at: datetime = _stamp())
Something the Model went through, such as a fallback or a retry.
ToolResultEntry
dataclass
¶
ToolResultEntry(*, kind: Literal['tool_result'] = 'tool_result', content: ToolResultContent, call: ToolCall, outcome: ToolOutcomeKind, late: bool = False, error: BaseException | None = None, child_ids: tuple[str, ...] = (), usage: Usage | None = None, turn: int, at: datetime = _stamp())
The result of a tool call, as the model got it, whatever way the call ended (outcome): it ran, it was
an input error, it was closed by an exception ("(aborted: TimeoutError)", "(interrupted by user)"), or
a permission kept it from running.
kind
class-attribute
instance-attribute
¶
kind: Literal['tool_result'] = 'tool_result'
Always "tool_result".
content
instance-attribute
¶
content: ToolResultContent
The result sent to the model: a str, or a tuple of TextBlock and Image (result_text
turns either into one string).
outcome
instance-attribute
¶
outcome: ToolOutcomeKind
How the call ended: DONE, ERROR, INPUT_ERROR, ABORTED, INTERRUPTED, DENIED or
CANCELLED. A plain string such as "denied" becomes the matching ToolOutcomeKind.
late
class-attribute
instance-attribute
¶
late: bool = False
True if it arrived after its call was already closed (the model gets it as a notice at the next
think).
error
class-attribute
instance-attribute
¶
error: BaseException | None = field(default=None, compare=False)
The ToolError behind an error result (its __cause__ is the original exception when
exception_handler made it), or the exception that closed the call. Not saved by a store, and ignored by
==.
child_ids
class-attribute
instance-attribute
¶
child_ids: tuple[str, ...] = ()
The ids of the subagent States this call started (runs started inside the call), in the order they
started. Open one with state.child(child_id). Empty for a call that started none. A late result
repeats the ids of its call.
usage
class-attribute
instance-attribute
¶
usage: Usage | None = None
The usage of those subagents, and of their own subagents, that this State has not counted yet: what each
one used since it joined the call. It is added to state.usage when the entry is recorded, so a late
result holds only what the subagents used after the call was closed. An empty Usage() when they used
nothing; None only when the call started no subagent.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
is_error
property
¶
is_error: bool
True if the model got it as an error result: the outcome is anything but DONE.
ExchangeEntry
dataclass
¶
ExchangeEntry(*, kind: Literal['ask', 'human'], content: Exchange, usage: Usage | None = None, turn: int, at: datetime = _stamp())
A question and its answer. The context does not change.
kind
instance-attribute
¶
kind: Literal['ask', 'human']
"ask": agent.ask asked the model. "human": the Human was asked (ask_human,
DecideByHuman).
usage
class-attribute
instance-attribute
¶
usage: Usage | None = None
The usage of the model request behind an "ask". None for "human".
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded (when the answer came), as a timezone-aware UTC datetime.
ContextChangeEntry
dataclass
¶
ContextChangeEntry(*, kind: Literal['context_change'] = 'context_change', content: ContextChange, turn: int, at: datetime = _stamp())
The context was replaced or shrunk (compact, clear_tool_results), imported at the start, or the State
went back to an earlier point (restore).
RunStartEntry
dataclass
¶
RunStartEntry(*, kind: Literal['run_start'] = 'run_start', content: AgentInfo, parent_id: str | None = None, parent_call_id: str | None = None, turn: int, at: datetime = _stamp())
A run started (agent.run). content is the Agent that runs it, which is what a later resume of this
State is compared with.
kind
class-attribute
instance-attribute
¶
kind: Literal['run_start'] = 'run_start'
Always "run_start".
parent_id
class-attribute
instance-attribute
¶
parent_id: str | None = None
The id of the State whose tool call started this run (a subagent run), or None for a run started
anywhere else.
parent_call_id
class-attribute
instance-attribute
¶
parent_call_id: str | None = None
The ToolCall.id of that tool call, or None together with parent_id.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
StopEntry
dataclass
¶
StopEntry(*, kind: Literal['stop'] = 'stop', content: Stopped | None, turn: int, at: datetime = _stamp())
The current run is set to stop, and why (state.stopped): an until function returned true, the loop
reached its limit, finish was called, or a permission stopped the turn.
content is None for a record that clears an earlier stop. A @loop that goes on after an inner loop
stopped records it, so state.stopped is not left at the inner loop's reason.
ExtraDataEntry
dataclass
¶
ExtraDataEntry(*, kind: Literal['extra_data'] = 'extra_data', content: Mapping[str, Any], removed: tuple[str, ...] = (), turn: int, at: datetime = _stamp())
state.extra_data changed (State(extra_data=...) or edit_extra_data()).
kind
class-attribute
instance-attribute
¶
kind: Literal['extra_data'] = 'extra_data'
Always "extra_data".
content
instance-attribute
¶
content: Mapping[str, Any]
The keys that were set, with their new values (JSON). A read-only dict (a FrozenDict).
removed
class-attribute
instance-attribute
¶
removed: tuple[str, ...] = ()
The keys that were deleted.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
ErrorEntry
dataclass
¶
ErrorEntry(*, kind: Literal['error'] = 'error', content: str, error: BaseException | None = None, call: ToolCall | None = None, turn: int, at: datetime = _stamp())
An exception raised in think, use_tools or run. An exception is recorded once.
An error right after a model_request (no model_reply) means the request failed and the context did
not change: the turn is taken back.
content
instance-attribute
¶
content: str
Its type and message, e.g. "TimeoutError: timed out" (only the type if the message is empty).
error
class-attribute
instance-attribute
¶
error: BaseException | None = field(default=None, compare=False)
The exception. None in a State loaded from a store (content keeps the text). Ignored by ==.
call
class-attribute
instance-attribute
¶
call: ToolCall | None = None
The call whose tool or permission raised it, or None.
at
class-attribute
instance-attribute
¶
at: datetime = _stamp()
When the entry was recorded, as a timezone-aware UTC datetime.
Other records¶
ContextChange
dataclass
¶
ContextChange(kind: ContextChangeKind, before_tokens: int, after_tokens: int, summary: str | None = None, kept: int = 0, cleared: tuple[str, ...] = (), messages: tuple[Message, ...] = (), restored_to: int | None = None, usage: Usage | None = None)
A record that the context changed. Shown by Reporter.on_context_change and kept in history.
before_tokens and after_tokens are facts recorded for display: they are never recomputed when a State
is replayed from its history.
kind
instance-attribute
¶
kind: ContextChangeKind
"compact", "clear_tool_results", "import" (State(messages=...)) or "restore"
(state.restore(snapshot)).
before_tokens
instance-attribute
¶
before_tokens: int
The estimated context size before the change (without the system prompt and tool overhead).
summary
class-attribute
instance-attribute
¶
summary: str | None = None
The summary text compact put in the context. None for other kinds.
kept
class-attribute
instance-attribute
¶
kept: int = 0
compact: how many of the latest messages stay after the summary, namely the ones added while a model
was writing the summary.
cleared
class-attribute
instance-attribute
¶
cleared: tuple[str, ...] = ()
clear_tool_results: the ids of the tool calls whose results were blanked.
messages
class-attribute
instance-attribute
¶
messages: tuple[Message, ...] = ()
import: the messages the State started with.
restored_to
class-attribute
instance-attribute
¶
restored_to: int | None = None
restore: the history length the State went back to (len(snapshot.history)).
Exchange
dataclass
¶
Exchange(question: str, answer: Any)
The question and answer of ask/ask_human: the content of an ExchangeEntry. answer is
None if no answer came (OutputError).
answer is JSON (a read-only FrozenDict/FrozenList when it is a dict or list), as the history
records it: a dataclass or Pydantic model comes back as a dict. agent.ask still returns the real object to
its caller.
AgentInfo
dataclass
¶
AgentInfo(*, name: str | None = None, model: str, system_sha256: str | None = None, tools: tuple[str, ...] = (), mcp_servers: tuple[str, ...] = ())
What an Agent was when it started a run, kept in history (RunStartEntry). Used to warn when a loaded
State is resumed by a different Agent (ResumeWarning). JSON values only; no MCP connection is needed to
build it.
system_sha256
class-attribute
instance-attribute
¶
system_sha256: str | None = None
The hash of the system prompt, or None without one. The text itself is not kept.
tools
class-attribute
instance-attribute
¶
tools: tuple[str, ...] = ()
The names of the Agent's own tools.
mcp_servers
class-attribute
instance-attribute
¶
mcp_servers: tuple[str, ...] = ()
The names of its MCP servers.
ModelEvent
dataclass
¶
ModelEvent(kind: str, message: str, data: Mapping[str, Any] = dict())
Something the Model went through while handling a request, such as falling back to another model or a retry. It is not part of the reply.
A Model reports it by calling on_event in respond or compact. The Agent records it in history
(model_event) and shows it with Reporter.on_model_event.
ToolOutcome
dataclass
¶
ToolOutcome(kind: ToolOutcomeKind, error: BaseException | None = None, decided_by: str | None = None)
How a tool call ended, passed to Reporter.on_tool_end next to the result.
The result is written for the model and may change. Branch on kind instead of reading it.
kind
instance-attribute
¶
kind: ToolOutcomeKind
One of:
DONE: the tool returned, and the result is its outputERROR: the tool returned an error result, which the model sees as an error (an MCP server'sisError)INPUT_ERROR: the tool was not called because the tool is unknown or the arguments failed validationABORTED: the tool raisedINTERRUPTED: closed byKeyboardInterruptorCancelledErrorDENIED: not run because a permission denied it, and the result is the reasonCANCELLED: not run because a permission stopped the turn (another call of the same turn was denied withstop=True)
A plain string such as "denied" given here becomes the matching ToolOutcomeKind.
error
class-attribute
instance-attribute
¶
error: BaseException | None = None
The exception for ABORTED and INTERRUPTED, and the ToolError a permission raised for a
DENIED call. Otherwise None.
decided_by
class-attribute
instance-attribute
¶
decided_by: str | None = None
The repr() of the permission that decided a DENIED or CANCELLED outcome. None for other
kinds, and when no permission decided.
ToolOutcomeKind
¶
Bases: StrEnum
How a tool call ended (ToolOutcome.kind). A StrEnum, so outcome.kind == "denied" works too.
DONE
class-attribute
instance-attribute
¶
DONE = 'done'
The tool returned, and the result is its output.
ERROR
class-attribute
instance-attribute
¶
ERROR = 'error'
The tool returned an error result, which the model sees as an error (an MCP server's isError).
INPUT_ERROR
class-attribute
instance-attribute
¶
INPUT_ERROR = 'input_error'
The tool was not called because the tool is unknown or the arguments failed validation.
INTERRUPTED
class-attribute
instance-attribute
¶
INTERRUPTED = 'interrupted'
Closed by KeyboardInterrupt or CancelledError.
DENIED
class-attribute
instance-attribute
¶
DENIED = 'denied'
Not run because a permission denied it, and the result is the reason.
CANCELLED
class-attribute
instance-attribute
¶
CANCELLED = 'cancelled'
Not run because a permission stopped the turn (another call of the same turn was denied with
stop=True).
Why a loop stopped¶
The values of state.stopped. alpineagents.types.Stopped is their union.
StoppedByUntil
dataclass
¶
StoppedByUntil(name: str)
state.stopped when an until function of @loop returned true. str() gives
stopped by waiting_for_user.
name
instance-attribute
¶
name: str
The function's __name__, e.g. "waiting_for_user" ("<lambda>" for a lambda).
StoppedByLimit
dataclass
¶
StoppedByLimit(turns: int)
state.stopped when the loop ran limit turns in this call. str() gives stopped at limit 30.
StoppedByFinish
dataclass
¶
StoppedByFinish(answer: Any = None)
state.stopped when state.finish() was called. str() gives stopped by finish.
answer
class-attribute
instance-attribute
¶
answer: Any = None
The answer given to finish as JSON (a read-only FrozenDict/FrozenList when it is a dict or
list), or None when finish() had no answer.
StoppedByPermission
dataclass
¶
StoppedByPermission(call: ToolCall, permission: str)
state.stopped when a permission denied a tool call with stop=True. The run ended normally and the
State is not finished: add a user message and run again to continue. str() gives
stopped by permission DecideByHuman().
Content blocks¶
The parts of a Message. ToolCall above is also one.
ToolResultBlock
dataclass
¶
ToolResultBlock(call_id: str, content: ToolResultContent, name: str = '', is_error: bool = False)
The result of one tool call. Goes in user messages only.
content is exactly what is sent to the model: a string (e.g. "(done)", "(input error: ...)",
"(aborted: TimeoutError)", "(cleared: kept in history)", a denial reason), or a tuple of
TextBlock and Image when the tool returned an image.
Image
dataclass
¶
Image(data: bytes, media_type: str | None = None)
An image the model sees: a tool result, or part of a user message.
Return it from a tool, alone or in a list with text:
return Image.from_path("chart.png") or return ["Page loaded in 1.2s", Image(png_bytes)].
Send it to the model with Message.user("What is wrong in this chart?", Image.from_path("chart.png")).
data is the file's bytes. The adapters encode it (base64) when they send it, and a Store saves it the
same way, so a run with many screenshots makes large files. PNG, JPEG, GIF and WebP are supported; the type is
read from the bytes (or pass media_type=).
Both Anthropic and OpenAICompatible send images in user messages. In a tool result, Anthropic takes
images as they are, while Chat Completions APIs take only text there, so OpenAICompatible sends them in a
user message right after, marked as the call's result. Nothing checks that the model can read images: a server
that cannot answers with its own error. agent.context_tokens(state) counts each image as 1,600 tokens, and
state.clear_tool_results() clears images with the rest of old results. A compaction summary keeps only what
the model wrote about them, while history and the store keep them.
Raises:
| Type | Description |
|---|---|
TypeError
|
|
ValueError
|
The data is not a PNG, JPEG, GIF or WebP image, or |
media_type
class-attribute
instance-attribute
¶
media_type: str | None = None
"image/png", "image/jpeg", "image/gif" or "image/webp". Read from data when not
given.
result_text
¶
result_text(content: ToolResultContent) -> str
A tool result as one string for display: the string itself, or the text blocks joined by newlines with
each image as (image/png, 34.2KB).
format_call
¶
format_call(call: ToolCall) -> str
A tool call as one line of code, such as read_file(path="main.py"): the name, then each argument as
key=value with the value as JSON (repr for a value JSON cannot show).
Terminal uses it for its tool lines and DecideByHuman for its question
(Run write_file(path="a.md", content="x")?). Use it to show a call to the person, for example in a
permission or a Reporter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
call
|
ToolCall
|
The tool call. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The one-line form. |
Example
format_call(ToolCall("read_file", {"path": "main.py"}, "call_1")) # 'read_file(path="main.py")'
RawBlock
dataclass
¶
RawBlock(provider: str, data: Mapping[str, Any])
A provider-specific block (thinking blocks, redacted_thinking, server tools, etc.).
data is kept exactly as received and sent back as is to the same provider. Never modified: it is a
read-only dict (a FrozenDict, frozen at every level).