Skip to content

Events Reference

Events are the fundamental communication mechanism in the agent framework. This page provides a complete reference for the event hierarchy, how to choose the right base event, and a catalog of all available events.

Control vs display vs combined events

The framework distinguishes between event categories based on their effect on the workflow:

CategoryBase classTriggers dispatcherUse case
ControlControlEventYesWorkflow state transitions, step dependencies
DisplayDisplayEventNoUI updates, streaming output, observability
Control and DisplayControlAndDisplayEventYesBoth: advances workflow AND shows in UI

Control events influence the workflow's control flow. When a ControlEvent is published, the dispatcher evaluates all steps to determine if any new steps should execute. Only ControlEvent types (or subclasses) can satisfy step input requirements.

Display events do not trigger the dispatcher. Use them for high-frequency updates that should appear in the UI but should not cause step re-evaluation. A streamed LLM response may emit hundreds of ChunkEvent instances per minute; making these control events would cause unnecessary dispatcher overhead.

Combined events need both behaviors — they influence workflow AND appear in the UI. Most semantic events (LLMEvent, RetrieverEvent, etc.) inherit from ControlAndDisplayEvent.

python
from swiss_ai_hub.core.events.control.control_event import ControlEvent
from swiss_ai_hub.core.events.display.display_event import DisplayEvent
from swiss_ai_hub.core.events.control_and_display_event import ControlAndDisplayEvent

# Control event: triggers dispatcher, can satisfy step dependencies
class AnalysisCompleteEvent(ControlEvent):
    summary: str

# Display event: does NOT trigger dispatcher, UI-only
class ProgressUpdateEvent(DisplayEvent):
    percent_complete: float
    status_message: str

# Combined: triggers dispatcher AND displays in UI
class RetrieveEvent(ControlAndDisplayEvent):
    nodes: list[IngestedNode]

Events as flow carriers

Events serve two distinct purposes:

  1. Data carriers: Transporting values between steps
  2. Flow carriers: Controlling execution order independent of data

A step may depend on an event solely to ensure execution ordering, without requiring any data from it:

python
class PathA(ControlEvent):
    pass  # No fields — pure flow control

class PathB(ControlEvent):
    pass

@step()
async def decide(self, event: StartEvent) -> PathA | PathB:
    if condition():
        return PathA()
    return PathB()

@step()
async def handle_path_a(self, _: PathA) -> StopEvent:
    # Underscore signals: "I need this event for flow control, not data"
    return StopEvent()

The _: EventType convention indicates dependency on an event's existence rather than its contents. This pattern is essential for:

  • Conditional branching: Different steps execute based on which event type was emitted
  • Sequencing: Ensuring step B waits for step A without needing A's output data
  • Synchronization barriers: Waiting for a signal that work completed

Event hierarchy

BaseEvent
├── ControlEvent                    # Triggers dispatcher
│   ├── StartEvent                  # Workflow entry points
│   │   └── UserMessageEvent        # User-initiated workflow
│   ├── StopEvent                   # Workflow termination
│   └── ExceptionEvent              # Error signaling

├── DisplayEvent                    # UI-only, no dispatcher trigger
│   ├── ChunkEvent                  # Streaming text chunks
│   ├── ThoughtEvent                # Agent reasoning display
│   └── CostEvent                   # Cost tracking display
│       └── LLMCostEvent            # LLM-specific costs

├── ControlAndDisplayEvent          # Both behaviors
│   ├── SemanticEvent               # OpenInference-compatible (OTEL/Phoenix/Langfuse)
│   │   ├── LLMEvent                # LLM invocation
│   │   │   └── LLMStopEvent        # Terminal LLM response
│   │   ├── RetrieverEvent          # Document retrieval
│   │   ├── RerankerEvent           # Result reranking
│   │   ├── EmbeddingEvent          # Embedding generation
│   │   ├── ToolEvent               # Tool invocation
│   │   ├── GuardEvent              # Guardrail evaluation
│   │   ├── ChainEvent              # Chain execution
│   │   └── AgentEvent              # Agent invocation
│   │
│   ├── HumanInTheLoopRequestEvent  # HITL requests
│   ├── HumanInTheLoopResponseEvent # HITL responses
│   ├── AgentInTheLoopRequestEvent  # AITL delegation
│   ├── AgentInTheLoopResponseEvent # AITL results
│   ├── BotInTheLoopRequestEvent    # BITL Teams/Slack requests
│   ├── BotInTheLoopResponseEvent   # BITL responses
│   │
│   ├── BaseRetrieveMemoryEvent     # Memory retrieval
│   │   ├── RetrieveUserMemoryEvent
│   │   └── RetrieveOrganizationMemoryEvent
│   ├── BaseStoreMemoryEvent        # Memory persistence
│   │   ├── StoreUserMemoryEvent
│   │   └── StoreOrganizationMemoryEvent
│   │
│   └── RouterEvent                 # LLM routing decisions

