weiqingy commented on code in PR #977:
URL: https://github.com/apache/flink-agents/pull/977#discussion_r3739205315
##########
docs/content/docs/development/workflow_agent.md:
##########
@@ -264,9 +278,142 @@ public class ReviewAnalysisAgent extends Agent {
{{< /tabs >}}
-In the function, user can also send new events, to trigger other actions, or
output the data.
+### Trigger Condition Syntax
+
+Use `EventType` constants for built-in exact event types. Inside a condition
expression, compare
+`type` with the constant, for example `type == EventType.InputEvent`.
`EventType.InputEvent` by
+itself is a value, not a condition.
+
+A bare identifier or dotted path is treated as an exact event type, so check
an attribute explicitly,
+for example `ready == true` or `attributes.ready == true`. An unquoted value
that starts with
+`EventType.` is treated as a condition expression. To use such a value as an
exact event type, quote
+the whole name, for example `'EventType.custom'`.
+
+Custom event types may be bare names such as `order.created` or
`order-created`. Each dot-separated
+segment must start with an ASCII letter or underscore and may then contain
ASCII letters, digits,
+underscores, or hyphens. Quote names that contain other punctuation or would
otherwise be treated as
+condition expressions, for example `'order:created'` or `'true'`. A quoted
event type must be
+non-empty and cannot contain whitespace, quotes, backslashes, or control
characters.
+
+### Trigger Condition Examples
+
+Multiple values use OR semantics. Entries may use `EventType` constants,
event-class
+`EVENT_TYPE` constants, custom event strings, or Boolean expressions:
+
+```java
+@Action({
+ EventType.InputEvent,
+ ChatResponseEvent.EVENT_TYPE,
+ "MyCustomEvent",
+ "attributes.urgent == true"})
+public static void handleAnyTrigger(Event event, RunnerContext ctx) {}
+```
+
+In the example above, an input event matches even when `urgent` is false
because separate entries
+are OR branches. Put type and attribute checks in one expression when both are
required:
+
+```java
+@Action("type == EventType.InputEvent && input.score > 5 && input.ip.name ==
'Chinese'")
+public static void handleQualifiedInput(Event event, RunnerContext ctx) {}
+```
+
+Dots and hyphens are valid in a bare exact event type. Quote the event type
when its name contains
+other punctuation or would otherwise be interpreted as a condition expression:
+
+```java
+@Action("com.example.order.created")
+public static void handleDottedOrderCreated(Event event, RunnerContext ctx) {}
+
+@Action("order-created")
+public static void handleOrderCreated(Event event, RunnerContext ctx) {}
+
+@Action("'order:created'")
+public static void handleColonOrderCreated(Event event, RunnerContext ctx) {}
+```
+
+Actions can also be registered programmatically:
+
+{{< tabs "Programmatic Action" >}}
+
+{{< tab "Python" >}}
+```python
+agent.add_action(
+ "process_event",
+ [
+ EventType.InputEvent,
+ "type == EventType.ChatResponseEvent && response.content != ''",
+ ],
+ process_event,
+)
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+public class ProgrammaticAgent extends Agent {
+ public ProgrammaticAgent() throws NoSuchMethodException {
+ addAction(
+ new String[] {
+ EventType.InputEvent,
+ "type == EventType.ChatResponseEvent && response.content
!= ''"
+ },
+ ProgrammaticAgent.class.getMethod(
+ "processEvent", Event.class, RunnerContext.class));
+ }
+
+ public static void processEvent(Event event, RunnerContext ctx) {
+ // Handle either matching event.
+ }
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+### Condition Data
+
+Condition expressions are written in CEL and evaluated by the Java runtime for
both Java and Python
+actions. The runtime provides these framework variables:
+
+- `type`: the event type string.
+- `id`: the event ID as a string.
+- `EventType`: the built-in event-type constants.
+- `attributes`: the event's attribute map.
+
+Referenced top-level attributes are also available as bare variables, so
`score > 80` and
+`attributes.score > 80` refer to the same field. All four framework variables
take precedence over
+attributes with the same names. Use the `attributes` namespace, such as
`attributes["type"]` or
+`attributes["id"]`, to access colliding attribute keys.
+
+Nested values are not flattened. For an input event whose attributes are
+`{input: {status: "ok"}}`, use `input.status` or `attributes.input.status`;
bare `status` is not
+available. Other event payloads keep their top-level envelope, for example
`response.content`.
+
+Use a literal index for top-level keys containing dots, such as
`attributes["a.b.c"]`, and test
+static membership with `"a.b.c" in attributes`. Dynamic access at the root,
such as
+`attributes[key]`, and expressions over the whole `attributes` map are not
supported. Dynamic access
Review Comment:
"Not supported" sits in the same register as line 398's "remains absent",
but this one is a hard failure: `ConditionExpressionCompiler.classifyIdent`
throws from the `ActionMatcher` constructor, so the job fails at operator
initialization. The fixture `dynamic_whole_attributes_index` captures the
split, `plan_validation: pass` with `runtime_compilation: fail`.
Would naming the outcome help a reader tell the two apart? Maybe something
like "rejected when the runtime compiles the condition, so the job fails to
start", if that reads right to you.
##########
docs/content/docs/development/yaml.md:
##########
@@ -278,25 +278,78 @@ Inline action (map) fields:
|-------|----------|-------------|
| `name` | yes | Action name (unique within the agent). |
| `function` | yes | Fully-qualified callable in the form
`<module-or-class>:<qualname>`. See [Function
references](#function-references). |
-| `trigger_conditions` | yes | List of event types the action listens to.
Built-in [event aliases](#event-aliases) (`input`, `chat_request`, ...) or your
own event-type strings. |
+| `trigger_conditions` | yes | Non-empty list of strings containing exact
event types or condition expressions that return a Boolean. Each string must
contain at least one non-whitespace character. Built-in [event
aliases](#event-aliases) (`input`, `chat_request`, ...) may be used as complete
entries. All entries use OR semantics. |
| `type` | no | Implementation language: `python` or `java`. Defaults to
`python` (see [Selecting the implementation
language](#selecting-the-implementation-language)). |
| `config` | no | Free-form configuration map passed to the action at runtime.
|
+For built-in exact event types, prefer an alias such as `input`. Custom event
types may be bare names
+such as `order.created` or `order-created`; each dot-separated segment must
start with an ASCII
+letter or underscore and may then contain ASCII letters, digits, underscores,
or hyphens. A bare
+identifier or dotted path is treated as an event type, so Boolean attributes
need an explicit check
+such as `ready == true` or `attributes.ready == true`.
+
+Inside a condition expression, compare `type` with a built-in constant, for
example
+`type == EventType.InputEvent`. Do not use `EventType.InputEvent` alone: it is
a value, not a Boolean
+condition.
+
+Every `trigger_conditions` entry must be a string with at least one
non-whitespace character. Empty
+or whitespace-only strings are rejected. Quote condition expressions in YAML,
including literals
+such as `- "true"` and predicates such as `- "ready == true"`; otherwise YAML
may convert a value
+such as `true`, `false`, or `null` to a non-string value before validation.
+
+Hyphenated event types need no extra quoting layer:
+
+```yaml
+trigger_conditions:
+ - order-created
+```
+
+For event types containing other punctuation, or names such as `true` that
would otherwise be
+interpreted as condition expressions, wrap the event type itself in quotes.
YAML removes its own
+scalar quotes, so the quotes used by trigger-condition classification must be
part of the scalar
+value, for example `- "'order:created'"` or `- "'true'"`. The inner text must
be non-empty and cannot
+contain whitespace, quotes, backslashes, or control characters.
+
+An unquoted value that starts with `EventType.` is treated as a condition
expression. To use such a
+value as an exact event type, preserve the single quotes inside the YAML
string, for example
+`- "'EventType.custom'"`.
+
```yaml
actions:
- name: action1
function: my_pkg.actions:action1
- trigger_conditions: [input]
+ trigger_conditions: [input, custom_event]
type: python
- name: action2
function: my_pkg.actions:action2
- trigger_conditions: [chat_response]
+ trigger_conditions:
+ - "type == EventType.ChatResponseEvent && response.content != ''"
type: python
- action3 # shared action reference (declared at file
level)
```
Action method signatures are fixed (`(Event, RunnerContext)`), so there is no
`parameter_types` field on actions.
+Different matching actions all run (fan-out), while one action runs at most
once for an event.
+Separate entries are OR branches. To combine a type restriction with an
attribute predicate using
+AND, write both checks in one Boolean expression, as shown by `action2` above.
Exact type matches run
+before matches from condition expressions.
+
+Expressions expose the event type as `type`, built-in constants under
`EventType`, and the event
+attribute envelope under `attributes`. Referenced top-level entries from
`Event.attributes` are also
+available directly. Nested values stay under their top-level attribute path
and are never flattened.
+For an input event whose attributes are `{input: {status: "ok"}}`, both
`input.status` and
+`attributes.input.status` are valid, while bare `status` is not. Other event
payloads follow the same
Review Comment:
"is not" reads as rejected, though nothing rejects bare `status`. It clears
plan validation and the type check (`ConditionExpressionCompiler.java:124`
declares non-framework idents `SimpleType.DYN`), then throws unbound at
evaluation. Under the default `WARN_AND_SKIP` that is logged and treated as
false, so the action just never fires.
That default lives only in the `configuration.md` row this PR adds, and
neither page links to it. Would a sentence on the failure semantics plus a link
fit here, the way `monitoring.md:335` points at `event-log.level`? Same two
gaps on `workflow_agent.md`, if so.
##########
docs/content/docs/development/yaml.md:
##########
@@ -498,7 +551,7 @@ When `type:` resolves to the **opposite** language of the
loader, the loader bui
### Provider aliases
-For `clazz:` on resource descriptors and for event names in
`trigger_conditions:`, you can use a short alias instead of a fully-qualified
class path.
+For `clazz:` on resource descriptors and for complete event-type entries in
`trigger_conditions:`, you can use a short alias instead of a fully-qualified
class path. Event alias replacement is an exact complete-entry lookup: `input`
is replaced, while `type == input`, `attributes.kind == 'input'`, and the
quoted event type `'input'` remain unchanged.
Review Comment:
This sentence now frames the alias table below as the menu of legal
`trigger_conditions` values, and that table offers `output`. An action
triggered on `output` never runs: `ActionExecutionOperator.java:247` sends
OutputEvents downstream in the `if` branch, so the `getActionsTriggeredBy` call
in the `else` at `:269` is never reached. Nothing fails and nothing logs.
You document exactly this at `workflow_agent.md:472-474`. Should the caveat
follow the alias table here, or hang off the pointer at 350-351?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]