Skip to main content
Agents in ai-query are designed to be real-time. Instead of just waiting for a final response, agents can emit events throughout their execution to keep users informed. The event system is built on three core pillars:
  1. Transport Agnostic - Emit once, deliver via SSE, WebSocket, or HTTP.
  2. Durable - Events are persisted to storage (SQLite, Postgres, etc.).
  3. Resumable - Clients can reconnect and automatically replay missed events.

The emit() API

The implementation is simple. Any method in your Agent subclass can call self.emit():
  • event: A string identifying the event type (e.g., “status”, “log”, “chunk”)
  • data: A JSON-serializable dictionary with the payload
  • Returns: A unique, strictly increasing integer ID for the event

Basic Example

Event Persistence & Replay

One of the most powerful features is Automatic Event Replay. In distributed systems, connections drop. Mobile users lose signal. If an agent emits an event while the user is disconnected, that information is usually lost. ai-query solves this with the enable_event_log flag.
When enabled:
  1. Every emitted event is saved to the agent’s storage.
  2. Events are assigned a strictly increasing ID (1, 2, 3…).
  3. Clients can connect with a last_event_id parameter.
  4. The server automatically sends all events with ID > last_event_id before sending new ones.
This guarantees exactly-once delivery semantics for your UI, even on flaky networks.

Consuming Events

The way you consume events depends on the transport.

Server-Sent Events (SSE)

The recommended way to consume events is via an SSE stream.
  • AgentServer: Exposes a long-running SSE stream at GET /agent/{id}/events.
  • Serverless/Adapters: Chat streaming is done via POST /agent/{id}/chat?stream=true. Emitted events are injected into this response stream.
JavaScript Client:
Python Client (connect) The connect() client handles SSE transparently for streaming.

WebSocket

If you connect via WebSocket (/agent/{agent_id}/ws), events are sent as JSON messages:

Custom Transports

If you use FastAPI or another framework without our adapters, you can inject a custom handler to deliver events however you like (e.g., to a message queue or a custom socket).