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]

Reply via email to