Repository navigation
fix(compat-0.3): signal normal stream completion from the REST transport - #1279
Conversation
|
Thanks, this looks good to me. I checked the changes against #1236 and ran the affected reactor locally: all 698 tests passed. Removing the two completion calls also makes exactly the two new tests fail, as you described. A couple of small suggestions:
One minor correction to the description: #1173 added the exactly-once guard to the 0.3 JSON-RPC listener; its completion notification already existed before that. |
42fd35e to
d2735bd
Compare
|
Thanks for the review, and for running the reactor yourself. Both suggestions are in
You were also right about #1173 - the description is corrected: the 0.3 JSON-RPC completion notification predates it, and what #1173 added was the exactly-once guard around it. Still a single commit, now 5 files. Local check with JDK 24: 19/19 across the two REST test classes and 11 modules green, and taking the guard out reproduces the five failures. |
The 0.3 REST transport never reported a normally finished stream back to the caller: both completion callbacks handed to postAsyncSSE were empty lambdas and RestSSEEventListener_v0_3 had no onComplete() at all, so a client could not tell a finished stream apart from one that is still running. Do what the native REST transport already does since a2aproject#1236, and what the 0.3 JSON-RPC transport has always done: turn the connection-end callback into a single terminal errorHandler(null) call. signalTerminal() keeps that to exactly one terminal outcome, so a completion that arrives after a parsing error is dropped instead of producing a second callback. The listener contract is covered directly, including error-then-completion, repeated completion and concurrent terminal signals, all of which fail when the atomic guard is removed. The errorConsumer Javadoc in ClientTransport_v0_3 now spells out the null-on-success convention, matching the native interface. Fixes a2aproject#1194
d2735bd to
d34f698
Compare
|
Heads-up on the earlier run, in case the red build shows up in the timeline. The previous run failed on That is outside this change: what is touched here is the compat-0.3 REST client listener and transport, plus a Javadoc-only edit to I re-ran the identical tree with no content change ( The review suggestions from before are still in this same commit. |
|
Thanks @WMK666666 ! |
Problem
Fixes #1194.
The 0.3 REST transport drops the normal-completion signal. Both
postAsyncSSEcall sites inRestTransport_v0_3(sendMessageStreamingandresubscribe) pass an empty completion callback, andRestSSEEventListener_v0_3has noonComplete()at all. A stream that the server ends cleanly is therefore never reported toerrorConsumer, and the caller cannot tell "stream finished" apart from "stream still running".This is the compat-0.3 copy of a defect that the other three transports no longer have:
Change
This is #1236 applied to the 0.3 REST code, and it also matches what the 0.3 JSON-RPC listener (
SSEEventListener_v0_3) already does:RestSSEEventListener_v0_3: addonComplete(), and route the existing error paths (InvalidProtocolBufferException, the invalid-payloaddefaultbranch) through a newsignalTerminal()that uses anAtomicBooleanto deliver exactly one terminal outcome. The body is the same asSSEEventListener_v0_3; the 0.3 REST listener has noAbstractSSEEventListenerbase class, so that state has to live in the class itself.RestTransport_v0_3: callsseEventListener.onComplete()from the completion callback ofsendMessageStreamingandresubscribe, with the same comment used by the native transport.ClientTransport_v0_3: theerrorConsumerJavadoc onsendMessageStreamingandresubscribenow states that anullargument signals clean completion and a non-nullThrowablesignals failure, matching the wording already used on the native interface.No public API change. The terminal outcome travels through the existing
errorConsumeras anullthrowable, exactly the way the native and JSON-RPC transports already report it. Exposing completion as a first-classClientTransportcapability (#310) is a separate API decision and is not attempted here.This is complementary to #1202, which keeps the in-flight futures so that
close()can cancel them. Both touch the same two call sites, so whichever lands second needs a small rebase.Tests
RestTransport_v0_3_TestgainstestSendMessageStreamingSignalsNormalCompletionandtestResubscribeSignalsNormalCompletion. Each asserts thaterrorConsumerreceives anullthrowable exactly once after the server ends the stream, mirroringtestSendMessageStreamingCompletionon the native side.RestSSEEventListener_v0_3_Testis new (the module had no listener-level test at all) and covers the terminal contract directly:testOnErrorThenOnCompleteDeliversSingleTerminalCallback- error followed by a late completiontestOnCompleteThenOnErrorDeliversSingleTerminalCallback- completion followed by a late errortestRepeatedOnCompleteDeliversSingleTerminalCallback- three completions in a rowtestConcurrentTerminalSignalsDeliverExactlyOneCallback- 32 threads racing completions and errorstestParsingFailureThenOnCompleteDeliversSingleTerminalCallback- a payload that fails to parse stays the single outcometestOnCompleteDeliversNullToErrorHandlerandtestOnCompleteWithoutErrorHandlerDoesNotThrowVerified locally with JDK 24:
mvn -pl :a2a-java-sdk-compat-0.3-client-transport-rest,:a2a-java-sdk-compat-0.3-client-transport-spi -am test- BUILD SUCCESS, 11 modules green, 19/19 across the two REST test classes.onComplete()calls in the transport still makes exactly the two transport tests fail.AtomicBooleanguard insignalTerminalnow makes five listener tests fail (expected: <1> but was: <2>,<3>for the repeated case,<32>for the concurrent one), so the guard is pinned rather than merely exercised.