Repository navigation
Conversation
A client takes its Stream Stores in a `StreamConfig`, and each call of the generated Streams API goes to one of them. The Stores run Core's Stream runtime through the bridge.
The Stream WIT now gives the Workflow target `append` and `close`, and keeps `read` out of it.
The Workflow `Stream` has `append` again, and Workflow code can't do I/O. The Local Activity writes as its own Producer with a fixed attempt, so a retry in one Workflow Task writes once.
A Local Activity reaches the Store before its Workflow Task completes, so a failed task would leave a closed Stream. A Worker with Stream Stores now polls for Activity Tasks, so it can run the close.
moetemp
force-pushed
the
moe/streams-api
branch
from
November 14, 2026 02:14
566c3ec to
fc6b8d3
Compare
This was referenced Nov 14, 2026
A create can name its Store with `store=`, so a config with several Stores needs no selector. A call that names no Store on such a config raises `StreamConfigError` at the call.
A failed Workflow Task retries for good, and a Worker with no Stream config never recovers. Workflow code now gets a non-retryable `ApplicationError`, so a user sees the failed Workflow.
A timer of the Workflow event loop goes into History, so in Workflow code the appender starts its write at the next yield instead. The appends of one activation become one Local Activity.
The public Streams API is the generated code. A helper on top of it is a library, so it belongs in contrib.
The other contrib packages have one, and the appender's rules for Workflow code need a place.
This branch has not been deployed
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.
This PR adds the Streams API to the Python SDK. A Stream is an ordered log of Records that a Workflow, an Activity or a client writes, and that any of them reads, for example the tokens of a model call. It is experimental.
The public API is the code that nexgen generates from the Stream WIT, which is in #2128. This PR is the hand-written part. It adds the Stream Stores, the config that gives them to a client, and the hooks that send each generated call to a Store.
Where to look
temporalio/client/_stream_store.py,temporalio/contrib/redis/streams.py,temporalio/testing/_stream_store.pyStreamStore, a public interface with the five operations and two Worker hooks.StreamConfig, with the Stores and the selector.RedisStreamStoreandMemoryStreamStore.temporalio/client/_impl.py,temporalio/client/_client.pytemporalio/worker/_stream_store.py,_workflow_instance.py,_worker.py,_replayer.pytemporalio/nexus/system/_support.py,temporalio/nexus/system/__init__.pytemporalio/contrib/streams/BufferedAppender, which batches appends. It is a library on top of the generated API, so it is incontrib.temporalio/bridge/src/streams.rs,temporalio/bridge/streams.py,temporalio/bridge/Cargo.tomltemporalio-streamsruntime, behind thestreamsandstreams-redisfeatures.temporalio/exceptions.pyStreamErrorwith areason, andStreamConfigError.scripts/gen_nexus_system_api.py,scripts/gen_payload_visitor.pypoe gen-protos.How it behaves
stream_config=StreamConfig(stores=[...], store_selector=...), in the shape ofExternalStorage. A Worker takes the config from its client and serves every Store in it.StreamConfigError. With no config, a client or Activity call raisesStreamConfigError, since this release has no Streams on the server. In Workflow code that case fails the Workflow with a non-retryableApplicationErrorof typeStreamConfigError, so a Worker with a wrong setup is visible.Streamhas noreadyet.appendalso takes an optionalsequence, and a batch that has one is written once. A Workflow append sends one, so a retry of its Local Activity in the same Workflow Task writes once.temporalio.contrib.streams.BufferedAppendercollects the appends of up tomax_delayinto one batch, with one write in flight. In Workflow code it uses no timer, and the appends of one activation go as one write.RedisStreamStorekeeps Streams in Redis, andMemoryStreamStorekeeps them in memory for tests. Both run Core'stemporalio-streamscrate through the bridge. A person can also write a Store in Python.StreamErrorwith areason. A wrong setup of the Stores raisesStreamConfigError.What it depends on
activity.Infofor a Local Activity #2127 adds the Workflow Task info of a Local Activity, chore: add the generated code of the Streams API #2128 adds the generated code on top of it, and this PR sits on chore: add the generated code of the Streams API #2128.moe/streams-crate-for-sdk-pythonof temporalio/sdk-rust. It has the commits of feat: add the Stream protos and WIT to the vendored API sdk-rust#1733 (the Stream protos and WIT) and feat: addtemporalio-streams, the Stream runtime that SDK bridges link sdk-rust#1799 (thetemporalio-streamscrate) on top of the Core commit thatmainuses. So this PR changes nothing else in Core, and a Core update replaces the branch once those two merge.mainof nexgen, which no release has yet. Sopoe gen-protostakes a second generator fromNEX_GEN_STREAM_BIN, and the other packages stay on the released pin.Please note that the README has no section on Streams yet.