sangkyoonnam opened a new pull request, #1240:
URL: https://github.com/apache/flink-agents/pull/1240

   Linked issue: #1190
   
   ### Purpose of change
   
   #### User-visible outcome
   
   The built-in Python chat action now gives each chat model attempt the 
durable id `"chat"`. For attempts persisted with this identity, recovery under 
the same action call sequence reuses the saved outcome without another model 
invocation caused by a process-dependent argument digest. This fixes the 
built-in path; canonicalizing `_compute_args_digest` for calls without an 
explicit id remains a follow-up.
   
   #### Runtime flow
   
   `chat()` in `chat_model_action.py` passes `durable_id="chat"` to 
`durable_execute` or `durable_execute_async` on every attempt of its retry 
loop. The runtime wraps the callable with that id, and 
`durable_identity_for_call` returns it without computing the argument digest. 
Attempts stay distinct by their position in the action's durable call sequence.
   
   #### Key decisions
   
   - `"chat"` matches the Java `ChatModelAction`, which keys a plain chat 
model's calls with the same id (`ResolvedModelRoute.durableChatCallId`). An id 
containing the model resource name and retry attempt is unnecessary for this 
deterministic built-in flow: the call index separates attempts, and matching 
Java keeps one identity scheme for plain chat calls across languages.
   - The change is limited to the built-in chat path. User calls without a 
`durable_id` keep the current digest.
   
   ### Behavioral Semantics
   
   #### Interaction decisions
   
   | Mode | Attempt | Durable id |
   |---|---|---|
   | `durable_execute` (sync) | first and every retry | `"chat"` |
   | `durable_execute_async` | first and every retry | `"chat"` |
   
   #### Behavioral contracts
   
   1. For a built-in chat attempt whose outcome was persisted under `"chat"`, 
recovery reuses the saved outcome without invoking the model again, provided 
the action follows the same deterministic call sequence. Process-dependent 
argument digests do not affect this match.
   2. Retried attempts remain separate: recovery consumes their saved outcomes 
in durable call order.
   
   #### Failure behavior
   
   Unchanged. Ordinary provider failures are persisted as invocation outcomes 
and follow the action's retry policy. Invalid or incomplete responses and 
structured-output parsing failures also follow that policy; exhaustion emits a 
failed ChatResponseEvent. Resource lookup failures emit a failed response. 
Cancellation and errors from journal writes or recovery propagate; 
event-dispatch errors also escape the action. In the existing completion-only 
runtime path, outcome serialization failures are logged and leave the attempt 
unrecorded.
   
   ### Tests
   
   | Contract | Coverage |
   |---|---|
   | 1 | Indirect: `test_every_attempt_uses_the_chat_durable_id[False/True]` 
checks explicit-id forwarding. The production explicit-id path bypasses the 
digest; this patch adds no cross-process recovery test. |
   | 2 | The new test checks `"chat"` on both the failed first attempt and its 
retry, and that the retry produces the successful response. It does not restore 
and replay those two saved outcomes. |
   
   The fixture now consumes the reserved `durable_id` keyword before invoking 
the callable, matching the runtime. The new test fails on main (2 failed); the 
plan, runtime and api Python tests pass (1642 passed, 15 skipped), and 
`tools/lint.sh -c` is clean.
   
   Not verified by this patch's regression coverage:
   
   - Cross-process recovery with a real Java context and different 
`PYTHONHASHSEED` values.
   - Replay through the built-in action of successive `"chat"` outcomes, 
including structured-output retries and tool follow-up actions.
   - Recovery across the old digest identity to `"chat"`, including suffix 
invalidation and completed-action reuse.
   
   <details>
   <summary>Implementation invariants and supporting coverage</summary>
   
   Both durable execution modes pass `"chat"` on every attempt.
   `durable_identity_for_call` returns an explicit id before computing the
   function id or argument digest, so messages and `prompt_args` do not
   contribute to these calls' journal identities.
   
   `test_with_durable_id_overrides_derived_function_id` covers callable
   wrapping and derived function-id override; it does not test the
   `durable_identity_for_call` digest bypass.
   
   The existing `test_chat_failure_outcome_replays_without_provider_call`
   covers failure-outcome replay through `_invoke_chat` and a fake Java
   context, without this patch's explicit chat id. Java's
   `testMultipleCallResultRecovery` and
   `testMatchNextOrClearSubsequentCallResultMismatch` cover ordered slots
   and mismatch invalidation, using distinct ids.
   
   </details>
   
   ### API
   
   No public API signature change. Persisted chat attempts inside unfinished 
actions retain their old digest identities. When recovery reaches the first 
such attempt, `"chat"` does not match: the runtime discards that saved outcome 
and every later outcome in the action. Calls subsequently reached execute again 
under the normal retry policy, potentially repeating multiple model attempts.
   
   Actions whose completion was durably persisted remain unaffected: recovery 
skips their bodies and replays saved memory updates and output events. Calls 
without an explicit durable id retain the current digest behavior. This 
intentional identity change is acceptable under the project's 0.x compatibility 
policy.
   
   ### Documentation
   
   - [ ] `doc-needed`
   - [x] `doc-not-needed`
   - [ ] `doc-included`
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   - [x] Yes
   - [ ] No
   
   Generated-by: Claude Code 2.1.296 (Claude Opus 5.5)
   


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