zzwqqq opened a new pull request, #1344: URL: https://github.com/apache/flink-agents/pull/1344
<!-- Suggested title: [runtime][java][python] Preserve internal subagent framework faults through tool calls and the Python bridge --> Linked issue: #1322 (second item only) ### Purpose of change Preserve framework faults from internal subagent calls, including their original causes. These faults currently become failed tool responses and can then be masked by an unresolved-handle error. This PR addresses the second item only. Group cleanup, ordinary future/gather behavior, and model routing/retry policies are unchanged. #### Runtime flow Internal call operations and ActionState writes mark framework faults with `UncapturableException`. `FunctionTool`, `ToolCallAction`, subgraph execution, and durable recording propagate that marker through wrapped causes. The Python bridge carries the Java Throwable or Python error details back to Java before result conversion. #### Key decisions `UncapturableException` moves to `plan.exceptions` so plan and runtime share the classification. Python uses an internal counterpart and a per-call return envelope because Pemja otherwise loses the type and cause. ### Behavioral Semantics #### Interaction decisions | Failure / path | Result | | --- | --- | | Ordinary child failure, Java or Python | Failed `SubagentResult` | | Marked tool/subagent fault, direct or nested, serial or parallel dispatch | Propagates with its cause | | Wait failure, even if the child also failed | Propagates the wait failure | | Ordinary / marked tool metadata failure | Existing fallback / propagate the fault | | Marked durable call or completion fault | No terminal failure recorded; earlier completed work remains available | | Java-origin / Python-origin fault crossing the bridge | Original Java Throwable / Python type, message and traceback | #### Behavioral contracts Nested calls retain the framework cause. Recovery can reuse completed child work and retry unfinished work after the storage fault is removed. Ordinary tool failures keep their existing handling, and user return values cannot be mistaken for bridge faults. #### Failure behavior Marked faults fail the Action through the existing runtime path; no retry policy is added. Cancellation, interruption and fatal errors retain their handling. Invalid bridge envelopes raise framework faults. Interpreter close still runs if dispatcher cleanup fails, retaining the first failure and suppressing a later close failure. User catch blocks can still handle exceptions. ### Tests | Behavior | Coverage | | --- | --- | | Framework faults escape tool-response conversion | `FunctionToolTest`, `ToolCallActionSubagentTest`; Python `test_tool_call_action.py` and `test_subagent_await_cleanup.py` | | Nested calls retain the cause; ordinary child failures remain results | `InternalSubagentFrameworkFaultTest`, `FrameworkFaultBridgeTest` | | Wait failures remain distinct from child failures | `SubgraphInvocationTest` | | Framework faults are not recorded as terminal failures; recovery reuses completed work | Java/Python durable tests and the operator snapshot/restore case | | The Python bridge preserves causes, diagnostics and ordinary return values | `FrameworkFaultBridgeTest` | | Invalid envelopes fail; interpreter close survives cleanup errors | `PythonFaultBridgeTest` | Not verified: distributed-cluster fault injection, external model-service E2E, or Python 3.10/3.12. Python-child operator tests require JDK 21; the existing JDK 17 restriction is unchanged. <details> <summary>Validation details</summary> | Local check (Flink 2.3.0) | Result | | --- | --- | | Java UT, JDK 21 | 3,741 tests, 129 skipped, no failures/errors | | Python UT, Python 3.11 | 2,928 passed, 16 skipped, 201 deselected | | Native Pemja/MiniCluster tests, JDK 21, no external model service | 11 passed | | New Java core regressions, JDK 17 | 19 passed | | Spotless, Ruff, Apache RAT, `git diff --check` | Passed | Java: `env -u OPENAI_API_KEY ./tools/ut.sh -j`, with live OpenAI tests skipped. Python: pytest with `not e2e_tests` and `not integration`, matching the `tools/ut.sh -p` selection. Classification uses explicit causes. The bridge envelope belongs to one call and never enters the journal; ordinary Python exceptions retain Pemja's exception path. Attribute lookup and close-time cleanup manage the dispatcher's references. </details> ### API No user-facing API signatures or YAML options change. The internal `UncapturableException` moves from `runtime.operator.parallel` to `plan.exceptions`; code importing it must update imports and recompile. ### Documentation - [ ] `doc-needed` - [ ] `doc-not-needed` - [x] `doc-included` ### Was this patch authored or co-authored using generative AI tooling? - [x] Yes - [ ] No Generated-by: Codex 0.164 (gpt-6) -- 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]
