Skip to main content
The event stream system lets you observe the full ReAct loop in real time instead of only seeing the final response. It works with both @llm_chat and @llm_function and is one of the key pieces behind TUI rendering, progress visualization, tool monitoring, and custom telemetry.

Quick Overview

LLM Call Events

Observe model-call start, streaming chunks, and completion.

Tool Call Events

Observe tool start, completion, batching, and failure details.

Execution Metrics

Inspect token usage, latency, call counts, and runtime statistics.

Custom UI

Drive progress bars, live message panes, and tool activity panels.

State Management

Build wrappers for context compression, persistence, or custom orchestration.

Why It Matters

Without event streaming, you usually only get a final result. With event streaming, you can:
  1. monitor model and tool activity in real time
  2. collect detailed performance metrics
  3. build richer interactive interfaces
  4. debug the ReAct loop more effectively
  5. add wrapper-based state management around otherwise stateless functions

Stateless Agent Design

SimpleLLMFunc treats the Agent itself as a function, not as a stateful object. That means:
  • @llm_chat functions are fundamentally stateless
  • state such as history is passed in and returned explicitly
  • advanced behavior can be layered through wrappers instead of hidden inside the decorator
This keeps the system easier to test, reason about, and compose.

Enabling Event Streaming

Event streaming works for both @llm_chat and @llm_function. In both cases, the output type is ReactOutput.

llm_chat example

from SimpleLLMFunc import llm_chat
from SimpleLLMFunc.hooks import EventYield, ResponseYield


@llm_chat(
    llm_interface=llm,
    toolkit=[calculate, get_weather],
    stream=True,
    enable_event=True,
)
async def chat(message: str, history=None):
    """You are a capable assistant."""
    pass


async for output in chat("Hello"):
    if isinstance(output, ResponseYield):
        print(output.response)
    elif isinstance(output, EventYield):
        print(output.event.event_type)

llm_function example

from SimpleLLMFunc import llm_function
from SimpleLLMFunc.hooks import EventYield, ResponseYield


@llm_function(
    llm_interface=llm,
    toolkit=[calculate, get_weather],
    enable_event=True,
)
async def analyze_text(text: str) -> str:
    """Analyze text content."""
    pass


async for output in analyze_text("Hello world"):
    if isinstance(output, ResponseYield):
        print(output.response)
    elif isinstance(output, EventYield):
        print(output.event.event_type)

Return Shapes

async for chunk, updated_history in chat("Hello"):
    print(chunk)

Core Types

ReactOutput

The common event-stream output type: ResponseYield | EventYield.

ResponseYield

Carries model output plus the updated message list.

EventYield

Carries an intermediate event plus origin metadata.

EventOrigin

Describes where an event came from within the call tree.

EventOrigin

EventOrigin helps you understand where an event came from in nested or forked execution. Useful fields include:
  • session_id
  • agent_call_id
  • parent_agent_call_id
  • event_seq
  • fork_id
  • fork_depth
  • fork_seq
  • selfref_instance_id
  • memory_key / source_memory_key
  • tool_name / tool_call_id
Example:
from SimpleLLMFunc.hooks import is_event_yield

async for output in chat("Please split this task into parallel sub-jobs"):
    if not is_event_yield(output):
        continue

    if output.origin.fork_id:
        print(f"[fork:{output.origin.fork_id}] {output.event.event_type}")
    else:
        print(f"[main] {output.event.event_type}")

How to Read This Page

Start Here

If event streaming is new to you, start with enablement and return shapes.

Core Types

If you need to build on the API, understand ReactOutput, ResponseYield, EventYield, and EventOrigin first.

Use Cases

If you care about UI, telemetry, or wrappers, jump to the practical patterns.

FAQ

If you are troubleshooting, jump to the end of the page.

Common Use Cases

Use event streaming to update message panes, progress states, and tool activity in real time.
Observe timing, tool counts, and token usage from event payloads.
Use output.origin.fork_id and related fields to route events to the correct visual lane or collector.
Use wrapper functions to compress history, persist state externally, or intercept and rewrite inputs and outputs.

Best Practices

Use enable_event=True when debugging Agent behavior, tool usage, or output generation.
If you add stateful behavior, implement it in wrapper functions so state flow remains understandable.
Treat EventOrigin as the canonical source of truth when rendering or collecting multi-branch events.
Model, tool, custom, and runtime events can all coexist in the same stream.

FAQ

Yes. Both @llm_chat and @llm_function support it.
No. Event streaming adds observability, not hidden internal state.
Yes, but do it in a wrapper around the decorated function instead of expecting the decorator itself to manage state for you.
Inspect output.origin.fork_id, fork_depth, and related metadata.