Choosing the right base event

When creating a custom event, inherit from the most specific applicable base class:

If your event represents...Inherit fromBenefits
Workflow start conditionStartEventRecognized as entry point
User message initiating workflowUserMessageEventChat history, locale, user identity
Workflow terminationStopEventSignals completion
Error/failureExceptionEventError handling patterns
LLM invocation resultLLMEventToken counts, messages, OpenInference spans
LLM terminal responseLLMStopEventCombines LLM data with workflow termination
Document retrievalRetrieverEventRetrieved nodes, OpenInference spans
Reranking operationRerankerEventInput/output nodes, OpenInference spans
Embedding generationEmbeddingEventVectors, model info, OpenInference spans
Tool/function callToolEventTool name, parameters, OpenInference spans
Guardrail checkGuardEventGuard result, OpenInference spans
Human approval neededHumanInTheLoopRequestEventWorkflow suspension, UI prompt
Agent delegationAgentInTheLoopRequestEventCross-agent communication
Memory retrievalRetrieveUserMemoryEvent / RetrieveOrganizationMemoryEventMemory search results
Memory storageStoreUserMemoryEvent / StoreOrganizationMemoryEventMemory persistence confirmation
Streaming text chunkChunkEventReal-time UI updates (display-only)
Agent thought/reasoningThoughtEventTransparency display (display-only)
Cost informationLLMCostEventToken usage, pricing (display-only)
Generic workflow stateControlAndDisplayEventTriggers dispatcher + UI display
Generic UI updateDisplayEventUI-only, no dispatcher overhead

Semantic events and OpenInference

SemanticEvent subclasses implement the to_semantic_convention() method, producing attributes compatible with the OpenInference specification. This enables integration with:

  • Arize Phoenix: Trace visualization and debugging
  • Langfuse: LLM observability and analytics
  • Any OpenTelemetry-compatible system: Standard span attributes
python
from swiss_ai_hub.core.events.semantic import RetrieverEvent

# RetrieverEvent automatically exports OpenInference attributes:
# - openinference.span.kind: "RETRIEVER"
# - retrieval.documents.{i}.document.id
# - retrieval.documents.{i}.document.content
# - retrieval.documents.{i}.document.score

event = RetrieverEvent.from_nodes(retrieved_nodes)
otel_attributes = event.to_semantic_convention()

When building agents that require observability, prefer semantic events over generic ControlAndDisplayEvent:

python
# Preferred: semantic event for observability
from swiss_ai_hub.core.events.semantic import RetrieverEvent

@step()
async def retrieve(self, event: UserMessageEvent) -> RetrieverEvent:
    nodes = await retriever.retrieve(event.user_query)
    return RetrieverEvent.from_nodes(nodes)  # OpenInference-compatible

# Discouraged: generic event loses observability benefits
class MyRetrieveEvent(ControlAndDisplayEvent):
    nodes: list[NodeWithScore]  # No OpenInference integration

Available events reference

Control events (trigger dispatcher)

EventModulePurpose
ControlEventcontrol.ControlEventBase class for all control events
StartEventcontrol.start.StartEventWorkflow entry point
StopEventcontrol.stop.StopEventWorkflow termination
ExceptionEventcontrol.exception.ExceptionEventError signaling

Display events (UI only)

EventModulePurpose
DisplayEventdisplay.DisplayEventBase class for display events
ChunkEventdisplay.ChunkEventStreaming text output
ThoughtEventdisplay.ThoughtEventAgent reasoning transparency
CostEventcost.CostEventCost tracking base
LLMCostEventcost.LLMCostEventLLM token/cost reporting

