|
| 1 | +--- |
| 2 | +title: Events |
| 3 | +description: High-level architecture of the typed event framework |
| 4 | +--- |
| 5 | + |
| 6 | +The **Event System** provides an immutable, type-safe event framework that drives agent execution and state management. Events form an append-only log that serves as both the agent's memory and the integration point for auxiliary services. |
| 7 | + |
| 8 | +**Source:** [`openhands-sdk/openhands/sdk/event/`](https://github.com/All-Hands-AI/agent-sdk/tree/main/openhands-sdk/openhands/sdk/event) |
| 9 | + |
| 10 | +## Core Responsibilities |
| 11 | + |
| 12 | +The Event System has four primary responsibilities: |
| 13 | + |
| 14 | +1. **Type Safety** - Enforce event schemas through Pydantic models |
| 15 | +2. **LLM Integration** - Convert events to/from LLM message formats |
| 16 | +3. **Append-Only Log** - Maintain immutable event history |
| 17 | +4. **Service Integration** - Enable observers to react to event streams |
| 18 | + |
| 19 | +## Architecture |
| 20 | + |
| 21 | +```mermaid |
| 22 | +%%{init: {"theme": "default", "flowchart": {"nodeSpacing": 25, "rankSpacing": 80}} }%% |
| 23 | +flowchart TB |
| 24 | + Base["Event<br><i>Base class</i>"] |
| 25 | + LLMBase["LLMConvertibleEvent<br><i>Abstract base</i>"] |
| 26 | + |
| 27 | + subgraph LLMTypes["LLM-Convertible Events<br><i>Visible to the LLM</i>"] |
| 28 | + Message["MessageEvent<br><i>User/assistant text</i>"] |
| 29 | + Action["ActionEvent<br><i>Tool calls</i>"] |
| 30 | + System["SystemPromptEvent<br><i>Initial system prompt</i>"] |
| 31 | + CondSummary["CondensationSummaryEvent<br><i>Condenser summary</i>"] |
| 32 | + |
| 33 | + ObsBase["ObservationBaseEvent<br><i>Base for tool responses</i>"] |
| 34 | + Observation["ObservationEvent<br><i>Tool results</i>"] |
| 35 | + UserReject["UserRejectObservation<br><i>User rejected action</i>"] |
| 36 | + AgentError["AgentErrorEvent<br><i>Agent error</i>"] |
| 37 | + end |
| 38 | + |
| 39 | + subgraph Internals["Internal Events<br><i>NOT visible to the LLM</i>"] |
| 40 | + ConvState["ConversationStateUpdateEvent<br><i>State updates</i>"] |
| 41 | + CondReq["CondensationRequest<br><i>Request compression</i>"] |
| 42 | + Cond["Condensation<br><i>Compression result</i>"] |
| 43 | + Pause["PauseEvent<br><i>User pause</i>"] |
| 44 | + end |
| 45 | + |
| 46 | + Base --> LLMBase |
| 47 | + Base --> Internals |
| 48 | + LLMBase --> LLMTypes |
| 49 | + ObsBase --> Observation |
| 50 | + ObsBase --> UserReject |
| 51 | + ObsBase --> AgentError |
| 52 | + |
| 53 | + classDef primary fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px |
| 54 | + classDef secondary fill:#e8f3ff,stroke:#2b6cb0,stroke-width:2px |
| 55 | + classDef tertiary fill:#fff4df,stroke:#b7791f,stroke-width:2px |
| 56 | + |
| 57 | + class Base,LLMBase,Message,Action,SystemPromptEvent primary |
| 58 | + class ObsBase,Observation,UserReject,AgentError secondary |
| 59 | + class ConvState,CondReq,Cond,Pause tertiary |
| 60 | +``` |
| 61 | + |
| 62 | +### Key Components |
| 63 | + |
| 64 | +| Component | Purpose | Design | |
| 65 | +|-----------|---------|--------| |
| 66 | +| **[`Event`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/base.py)** | Base event class | Immutable Pydantic model with ID, timestamp, source | |
| 67 | +| **[`LLMConvertibleEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/base.py)** | LLM-compatible events | Abstract class with `to_llm_message()` method | |
| 68 | +| **[`MessageEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/message.py)** | Text messages | User or assistant conversational messages with skills | |
| 69 | +| **[`ActionEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/action.py)** | Tool calls | Agent tool invocations with thought, reasoning, security risk | |
| 70 | +| **[`ObservationBaseEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/observation.py)** | Tool response base | Base for all tool call responses | |
| 71 | +| **[`ObservationEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/observation.py)** | Tool results | Successful tool execution outcomes | |
| 72 | +| **[`UserRejectObservation`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/observation.py)** | User rejection | User rejected action in confirmation mode | |
| 73 | +| **[`AgentErrorEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/observation.py)** | Agent errors | Errors from agent/scaffold (not model output) | |
| 74 | +| **[`SystemPromptEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/system.py)** | System context | System prompt with tool schemas | |
| 75 | +| **[`CondensationSummaryEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/condenser.py)** | Condenser summary | LLM-convertible summary of forgotten events | |
| 76 | +| **[`ConversationStateUpdateEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/conversation_state.py)** | State updates | Key-value conversation state changes | |
| 77 | +| **[`Condensation`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/condenser.py)** | Condensation result | Events being forgotten with optional summary | |
| 78 | +| **[`CondensationRequest`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/condenser.py)** | Request compression | Trigger for conversation history compression | |
| 79 | +| **[`PauseEvent`](https://github.com/All-Hands-AI/agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/user_action.py)** | User pause | User requested pause of agent execution | |
| 80 | + |
| 81 | +## Event Types |
| 82 | + |
| 83 | +### LLM-Convertible Events |
| 84 | + |
| 85 | +Events that participate in agent reasoning and can be converted to LLM messages: |
| 86 | + |
| 87 | + |
| 88 | +| Event Type | Source | Content | LLM Role | |
| 89 | +|------------|--------|---------|----------| |
| 90 | +| **MessageEvent (user)** | user | Text, images | `user` | |
| 91 | +| **MessageEvent (agent)** | agent | Text reasoning, skills | `assistant` | |
| 92 | +| **ActionEvent** | agent | Tool call with thought, reasoning, security risk | `assistant` with `tool_calls` | |
| 93 | +| **ObservationEvent** | environment | Tool execution result | `tool` | |
| 94 | +| **UserRejectObservation** | environment | Rejection reason | `tool` | |
| 95 | +| **AgentErrorEvent** | agent | Error details | `tool` | |
| 96 | +| **SystemPromptEvent** | agent | System prompt with tool schemas | `system` | |
| 97 | +| **CondensationSummaryEvent** | environment | Summary of forgotten events | `user` | |
| 98 | + |
| 99 | +The event system bridges agent events to LLM messages: |
| 100 | + |
| 101 | +```mermaid |
| 102 | +%%{init: {"theme": "default", "flowchart": {"nodeSpacing": 30}} }%% |
| 103 | +flowchart LR |
| 104 | + Events["Event List"] |
| 105 | + Filter["Filter LLMConvertibleEvent"] |
| 106 | + Group["Group ActionEvents<br>by llm_response_id"] |
| 107 | + Convert["Convert to Messages"] |
| 108 | + LLM["LLM Input"] |
| 109 | + |
| 110 | + Events --> Filter |
| 111 | + Filter --> Group |
| 112 | + Group --> Convert |
| 113 | + Convert --> LLM |
| 114 | + |
| 115 | + style Filter fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px |
| 116 | + style Group fill:#fff4df,stroke:#b7791f,stroke-width:2px |
| 117 | + style Convert fill:#e8f3ff,stroke:#2b6cb0,stroke-width:2px |
| 118 | +``` |
| 119 | + |
| 120 | +**Special Handling - Parallel Function Calling:** |
| 121 | + |
| 122 | +When multiple `ActionEvent`s share the same `llm_response_id` (parallel function calling): |
| 123 | +1. Group all ActionEvents by `llm_response_id` |
| 124 | +2. Combine into single Message with multiple `tool_calls` |
| 125 | +3. Only first event's `thought`, `reasoning_content`, and `thinking_blocks` are included |
| 126 | +4. All subsequent events in the batch have empty thought fields |
| 127 | + |
| 128 | +**Example:** |
| 129 | +``` |
| 130 | +ActionEvent(llm_response_id="abc123", thought="Let me check...", tool_call=tool1) |
| 131 | +ActionEvent(llm_response_id="abc123", thought=[], tool_call=tool2) |
| 132 | +→ Combined into single Message(role="assistant", content="Let me check...", tool_calls=[tool1, tool2]) |
| 133 | +``` |
| 134 | + |
| 135 | + |
| 136 | +### Internal Events |
| 137 | + |
| 138 | +Events for metadata, control flow, and user actions (not sent to LLM): |
| 139 | + |
| 140 | +| Event Type | Source | Purpose | Key Fields | |
| 141 | +|------------|--------|---------|------------| |
| 142 | +| **ConversationStateUpdateEvent** | environment | State synchronization | `key` (field name), `value` (serialized data) | |
| 143 | +| **CondensationRequest** | environment | Trigger history compression | Signal to condenser when context window exceeded | |
| 144 | +| **Condensation** | environment | Compression result | `forgotten_event_ids`, `summary`, `summary_offset` | |
| 145 | +| **PauseEvent** | user | User pause action | Indicates agent execution was paused by user | |
| 146 | + |
| 147 | +**Source Types:** |
| 148 | +- **user**: Event originated from user input |
| 149 | +- **agent**: Event generated by agent logic |
| 150 | +- **environment**: Event from system/framework/tools |
| 151 | + |
| 152 | +## Component Relationships |
| 153 | + |
| 154 | +### How Events Integrate |
| 155 | + |
| 156 | +```mermaid |
| 157 | +%%{init: {"theme": "default", "flowchart": {"nodeSpacing": 30}} }%% |
| 158 | +flowchart LR |
| 159 | + Events["Event System"] |
| 160 | + Agent["Agent"] |
| 161 | + Conversation["Conversation"] |
| 162 | + Tools["Tools"] |
| 163 | + Services["Auxiliary Services"] |
| 164 | + |
| 165 | + Agent -->|Reads| Events |
| 166 | + Agent -->|Writes| Events |
| 167 | + Conversation -->|Manages| Events |
| 168 | + Tools -->|Creates| Events |
| 169 | + Events -.->|Stream| Services |
| 170 | + |
| 171 | + style Events fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px |
| 172 | + style Agent fill:#e8f3ff,stroke:#2b6cb0,stroke-width:2px |
| 173 | + style Conversation fill:#fff4df,stroke:#b7791f,stroke-width:2px |
| 174 | +``` |
| 175 | + |
| 176 | +**Relationship Characteristics:** |
| 177 | +- **Agent → Events**: Reads history for context, writes actions/messages |
| 178 | +- **Conversation → Events**: Owns and persists event log |
| 179 | +- **Tools → Events**: Create ObservationEvents after execution |
| 180 | +- **Services → Events**: Read-only observers for monitoring, visualization |
| 181 | + |
| 182 | +## See Also |
| 183 | + |
| 184 | +- **[Agent Architecture](/sdk/arch/agent)** - How agents read and write events |
| 185 | +- **[Conversation Architecture](/sdk/arch/conversation)** - Event log management |
| 186 | +- **[Tool System](/sdk/arch/tool-system)** - ActionEvent and ObservationEvent generation |
| 187 | +- **[Condenser](/sdk/arch/condenser)** - Event history compression |
0 commit comments