Results

What agent.run and arun return — the Result object, its output, usage, and message transcript, plus the forthcoming streaming events.


Every call to an agent hands you back a single value: a Result. It carries the final output, the token-and-cost usage for the run, and the full message transcript so you can inspect exactly what happened.

What run and arun return

Agent.run (sync) and Agent.arun (async) both return a Result. run is a thin wrapper around arun via asyncio.run, so the two share the same return type.

from agentroute import Agent
 
agent = Agent("assistant", model="claude-sonnet-4")
result = agent.run("What is the capital of France?")
 
print(result)  # Paris.

Result is a dataclass with four fields:

@dataclass
class Result:
    output: Any
    usage: Usage = Usage()
    messages: list[dict] = []
    interrupted: bool = False
  • output — the agent's final answer. A str by default, or an instance of your output model when you set output= on the agent.
  • usage — accumulated tokens, cost, and model-call count for the whole run. See Context and usage.
  • messages — the complete transcript (system, user, assistant, and tool messages) as OpenAI-compatible dicts.
  • interruptedTrue when the run stopped early (for example, a pending tool approval).
Tip

Result.__str__ returns the empty string when output is None, otherwise str(output). That means print(result) shows the text directly — no need to reach for result.output in throwaway scripts.

Reading the output

For a plain text agent, result.output is the model's final message as a string:

result = agent.run("Summarize photosynthesis in one sentence.")
text = result.output  # str
print(text)

When the agent is configured with a Pydantic output= model, result.output is an instance of that model — already validated and typed:

from pydantic import BaseModel
from agentroute import Agent
 
 
class Weather(BaseModel):
    city: str
    temp_c: float
    summary: str
 
 
agent = Agent("forecaster", model="claude-sonnet-4", output=Weather)
result = agent.run("What's the weather in Berlin right now?")
 
print(result.output.city)      # Berlin
print(result.output.temp_c)    # 18.0
print(type(result.output))     # <class '__main__.Weather'>

See Structured output for how output= and output_mode shape the response.

Inspecting usage

result.usage is a Usage covering the entire run — every model call across every turn is summed in.

result = agent.run("Research and compare three databases.")
 
print(result.usage.input_tokens)    # e.g. 4210
print(result.usage.output_tokens)   # e.g. 880
print(result.usage.total_cost_usd)  # e.g. 0.0123
print(result.usage.model_calls)     # e.g. 3

Usage is the same object exposed inside tools as ctx.usage. The agentic loop accumulates into it turn by turn — read Context and usage for the full picture.

Inspecting the transcript

result.messages is the full conversation as a list of OpenAI-compatible dicts: the system instructions, the user prompt, each assistant turn (including tool calls), and each tool result. It is the source of truth for debugging what the model actually saw and did.

result = agent.run("What's 19 * 23? Use the calculator tool.")
 
for msg in result.messages:
    role = msg["role"]
    if msg.get("tool_calls"):
        print(role, "->", msg["tool_calls"])
    else:
        print(role, ":", msg.get("content"))
 
# system : You are a helpful assistant.
# user : What's 19 * 23? Use the calculator tool.
# assistant -> [{'id': 'call_1', 'function': {'name': 'calculator', ...}}]
# tool : 437
# assistant : 19 * 23 is 437.

Each dict mirrors the Message shape: role, content, and optionally tool_calls, tool_call_id, and name. You can persist this list, feed it to a history compaction policy, or replay it for audits.

The interrupted flag

result.interrupted is True when the loop stopped before producing a final answer — for example, when a tool marked needs_approval is waiting on a human. When it is False, output holds the model's final response.

result = agent.run("Delete the staging database.")
if result.interrupted:
    print("Run paused — awaiting approval.")
else:
    print(result.output)

Streaming events

Token-by-token streaming is exposed through an Event type and an EventKind literal:

EventKind = Literal[
    "text_delta",
    "tool_call_start",
    "tool_call_end",
    "agent_start",
    "agent_end",
]
 
 
@dataclass
class Event:
    kind: EventKind
    data: dict = {}
Streaming is forthcoming

The Event and EventKind types are defined today, but a streaming agent.stream() API that yields them is not yet available. For now, use run / arun and read result.output. Streaming will land in a later phase.

Deployment results

A ResultDeploy type is reserved for a future Agent.deploy():

@dataclass
class ResultDeploy:
    url: str
    status: str = "pending"
    agent_card_url: str | None = None
Deployment is a placeholder

ResultDeploy is a placeholder return value — Agent.deploy() is not implemented yet. Follow the platform roadmap for when hosted deployment ships.

Next steps