Repository navigation
Conversation
3 tasks
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.
What changed?
This PR adds the Streams API to the Python SDK. It's experimental.
A Stream is an ordered log of Records, for example the tokens of a model call. A Workflow, an Activity or a client writes it, and any of them reads it.
The public API is the code that nexgen generates from the Stream WIT, which is in #2128. This PR is the hand-written part.
StreamConfiggives a client its Stores, in the shape ofExternalStorage, and a Worker uses the config of its client. A call goes to the Store in its ref, else to the only Store, else to the one that the selector returns.RedisStreamStoreandMemoryStreamStorerun Core'stemporalio-streamscrate through the bridge. You can also write a Store in Python.readyet. A create, an append and a describe each run as a Local Activity, so a replay sends nothing to the Store. A close runs as a regular Activity, so it reaches the Store only after its Workflow Task completes. For that reason, a Worker with Workflows and a Stream config polls for Activity Tasks.activity.Infofor a Local Activity #2127), so the Store refuses the late writes of a failed Workflow Task.appendalso takes an optionalsequence, so a retried batch is written once.temporalio.contrib.streams.BufferedAppendercollects appends into one write.It depends on three things that have no release yet.
moe/streams-crate-for-sdk-python, which is the Core commit ofmainplus feat: add the Stream protos and WIT to the vendored API sdk-rust#1733 and feat: addtemporalio-streams, the Stream runtime that SDK bridges link sdk-rust#1799.main, sopoe gen-protosgets a second generator fromNEX_GEN_STREAM_BIN.Please note that the top-level
README.mdhas no section on Streams yet.Why?
An agent application shows partial output, such as the tokens of a model call, while the work still runs. Workflow History isn't the place for that data, so a Stream keeps its Records in a Store outside History.
The API is generated and the Stores run in Core, so the other SDKs can get the same API and the same Stores.
How did you test it?
The tests are in
tests/nexus/test_stream_store*.py,tests/contrib/streams/andtests/contrib/redis/. Most of them run on the local dev server. The Redis tests use a real Redis in CI on Linux and macOS, and they skip on Windows.Link to a test plan if any -