> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orxhestra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming

> Stream token-by-token events from agents in real-time. Sub-agent streaming via AgentTool with branch isolation and event bubbling.

All agents stream by default - `astream()` yields `Event` objects as they occur:

```python theme={null}
from orxhestra.events.event import Event, EventType

async for event in agent.astream("Write about distributed systems"):
    if event.type == EventType.AGENT_MESSAGE and event.partial:
        # Token-by-token streaming
        print(event.text, end="", flush=True)
    elif event.is_final_response():
        print(f"\n[DONE] {event.text}")
```

Partial events are suppressed automatically if the LLM decides to call a tool instead of answering - only real text chunks are streamed.

## Sub-agent streaming via AgentTool

When using `AgentTool`, sub-agent events stream through the parent in real-time via `ctx.event_callback`. Events carry `branch` and `agent_name` fields so you can distinguish which sub-agent is producing output:

```python theme={null}
from orxhestra import LlmAgent
from orxhestra.tools.agent_tool import AgentTool

weather_agent = LlmAgent(name="WeatherAgent", model=model, tools=[get_weather])
travel_agent = LlmAgent(name="TravelAgent", model=model, tools=[get_attractions])

planner = LlmAgent(
    name="TripPlanner",
    model=model,
    tools=[AgentTool(weather_agent), AgentTool(travel_agent)],
    instructions="Use the sub-agents to plan a trip.",
)

async for event in planner.astream("Plan a trip to Copenhagen"):
    if event.branch:
        # Event from a sub-agent (e.g. branch="WeatherAgent")
        print(f"  [{event.agent_name}] {event.text}", end="")
    elif event.is_final_response():
        print(f"\nFinal: {event.text}")
```

Sub-agents run in parallel when the LLM calls multiple tools at once - their events are interleaved in the stream as they arrive.

## How it works

1. Before tool execution, `LlmAgent` creates an `asyncio.Queue` and sets `ctx.event_callback = queue.put_nowait` on the tool context.
2. `AgentTool` calls `ctx.event_callback(event)` for each child event as it streams.
3. `LlmAgent` yields events from the queue concurrently while tools run, so events appear in real-time.
4. The `event_callback` propagates through `ctx.derive()`, so nested sub-agents also push events up to the root.

Any custom tool can use `ctx.event_callback` to push events - it's not limited to `AgentTool`.

## With Runner

```python theme={null}
async for event in runner.astream(
    user_id="user-1",
    session_id="session-1",
    new_message="Write me a long essay about distributed systems.",
):
    if event.is_final_response():
        print(f"\n[DONE] {event.text}")
    elif event.type == EventType.AGENT_MESSAGE and event.partial:
        print(event.text, end="", flush=True)
```
