Repository navigation
Conversation
This was referenced Oct 28, 2026
The sample now generates what Brook generates, byte for byte from its WIT. No SDK has the Stream protos yet, so the sample projects ship their bindings. Today only .NET generates the service, and each fix turns its part on.
Python and TypeScript panic on it, so their outputs keep their previous content. .NET types each case as the raw proto message, not as its record.
Variant planning kept such a case as the raw proto message. Python and TypeScript then panicked on a message conversion that wasn't model-shaped, and .NET typed the case as the proto class. The case now plans as the record, as a record field already does. Brook's stream service now generates for TypeScript, and its .NET read item holds the records.
nexgen maps every temporal/... proto to a temporalio module. The Stream protos aren't in temporalio yet, so the service's imports don't resolve, and basedpyright skips it.
`@nexus.proto` takes `python-import`, the Python module of the bound message or enum, as `typescript-import` names the TypeScript module. A proto that the installed temporalio doesn't have yet needs it. The stream sample imports the Stream protos from its own streamprotos package, so the Python checks now cover it.
A proto-backed request holds a 64-bit integer as a `Long`, but a resource method took a number. A Brook offset doesn't fit in a number. A resource field, method param or result is now a `Long` when the method's request is proto-backed, so it goes into the request as it is. Brook's stream service now type-checks.
The setup of a @nexus.flatten-in-api record always took the indent of a module function. In a method of the client of a service with no endpoint, the code then left the method body, and services.py didn't parse.
Before planning, a proto-backed record is named by its proto type, so the walk for a resource's type parameters stopped there. nexgen then wrote generic models for a resource class that isn't generic, and the code didn't compile.
…output. The request's email field has the name of the user's own email, with another type. Today the user's email fills it.
A resource method filled each request field from the resource field or param of the same name, whatever its type. So the user's own email went into the notify request's email flag. A source now binds only when its type is the field's type.
Python's caller method renders a bare `*` with no keyword parameter after it, so its services.py doesn't parse and the Python output keeps its previous content.
Python refuses a bare `*` with no parameter after it. An unpacked operation with an empty request rendered one, in the caller of a service with no endpoint and in an operation function. The user-service caller now generates its `notify_all` method.
Python's caller method takes the docstring indent of a module function, so its services.py doesn't parse and the Python output keeps its previous content.
The caller of a service with no endpoint took the docstring indent of a module function, so a documented operation broke the module. The user-service caller now generates with the doc of `notify_all`.
Proto3 enums are open. Python fails the whole message on a Stream status that it doesn't know. TypeScript and .NET pass the number through, and their tests pin it.
Proto3 enums are open, but a generated Python enum raised `ValueError` on a number that it didn't know, so the whole message failed. A newer server's Producer kind stopped an old reader's poll loop. The enum now makes a member named `Unknown<number>` with that value, as TypeScript and .NET pass the number through.
Its System Nexus interceptors name the generic models without type arguments. basedpyright refuses the Python one. The TypeScript one names a type parameter that isn't in scope, so `tsc` fails.
An interceptor serves every type argument of an operation's models. Python named a generic model with no type arguments, which basedpyright refuses. TypeScript named the operation's own type parameter, which isn't in scope. Both now erase them, as the service definition does.
The operation renderers take a `PythonTarget` and start each line at its indent. The client target can then render the same operations as methods, with no thread-local flag and no re-indented text. Only services with an endpoint reach these renderers, so their branches for a service with no endpoint are gone. The output doesn't change.
`--targets client` writes `client.py`, with a client extension for each service. Its methods start each operation through `_create_nexus_client` and return the result. One resource class serves both targets, through an operations object that each target supplies.
An operation with `@nexus.poll` gets an `iter_<name>` iterator in each generated target, and so does each resource method that binds it. A support helper turns a request and its response into the next request, or into `None` to stop. `target` limits the iterator to one target, and nexgen refuses the directive where no iterator can exist.
A resource with `@nexus.type-parameter "<alias>"` is generic in Python, TypeScript and .NET. Its operations take a runtime type where the request can't give one, 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.
Each client call and resource method takes `rpc_metadata` and `rpc_timeout`, and Workflow code refuses them. An `async func` operation also gets `start_<name>`, which returns the handle.
A field takes a source or an api-omit for the Workflow or the client target. Target code runs a targeted source, so a model built outside a Workflow never calls a Workflow helper.
`@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.
Each resource gets a factory on the client and in the Workflow package, which builds a handle with no call. A resource with one proto-backed record field passes as that record, so a handle can be an argument or a result.
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, TypeScript and .NET close it at the end of `await using`.
`@nexus.error` on an enum makes a class per case of a failed call, under one base. Python's operations objects raise the class of a failed call's reason from the original error.
The package of an internal service exports nothing. Python's `__init__.py` has no exports, TypeScript has no `index.ts` and marks each declaration `@internal`, and .NET types are `internal`.
moetemp
force-pushed
the
moe/up/error-enums
branch
from
October 29, 2026 04:00
dbd78c4 to
f867378
Compare
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/error-enums
branch
from
October 31, 2026 13:08
f867378 to
12e53e5
Compare
Owner
Author
|
Closing. The Stream writer stays hand-written in each SDK, so nothing uses |
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
@nexus.internal, which marks an interface that only an SDK calls, so its package exports nothing.__init__.pyexports nothing, but keeps the operation registry for serialization contexts.index.ts, and each top-level declaration is@internal, sostripInternalremoves it from the published types.internal. Go output doesn't change, since a caller writes it underinternal/, and Java ignores the directive with a warning.A new
internal-servicesample shows it in the four languages.It's based on #95, and it's on the fork for review before an upstream PR.
Why?
A service of an SDK's runtime isn't for its users, but the generated packages export it next to the user API. The Python module names stay as they are, since the generated imports between packages name them.
How did you test it?
Link to a test plan if any -
The
internal-servicetests check the exports in each language.