GitHub user addu390 created a discussion: [Feature] Cross Language Actions
## Context
After #631 (event refactor) and #670 (Function descriptors), the runtime and
plan layers already do most of the heavy lifting needed for cross-language
actions. #622 covers what's left: pinning down the usage patterns and closing
other gaps in SerDe, E2E Tests, Examples and Documentation.
## What's already there
`JavaFunction` and `PythonFunction` data descriptors in both languages.
`add_action(func=Function)` / `addAction(Function)` accept cross-language
descriptors, and `ActionExecutionOperator` dispatches by `instanceof
JavaFunction` vs `PythonFunction`. The YAML loader can already express
cross-language actions via `type: java|python` + `function: class:method`.
So today, cross-language usage would look like:
```python
class MyAgent(Agent):
def __init__(self):
super().__init__()
self.add_action(
name="handle",
events=["my_event"],
func=JavaFunction(
qualname="com.example.Handlers",
method_name="handle",
parameter_types=["org.apache.flink.agents.api.Event",
"org.apache.flink.agents.api.context.RunnerContext"],
),
)
```
## What's missing
Three gaps:
- **Decorator parity.** `@action` / `@Action` can't declare a foreign target
today, only the imperative `add_action` can.
- **SerDe inconsistencies.** Either to fix the inconsistencies, or clearly call
out the lack of support, backed by comprehensive cross-language tests on
built-in events.
- **E2E test coverage for cross-language actions.** Nothing currently exercises
a user-authored Java action consuming an event from a user-authored Python
action (or vice versa). Built-in event SerDe is only partially exercised today
through the resource path and needs comprehensive coverage to surface issues
across all built-in events.
## Standardizing the usage pattern
The imperative form already exists, the question is should we and what the
`decorator` form looks like. Two options worth noting down:
Option A, descriptor object:
```python
@action(events=["my_event"], target=JavaFunction(
qualname="com.example.Handlers",
method_name="handle",
parameter_types=[...],
))
def handle(): ...
```
Option B, string form (a bit out of convention in the code-base):
```python
@action(events=["my_event"], target="java:com.example.Handlers#handle")
def handle(): ...
```
Java would mirror whichever shape we pick. Option A, although verbose would be
more consistent with existing usage.
## SerDe standardization
Cross-language actions work regardless of event payload format, but events also
need to be cross-language-safe for the feature to be usable end-to-end. Once
events cross the language boundary they go through JSON SerDe, and any built-in
event whose Jackson form doesn't line up with its Pydantic form breaks the
consumer.
Categories of mismatch worth checking are enum casing, field naming convention
(camelCase vs snake_case), and optional/null handling.
The fix is to pick one wire format, `snake_case` fields, lowercase enum values,
and annotate the Java side (`@JsonValue` on a lowercase accessor, or
`@JsonProperty` per constant) to conform. Python is already in that shape.
So, two paths forward, not mutually exclusive:
- Audit the built-in events (`ChatRequestEvent`, `ChatResponseEvent`,
`ToolRequestEvent`, etc.) and align both sides on the same wire format.
- For anything we choose not to support cross-language, document it explicitly
and add tests that pin the boundary.
Either way, the gate is comprehensive cross-language tests on built-in events
so this stops being a discovery problem.
## Signature validation
Same-language signature validation already works (`JavaFunction.checkSignature`
in Java, `PythonFunction.check_signature` in Python). The cross-language sides
are empty stubs right now and worth filling those gaps for the full cross
language support.
- Java's `PythonFunction.checkSignature`, no validation that the Python
function takes `(Event, RunnerContext)`.
- Python's `JavaFunction.check_signature`, no validation that the Java method
signature matches the declared `parameter_types`.
## Tests
For each built-in event, two directions: Java-emit / Python-consume and
Python-emit / Java-consume. This is the matrix that would have caught the kind
of SerDe mismatches discussed above, and is also what signature-validation
regression tests sit on top of.
## Open questions
- Preferred decorator shape, descriptor object or string or any other?
- Anything else worth surfacing before implementation starts?
GitHub link: https://github.com/apache/flink-agents/discussions/697
----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: [email protected]