Repository navigation
Conversation
moetemp
force-pushed
the
moe/up/internal-interfaces
branch
from
October 29, 2026 04:00
c88f90e to
bd8e2ef
Compare
moetemp
force-pushed
the
moe/up/stream-sample-k10
branch
2 times, most recently
from
October 31, 2026 13:08
526cf81 to
2670394
Compare
moetemp
changed the base branch from
moe/up/internal-interfaces
to
moe/up/workflow-sync-start
October 31, 2026 13:09
`--targets workflow,client` also writes a `client` package, with its own resource classes and a client extension for each service. Its methods start each operation through `_create_nexus_client` and return the result. The models are shared, and the Workflow package doesn't change. The operation renderers take a target and start each line at its indent, so both targets share them. The stream sample gets the client target.
…emporalio#346) * Scoped the Workflow-only sample defaults to the Workflow target. A default without a target list applies to every target, and these helpers need a Workflow context. * Used `GenerationTarget` in Python and ran each target check once. `PythonTarget` copied `GenerationTarget`, and two checks ran twice per run. A private client method named `_create_nexus_client` replaced the extension hook. * Rendered each resource method request argument on its own line. One long line has no trailing comma, so `ruff format` joined the requests in the samples, and unformatted output got lines of about 200 characters. * Accepted an empty sourced string again in Python proto decode. Without a source default in the shared model, decode refused an empty sourced value. A handler then rejected requests that it accepted before. * Rendered fields-only resources when the Workflow target is off. The root classes had stub methods that always raised, and the client package kept private copies of them. The client now decodes results into the root classes. * Generated definitions for resources and sources with no API target. A handler that implements a service generates definitions only, from the WIT that its callers share. That WIT can have resources and sources, and the default build has no `--targets`, so it couldn't generate such WIT at all. * Gave a returned client handle the Nexus client of its own service. When another interface returned a resource, the handle kept the client of the returning service, so its methods started operations on the wrong endpoint. * Matched the client Nexus protocol to `temporalio.client.NexusClient`. `Client.create_nexus_client` didn't fit the generated hook, so a real client failed to type-check and to run. The SDK needs an operation ID, so each call passes a new random one. * Added a public factory for each resource on the client extensions. To rebuild a client handle from stored identity fields, a caller needed the private `_create_nexus_client` hook and the wire names of the service. * Rejected unknown settings in examples.toml by name. A misspelled key was read as a child configuration and the parent then stopped generating. The error now names the key and its section. * Rejected examples.toml sections that match no input. A misspelled section name gave definitions-only output with no error, or an error that did not point at examples.toml. * Rejected child names that share a directory or cannot name a package. Children whose names map to one directory in any language would merge their files. Names with a leading digit or a Python keyword are not importable packages. * Built the target-sources sample in TypeScript, Go and .NET. The target-sources sample now builds for the workflow target in all three languages. Go filled sourced fields only on proto-backed inputs, so operations without a proto input now fill them too. * Pruned Go imports that the rendered code does not use. Go output with no Workflow API kept the imports of that API and did not compile. The renderer pruned only aliased imports, and now prunes plain ones too. * Updated the changelog for the targets change. The client constructor line describes a new feature, so it moves to added. The breaking entry lists the flag and library changes, and a new fixed entry covers Go output with no Workflow API. * Renamed the leftover native-api wording to Workflow API. The flag is gone, so READMEs, specs and comments now name the Workflow API. * Explained why target-sources borrows Temporal protos. A reader could copy the sample, but its fields do not match the meaning of the borrowed messages. * Updated the core-fix changelog and reworded a native API comment. The sources line still said that resources need a target. The `kw_only` flips had no entry yet. * Generated and tested the Python client of the stream sample. The stream sample is the one System Nexus service with a client, and no sample built or type-checked it. Only Python has the client target, so Python builds both APIs in its own variant, and TypeScript and .NET keep the Workflow API. * Moved the cross-interface resource sample to its own WIT. The `orders` interface in `target-sources.wit` broke the .NET build of that sample, which now runs in every language. Two services there declare two `Operations` classes in one namespace. * Used a resource field before its source in every language. Go, TypeScript and .NET replaced a resource field with the source of the same request field, and Python kept the resource field. Each now sends the resource's value and fills only the other sources.
The poll helper names the support function that builds the next request. Only the client target gets an iterator, so the parser refuses a poll without `target="client"` and a poll on a resource method.
A WIT stream can't cross a Nexus call, so a read is a request and a response. The iterator reads every page. The extension creates one Nexus client for the iteration, and a handle uses its own client. Co-authored-by: Tim Conley <timothy.conley@temporal.io>
The Stream service sets `next_offset` on an empty page too, so the helper reads it as the Stream reader does. Each answer's next offset sets the later reads, also after `start` or `last`.
A caller that changes a yielded page then can't change the next read. `AsyncGenerator` lets a caller close the iterator with `aclose()` or `contextlib.aclosing`. A handle's iterator also gets the method doc and `Args:`.
The poll directive then reads its targets as the other directives do. A refused poll gives the service, the operation and the input path.
Also rewrite poll comments that read two ways or use `name` as a verb.
A generic directive check is planned for syntax and placement. The kept fixtures cover the client-only rule, an operation shape the iterator can't take, and an iterator name clash.
The full stream helper is in the sample, so the guide keeps a short version and links to it.
# Conflicts: # advanced/samples/typescript/tests/stream-service.test.ts # advanced/samples/typescript/wit/stream-service/workflow/models.ts # architecture.md # src/generator/proto/typescript.rs # src/generator/typescript.rs # src/planning/resource_resolution.rs
The stream sample's TypeScript output gets the client API and the read iterator that Python has. A client resource takes a service-bound Nexus client, as in Python.
The stream sample's .NET output gets the client API and the read iterator that Python has. A client resource takes the `NexusClient` of its service, as in Python.
A resource with `@nexus.type-parameter "<alias>"` is generic in Python, TypeScript and .NET. An operation that returns it, or that its method binds, takes a runtime type for each type parameter of its output, so a response decodes its generic values.
`@nexus.definition` names an SDK type, such as a Stream definition, that gives the type arguments of a generic resource. An operation that returns the resource takes it in place of runtime types, unless a resource method binds the operation.
Each client call and client resource method takes `rpc_metadata` and `rpc_timeout`, and passes them to the `start_operation` of its Nexus client.
A Stream request fills the producer from a Workflow helper in Workflow code and from an Activity helper in a client, and the client API leaves the owner unset. A field takes one `@nexus.source` for each target, and `@nexus.api-omit` takes `targets`.
`@nexus.flatten-in-api fields="..." fill="<field>=<sibling>"` on a field makes the call take the named fields of its record, and copies sibling fields into the others. The Stream create takes the backend of its ref this way, and the ref copies the id.
Workflow code builds a handle on an existing Stream with the factory, from an id and a backend.
A resource method can take the items of its last list as arguments, take a one-field record as its field, and return one field of the response. The Stream's publish takes values and returns the last offset.
`@nexus.poll items=` makes the iterator yield the items of each page, and `resume=` names the helper that resumes after one item. The iterator's `next_request` lets a caller save its place.
`@nexus.close` marks the method that ends a resource. Python closes it at a block's end with the block's error, and a call that returns it returns a `ResourceCall` that nexgen emits.
`@nexus.error` on an enum makes a class per case of a failed call, under one base. Python's calls raise the class of a failed call's reason from the original error.
Python runs a coroutine only when something awaits it, so `@workflow.init` and sync handlers can't place a System Nexus operation at the call. A sync `start_<op>` starts it through the support package's `start_now`, as `workflow.start_activity` places an Activity.
The stream sample's WIT is the Stream service WIT of the protos PR, with the sample's own definition, and its protos and bindings are those of the PR. The sample gains the `withdraw_run` helper that the WIT's sources call.
moetemp
force-pushed
the
moe/up/stream-sample-k10
branch
from
November 3, 2026 15:22
2670394 to
f9578dd
Compare
moetemp
force-pushed
the
moe/up/workflow-sync-start
branch
from
November 4, 2026 09:34
10509ef to
c392d48
Compare
Owner
Author
|
The rebase left it empty. temporalio#354 synced the sample, and each PR now restores its own WIT lines. |
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 syncs the stream sample with
temporalio/api#895, the API PR of the Stream service.nexus/stream-service.wit, with three differences. The@nexus.definitionlines name the sample's support types. Five enums keep the@nexus.fallbackof feat: decode an unknown proto enum number as a marked fallback case temporalio/nexgen#321, and the handle of the create keeps the owner source of feat: flatten some fields of one request field #85. The API PR will take the last two.descriptors/streams/are the API PR's files, with rebuilt bindings in each language.withdraw_runhelper that the WIT's sources call, in Python, TypeScript and .NET.StreamHandle, thehandlefield of a request,stream_handleon the create, andStreamSuperseded. The WIT's directives usetargets=.Why?
The SDKs generate their Stream API from the API PR's WIT, so the sample tests what they get.
How did you test it?
Link to a test plan if any -
cargo validatepasses, with a clean tree after the example build. A Python test checks that a retry run withdraws the Records of the run that it retries.