---
title: "Events & Streaming"
description: "Monitor execution in real-time and handle streaming results"
---

## The Event Stream

In the previous [examples](./runnables.mdx#parameter-handling-and-basic-execution), we used `.collect()` to get the final result. When you call a Runnable, it doesn't just return the answer - it returns a stream of events that tell you what's happening step by step (an async generator). The `.collect()` method waits for all events and gives you just the final answer.

You can iterate through the async generator to process events in real-time:

```python
async for event in add_tool(a=5, b=3):
    print(event)

# Output:
# StartEvent(run_id="068c4458382e79bb80006dc019ac3039", ...)
# OutputEvent(run_id="068c4458382e79bb80006dc019ac3039", output=8, ...)
```

This enables you to:
- Monitor execution progress in real-time
- Handle streaming responses from LLMs
- Debug execution flow
- Build reactive user interfaces

## Event Logging

In reality, you don't need to manually print events. By default, events are logged to standard output by the framework. You can control logging behavior with these environment variables:

- `TIMBAL_LOG_EVENTS`: Which events to log (default: `"START,OUTPUT"`)
- `TIMBAL_LOG_FORMAT`: Log format - `"dev"` for human-readable or `"json"` for structured (default)
- `TIMBAL_LOG_LEVEL`: Standard log level (default: `"INFO"`)

## Event Types

Events are the communication mechanism that Runnables use to stream information throughout their execution lifecycle. Every Runnable execution produces a sequence of events that can be consumed in real-time or collected for later processing. 

Events are designed to be lightweight. For comprehensive execution data, see [Traces](./tracing.mdx).

Every execution produces at least a **Start** event (when it begins) and an **Output** event (when it finishes). LLMs and streaming operations also produce **Chunk** events for intermediate results.

The following examples show what these events look like for an LLM interaction:

### Start Event

Signals the beginning of an execution. 

```python
from timbal.types.events import StartEvent

start_event = StartEvent(
    run_id="068c4458382e79bb80006dc019ac3039",
    parent_run_id=None, 
    path="agent.llm",
    call_id="068c4458383678be800031537a8df42e",
    parent_call_id="068c4458382e708f8000cc0f9b19d970",
)
```

<Note>
These fields are present in all events to identify their source - **the framework handles this automatically**. We'll explore this in greater detail in advanced sections.
</Note>

### Chunk Event

Contains streaming or intermediate results during execution. The `chunk` property contains each piece of the response as it's generated during streaming.

```python
from timbal.types.events import ChunkEvent

chunk_event = ChunkEvent(
    run_id="068c4458382e79bb80006dc019ac3039",
    parent_run_id=None,
    path="agent.llm", 
    call_id="068c4458383678be800031537a8df42e",
    parent_call_id="068c4458382e708f8000cc0f9b19d970",
    chunk="Hello"
)
```

### Output Event

Contains the final result and signals completion. Contains either the final `output` result (if successful) or `error` information (if something went wrong).

```python
from timbal.types.events import OutputEvent

output_event = OutputEvent(
    run_id="068c4458382e79bb80006dc019ac3039",
    parent_run_id=None,
    path="agent.llm",
    call_id="068c4458383678be800031537a8df42e",
    parent_call_id="068c4458382e708f8000cc0f9b19d970",
    output={
        "role": "assistant",
        "content": [{"type": "text", "text": "Hello! How can I assist you today?"}]
    },
    error=None
)
```

## Handling Errors

When a Runnable encounters an error during execution, it **won't raise an exception**. Instead, it will always return an `OutputEvent` with error information. This ensures the event stream continues and you can handle errors gracefully:

```python
try:
    result = await runnable(**kwargs).collect()
except Exception as e:
    # This won't happen - errors are in the OutputEvent
    pass

# Result will always be an instance of OutputEvent
if result.error:
    print(f"Error: {result.error['message']}")
```

Errors will always have this structure:

```python
error = {
    "type": "ValueError",
    "message": "Invalid input provided",
    "traceback": "Traceback (most recent call last):\\n..."
}
```
