yunfengzhou-hub commented on issue #1085:
URL: https://github.com/apache/flink-agents/issues/1085#issuecomment-5726488837

   Hi @wenjin272, +1 for the proposed direction.
   
   ### Proposed changes on top of the issue
   
   One refinement on the Python side: apply the decorator directly to the 
descriptor attribute, instead of a placeholder def with `target=`. Today's 
placeholder form carries exactly the problems the issue lists for the Java stub 
— a body that must never be called, the Action identity split between the 
placeholder name and the target qualname, and a placeholder signature that 
validates nothing about the real target:
   
   ```python
   class MyAgent(Agent):
   
       # The decorated attribute is the cross-language target descriptor.
       handle = action(EventType.InputEvent)(
           JavaFunction.for_action("com.example.Handlers", "handle"))
   ```
   
   The attribute name is the default Action name, with the issue's optional 
`name` override still available. On the form itself: Python's `@` syntax can 
only decorate a `def`/`class`, never an assignment, so keeping the literal 
`@action` spelling would require a placeholder def — the very thing the design 
goals rule out. And `action(...)(descriptor)` is exactly what `@` desugars to 
(`f = decorator(f)`); it is the decorator mechanism applied to the descriptor 
instead of a placeholder body. Java keeps the literal annotation because 
annotations can sit on fields; Python has no `@`-on-assignment, so the two 
forms are as close to parity as the languages allow.
   
   The YAML API does not change. YAML is already descriptor-first — name, 
trigger conditions, and a language-tagged function reference 
(`<module-or-class>:<qualname>`, `type` defaulting to python), with no 
placeholder concept — and both loaders accept both action languages, so all 
four host/action combinations already work today (examples in the next 
section). The plan JSON `exec` block already carries the language-tagged 
descriptor (`func_type: PythonFunction | JavaFunction`), so everything above is 
declaration-side only: no new keys, no wire or plan-schema change, and 
cross-language plan snapshots should stay stable.
   
   ### The resulting declaration experience
   
   With the issue's field form plus the Python refinement, declarations read 
the same way in both languages — the annotated/decorated element is always 
either the native implementation or the executable descriptor.
   
   Java Agent:
   
   ```java
   public class MyAgent extends Agent {
   
       // Native Action: the annotated method is the implementation.
       @Action(EventType.InputEvent)
       public static void processLocally(Event event, RunnerContext ctx) {
           // Java Action implementation
       }
   
       // Cross-language Action: the annotated field is the target descriptor.
       @Action(EventType.InputEvent)
       private static final PythonFunction handle =
               PythonFunction.of("my_pkg.handlers", "handle_input");
   }
   ```
   
   Python Agent:
   
   ```python
   class MyAgent(Agent):
   
       # Native Action: the decorated method is the implementation.
       @action(EventType.InputEvent)
       @staticmethod
       def process_locally(event: Event, ctx: RunnerContext) -> None:
           # Python Action implementation
           ...
   
       # Cross-language Action: the decorated attribute is the target 
descriptor.
       handle = action(EventType.InputEvent)(
           JavaFunction.for_action("com.example.Handlers", "handle"))
   ```
   
   YAML keeps its existing syntax in all four host/action combinations:
   
   ```yaml
   # Java-hosted agent, Java action — e.g. 
examples/yaml_review_analysis_agent.yaml
   actions:
     - name: processInput
       type: java
       function: 
org.apache.flink.agents.examples.agents.ReviewAnalysisAgent:processInput
       trigger_conditions: [input]
   ```
   
   ```yaml
   # Java-hosted agent, Python action (type omitted)
   actions:
     - name: handle_input
       function: my_pkg.handlers:handle_input
       trigger_conditions: [input]
   ```
   
   ```yaml
   # Python-hosted agent, Python action (type omitted)
   actions:
     - name: process_input
       function: 
flink_agents.e2e_tests.e2e_tests_integration.yaml_test_actions:process_input
       trigger_conditions: [input]
   ```
   
   ```yaml
   # Python-hosted agent, Java action — e.g. 
e2e_tests/resources/yaml_cross_language_java_action.yaml
   actions:
     - name: multiply_by_two
       type: java
       function: 
org.apache.flink.agents.resource.test.JavaActionHandler:multiplyByTwo
       trigger_conditions: [input]
   ```
   
   Validation: at plan construction, in both languages, invalid member kinds, 
null descriptors, and duplicate Action names fail with actionable errors. 
Cross-language targets resolve at the earliest boundary that can observe the 
real implementation: `JavaFunction` targets when the plan is materialized on 
the Java side — for Python-driven jobs, client-side job-graph construction, so 
unresolved classes or mismatched signatures fail before submission; 
`PythonFunction` targets when the Python worker imports the module and runs 
`check_signature` at startup, before any record is processed.
   
   ### Migration
   
   0.4 is the designated breaking window, so it might be better to remove 
`@Action(target = @PythonFunction(...))` and the decorator `target=` form 
outright, with migration notes in the release docs, rather than a deprecation 
cycle. @wenjin272 what do you think?
   
   ---
   
   Besides, after we reached an agreement on the design, I'm willing to submit 
a PR for this issue.


-- 
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]

Reply via email to