Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 92 additions & 10 deletions docs/content/docs/development/workflow_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,37 +491,119 @@ To use async execution on JDK 21+, user should append jvm option `--add-exports=
{{< /tabs >}}

## Event
Events are messages passed between actions. Events may carry payloads. A single event may trigger multiple actions if they are all listening to its type.

There are 2 special types of event.
* `InputEvent`: Generated by the framework, carrying an input data record that arrives at the agent in `input` field . Actions listening to the `InputEvent` will be the entry points of agent.
* `OutputEvent`: The framework will listen to `OutputEvent`, and convert its payload in `output` field into outputs of the agent. By generating `OutputEvent`, actions can emit output data.
Events are JSON-serializable messages passed between actions. Every event has a `type` string used for routing and an `attributes` map that carries the payload. A single event may trigger multiple actions if they are all listening to its type.

User can define own event by extends `Event`.
### Special Events

* `InputEvent`: Generated by the framework, carrying an input data record that arrives at the agent in its `input` attribute. Actions listening to `InputEvent` are the entry points of the agent.
* `OutputEvent`: The framework listens to `OutputEvent` and converts its `output` attribute into outputs of the agent.

### Unified Event

For simple cases, users can pass data between actions directly using `Event` with a custom `type` and `attributes`, without needing to define a subclass. For more structured events, see [Custom Event Subclasses](#custom-event-subclasses) below.

{{< tabs "Unified Event" >}}

{{< tab "Python" >}}
```python
# Send a unified event from one action
@action(InputEvent.EVENT_TYPE)
@staticmethod
def create_my_event(event: Event, ctx: RunnerContext) -> None:
ctx.send_event(
Event(type="my_event", attributes={"field1": "test", "field2": 42})
)

# Consume it in another action
@action("my_event")
@staticmethod
def handle_my_event(event: Event, ctx: RunnerContext) -> None:
field1: str = event.get_attr("field1")
field2: int = event.get_attr("field2")
```
{{< /tab >}}

{{< tab "Java" >}}
```java
// Send a unified event from one action
@Action(listenEventTypes = {InputEvent.EVENT_TYPE})
public static void createMyEvent(Event event, RunnerContext ctx) {
ctx.sendEvent(new Event("my_event", Map.of("field1", "test", "field2", 42)));
}

// Consume it in another action
@Action(listenEventTypes = {"my_event"})
public static void handleMyEvent(Event event, RunnerContext ctx) {
String field1 = (String) event.getAttr("field1");
int field2 = (int) event.getAttr("field2");
}
```
{{< /tab >}}

{{< /tabs >}}

### JSON Serialization

Events are serialized as JSON when passed between Python actions or across the Java-Python boundary. This means attribute values of non-trivial types (such as Pydantic models) lose their type information and arrive as plain `dict` objects. Users must manually reconstruct the typed object:

```python
input_event = InputEvent.from_event(event)
input_data = ItemData.model_validate(input_event.input)
```

### Custom Event Subclasses

Users can also define custom event subclasses for reusable, structured events. Data should be stored in the `attributes` map, and the subclass must implement a `from_event` / `fromEvent` factory method that validates required attributes and reconstructs typed objects from the deserialized data.

{{< tabs "Custom Event" >}}

{{< tab "Python" >}}
```python
class MyEvent(Event):
value: Any
EVENT_TYPE: ClassVar[str] = "my_event"

def __init__(self, value: str) -> None:
super().__init__(type=MyEvent.EVENT_TYPE, attributes={"value": value})

@classmethod
@override
def from_event(cls, event: Event) -> "MyEvent":
assert "value" in event.attributes
return MyEvent(value=event.attributes["value"])

@property
def value(self) -> str:
return self.get_attr("value")
```
{{< /tab >}}

{{< tab "Java" >}}
```java
public class MyEvent extends Event {
private Object value;
public static final String EVENT_TYPE = "my_event";

public MyEvent(String value) {
super(EVENT_TYPE);
setAttr("value", value);
}

public static MyEvent fromEvent(Event event) {
MyEvent result = new MyEvent((String) event.getAttr("value"));
return result;
}

public String getValue() {
return (String) getAttr("value");
}
}
```
{{< /tab >}}

{{< /tabs >}}

Then, user can define actions listen to or send `MyEvent`.

{{< hint info >}}
The payload of python `Event` should be `BaseModel` serializable, of java `Event` should be json serializable.
All attribute values must be JSON-serializable. In Python, this means `BaseModel`-serializable or primitive types. In Java, values must be Jackson-serializable.
{{< /hint >}}

## Built-in Events and Actions
Expand Down
Loading