Repository navigation
Conversation
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 26, 2026 00:14
b298e44 to
90966d6
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
4 times, most recently
from
October 26, 2026 04:52
24f6a90 to
79cf55a
Compare
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 26, 2026 08:44
1c5e48f to
84f1587
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 26, 2026 08:44
79cf55a to
15eebd7
Compare
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 27, 2026 09:07
84f1587 to
c3f30a4
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 27, 2026 09:07
15eebd7 to
b3da39a
Compare
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 27, 2026 09:17
c3f30a4 to
b9572b1
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 27, 2026 09:17
b3da39a to
212ef71
Compare
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 28, 2026 01:33
b9572b1 to
925cf34
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 28, 2026 01:33
212ef71 to
4a67b4e
Compare
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 28, 2026 07:54
925cf34 to
cc8e51c
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 28, 2026 07:54
4a67b4e to
0c7ec5b
Compare
An Activity writes as its attempt, so a retry withdraws the Records of the earlier attempts. An Activity's `BufferedPublisher` flushes at the end of the attempt.
moetemp
force-pushed
the
moe/brook/review/s7-workflow-streams
branch
from
October 28, 2026 08:05
cc8e51c to
e7710c0
Compare
moetemp
force-pushed
the
moe/brook/review/s8-activity-handles
branch
from
October 28, 2026 08:05
0c7ec5b to
86d50a1
Compare
In an Activity a handle passes each call to the Activity calls, which write as the attempt's Producer. A write error that no caller got fails the attempt. Only the creator's side closes a Stream, so a close and `async with` raise before any call. `SyncStreamHandle` wraps a handle for a sync Activity, and `BufferedPublisher` takes a handle in client and Activity code.
# Conflicts: # temporalio/common/__init__.py # temporalio/common/_streams/__init__.py # temporalio/common/_streams/_handle.py # tests/contrib/streams/test_redis.py
BufferedPublisher.SyncStream and BufferedPublisher.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was changed
This PR adds the Stream calls in Activities, and
BufferedPublisher,SyncStreamandSyncSubscriptionintemporalio.client.Streamof the Worker's client, such as fromactivity.client().to_stream(handle), writes as the attempt. So a retry withdraws the Records of the earlier attempts. AStreamof another client writes as that client.activity.client(), so it keeps the Worker's client, such as in an Activity class, and wraps aStreaminSyncStream(stream), whose calls block.publishandsubscribeof aStreamin a sync Activity raiseStreamUnsupportedError, and the message namesSyncStream.publishstarts the write at the call. The end of the attempt waits for the attempt's writes, and a write error that no caller got fails the attempt.BufferedPublisher(stream, *, max_delay=...)takes aStreamand batches its publishes, in order, and flushes at the end of an Activity attempt. Workflow code has noStream, and aWorkflowStreampassed with no type checks raisesStreamUnsupportedError.close,async withandSyncStream.closeraiseStreamNotOwnerErrorin an Activity with no call, andasync withraises at__aenter__. Only the creator's side closes a Stream, so a Workflow's Stream closes from Workflow code and a client's Stream from a client. The methods stay, sinceStreamis also the client's class.producerraisesStreamUnsupportedError, since the attempt is the Producer._runmakes the call's future before it schedules the call, so a cancel of the Activity thread after the schedule also cancels the call. Withasyncio.run_coroutine_threadsafe, a cancel can leave a long poll running. A call when the Worker's loop is already closed closes its coroutine, so nothing warns at shutdown.Depends on S7 (#377). No changelog fragment, since S9 has the Streams entry.
Part of the Brook review series.
Why?
Agents stream model tokens from an Activity. The Workflow keeps the Stream, and the Activity writes to it.
Checklist
poe lint,poe gen-docsandpytest tests/contrib/streamson both backends. The sync cancel tests ran in a loop on Python 3.13 and 3.14, alone and in parallel.