This is an automated email from the ASF dual-hosted git repository.
wenjin272 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git
The following commit(s) were added to refs/heads/main by this push:
new d30267af [Doc] Update Custom Event Definition Documentation (#651)
d30267af is described below
commit d30267af044c117847c7650ac6f1debf18d818ec
Author: Adesh Nalpet Adimurthy <[email protected]>
AuthorDate: Sun May 10 22:18:44 2026 -0400
[Doc] Update Custom Event Definition Documentation (#651)
---
docs/content/docs/development/workflow_agent.md | 102 +++++++++++++++++++++---
1 file changed, 92 insertions(+), 10 deletions(-)
diff --git a/docs/content/docs/development/workflow_agent.md
b/docs/content/docs/development/workflow_agent.md
index 5b3da32f..f122727f 100644
--- a/docs/content/docs/development/workflow_agent.md
+++ b/docs/content/docs/development/workflow_agent.md
@@ -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