Semantic events (OpenInference compatible)

EventModuleOpenInference span kind
SemanticEventsemantic.SemanticEventBase class (abstract)
LLMEventsemantic.llm.LLMEventLLM
LLMStopEventsemantic.llm.LLMStopEventLLM (terminal)
RetrieverEventsemantic.retriever.RetrieverEventRETRIEVER
RerankerEventsemantic.reranker.RerankerEventRERANKER
EmbeddingEventsemantic.embedding.EmbeddingEventEMBEDDING
ToolEventsemantic.tool.ToolEventTOOL
GuardEventsemantic.guard.GuardEventGUARDRAIL
ChainEventsemantic.chain.ChainEventCHAIN
AgentEventsemantic.agent.AgentEventAGENT

Interaction events

EventModulePurpose
UserMessageEventuser.UserMessageEventUser-initiated workflow start
HumanInTheLoopRequestEventhuman_in_the_loop.requestBase class for HITL requests
HumanInTheLoopInputRequestEventhuman_in_the_loop.requestPopup with text input field
HumanInTheLoopConfirmationRequestEventhuman_in_the_loop.requestYes/No button selection
HumanInTheLoopChatRequestEventhuman_in_the_loop.requestChat message (fallback)
HumanInTheLoopResponseEventhuman_in_the_loop.responseBase class for HITL responses
AgentInTheLoopRequestEventagent_in_the_loop.requestDelegate to another agent
AgentInTheLoopResponseEventagent_in_the_loop.responseDelegated agent result
AgentInTheLoopExceptionEventagent_in_the_loop.exceptionDelegated agent failure
BotInTheLoopRequestEventbot_in_the_loop.requestSend message to Teams/Slack channel
BotInTheLoopResponseEventbot_in_the_loop.responseResponse from Teams/Slack user

HITL helper classes (not events, but workflow utilities):

HelperUI behavior
HumanInTheLoopInputPopup dialog for free-form text entry
HumanInTheLoopConfirmationYes/No button selection
HumanInTheLoopChatMessage in chat stream (fallback for simple UIs/APIs)

Memory events

EventModulePurpose
BaseRetrieveMemoryEventmemory.retrieveMemory retrieval base
RetrieveUserMemoryEventmemory.retrieveUser-scoped memory retrieval
RetrieveOrganizationMemoryEventmemory.retrieveOrg-scoped memory retrieval
BaseStoreMemoryEventmemory.storeMemory storage base
StoreUserMemoryEventmemory.storeUser-scoped memory storage
StoreOrganizationMemoryEventmemory.storeOrg-scoped memory storage
AddMemoryToChatHistoryEventmemory.historyExtended chat history

Guard events

EventModulePurpose
GuardAcceptEventguard.GuardAcceptEventGuard passed
GuardRejectionEventguard.GuardRejectionEventGuard rejected
AgentSuitabilityAcceptEventguardAgent can handle request
AgentSuitabilityRejectEventguardAgent cannot handle request
ContextSufficientAcceptEventguardSufficient context available
ContextInsufficientRejectEventguardInsufficient context
SensitiveInfoAcceptEventguardNo sensitive info detected
SensitiveInfoRejectEventguardSensitive info detected

Utility events

EventModulePurpose
RouterEventrouter.RouterEventLLM routing decision
LanguageEventcommon.LanguageEventLanguage detection
LimitChatHistoryEventcommon.LimitChatHistoryEventTruncated history
StandaloneQuestionCondenserEventcommonQuestion reformulation

Custom events

Custom events are Pydantic models. All fields require type annotations:

python
from swiss_ai_hub.core.events.control.control_event import ControlEvent

class AnalysisCompleteEvent(ControlEvent):
    summary: str
    confidence: float
    findings: list[str]
    metadata: dict | None = None

The stop event constraint

No step may depend on StopEvent or any subclass as an input parameter. When a StopEvent is emitted, the run terminates and subsequent steps are not scheduled. See the execution model for details and the correct pattern.

Built with ❤️ in Switzerland 🇨🇭