dex
Control which active Steps are included in Flow search indexing.
Attributes:
- DEFAULT: Use the Dex server's current default policy.
- ALL: Index every active Step.
- WITH_WAIT_FOR: Index only active Steps that define
wait_for. - DISABLED: Do not index active Steps.
Define a typed, durable singleton value owned by a Flow.
Declare Attributes once in a Flow's PersistenceSchema and reuse the same
definition from Steps, RPCs, and Client calls. Values are read and written
through a handler Context; Client methods provide external access.
Set sync_to_attribute_store to project every write asynchronously through
the Flow's configured Attribute Store. Projection is latest-state only, deletion
projects SQL NULL, and failures do not roll back the Flow Attribute.
Set hard_delete_on_null=True for short-lived secrets, such as API keys,
that must be removable. Dex stores every value of a hard-delete Attribute only
in the Dex blob store and never in Flow history. Deleting the Attribute, or
setting it to None, permanently deletes the stored value. A deleted value
reads as absent, so get returns None.
A hard-delete Attribute accepts only string and object values, including
bytes and dataclasses. int, bool, and float value types are
rejected when the Attribute is defined. A custom codec that produces a
numeric or Boolean value is rejected when its Flow is registered, and any
numeric or Boolean value is rejected when it is written. The option cannot
be combined with index or sync_to_attribute_store.
Client.wait_for_attribute_match and AsyncClient.wait_for_attribute_match
reject a hard-delete Attribute before contacting Dex. start_flow and
SubFlow starts reject an initial value whose definition sets
hard_delete_on_null differently from the target Flow's registered
definition of that name, so a secret is never sent inline by mistake.
Hard-delete writes require a Dex Server with an enabled blob store.
This SDK requires Server protocol 4. Workers negotiate before startup and
Clients negotiate lazily before their first service request. Incompatible
Servers fail with RuntimeError. A Server without an enabled blob store
rejects the write.
Step and RPC handlers receive the current value inline. The SDK never stores
the value in the local blob cache.
Hard deletion has these limitations:
- Each Flow keeps one stored copy per Attribute key. Every write overwrites that copy in place.
- A Step or RPC attempt writes or deletes the stored value before its result is committed. A retried attempt, other Steps, and RPC handlers can therefore observe a write or deletion from an attempt that later failed.
- When concurrent Steps or RPCs write the same key, the final stored value may not be the last committed write.
- A failed
start_flowor SubFlow start for a Flow ID that is already running still overwrites the running Flow's value. ANoneinitial value deletes it. - Executions that reuse a Flow ID share the stored value.
Client.time_traveldoes not restore a deleted value. The value stays absent.- A Flow retry or a restarted SubFlow starts from the original initial value. If an earlier run deleted that value, the new run reads it as absent.
- Removing
hard_delete_on_nullfrom a definition leaves the stored value in place until Dex removes the Flow. Delete the value with the hard-delete definition first. - Values remain until deleted, or until the Flow is removed by retention and blob cleanup.
- Blob store backups or bucket versioning can retain copies outside Dex.
Attributes:
- name: The non-empty logical Attribute name without
/, unique within its Flow. - value_type: The Python type used to encode and decode values.
- index: Optional search-index configuration;
Nonedisables indexing. - sync_to_attribute_store: Whether writes are projected to the selected
Attribute Store. Defaults to
False. - hard_delete_on_null: Whether values are stored only in the Dex blob store
and permanently deleted when the Attribute is deleted or set to
None. Defaults toFalse. Requires a string or objectvalue_typeand noindexorsync_to_attribute_store.
Raises:
- ValueError: If
nameis invalid, or ifhard_delete_on_nullis combined withindexorsync_to_attribute_store. - TypeError: If
hard_delete_on_nullis set for anint,bool, orfloatvalue type.
Examples:
>>> status = Attribute("status", str, sync_to_attribute_store=True) >>> status.set(context, "paid") >>> status.get(context) 'paid'A Step uses a short-lived API key and then permanently deletes it:
>>> class CallProviderStep(Step[None]): ... def __init__(self, api_key: Attribute[str]) -> None: ... self.api_key = api_key ... ... def execute(self, context: Context, input: None) -> StepDecision: ... call_provider(self.api_key.get(context)) ... self.api_key.delete(context) ... return graceful_complete("called") >>> class ProviderFlow(Flow[None]): ... def __init__(self) -> None: ... self.api_key = Attribute("api-key", str, hard_delete_on_null=True) ... self.call = CallProviderStep(self.api_key) ... ... def get_steps(self) -> StepList[None]: ... return StepList.start_step(self.call) ... ... def get_persistence_schema(self) -> PersistenceSchema: ... return PersistenceSchema.of(self.api_key) >>> flow = ProviderFlow() >>> with Client(Registry([flow]), cache) as client: ... client.start_flow( ... flow, ... "provider-42", ... None, ... StartFlowOptions().with_attribute(flow.api_key, "sk-live-123"), ... )
Return the current value from a Step or RPC Context.
Arguments:
- context: The current handler Context.
Returns:
The decoded value.
Raises:
- KeyError: If the Attribute has no value.
- TypeError: If the stored value cannot be decoded as
value_type.
Stage a value to persist with the current handler decision.
Arguments:
- context: The current handler Context.
- value: A value compatible with
value_type.
Raises:
- TypeError: If
valuecannot be encoded asvalue_type.
Stage deletion of this Attribute in the current handler decision.
Arguments:
- context: The current handler Context.
Configure the search index for an Attribute or AttributeMap.
Attributes:
- type: The value representation accepted by the search index.
- index_key: The physical search key. An empty value uses the Attribute name for a singleton Attribute; AttributeMap definitions require an explicit key so all instances share one index.
Describe an Attribute lock acquired for a Step or RPC handler.
Attributes:
- attribute: The singleton Attribute or AttributeMap definition to lock.
- instance: The AttributeMap instance, or
Nonefor a singleton Attribute.
Define a typed family of durable values keyed by map instance.
AttributeMap instances share one schema definition while keeping independent
values and locks. Declare the map in PersistenceSchema before using it.
Instance keys must be non-empty and must not contain /.
Synced instances use their physical Attribute names as target columns. Projection
is asynchronous and latest-state only, deletion projects SQL NULL, and failures
do not roll back Flow Attributes.
Set hard_delete_on_null=True for short-lived secrets keyed by instance,
such as one API key per provider. Every instance follows the hard-delete rules
documented on Attribute. Values are stored only in the Dex blob store
and never in Flow history. Deleting an instance, or setting it to None,
permanently deletes its stored value. Only string and object value types,
including bytes, are supported. The option cannot be combined with
index or sync_to_attribute_store, and wait_for_attribute_match
rejects it. Writes require a Server with an enabled blob store and Server
protocol version 3 or later. Handlers receive values inline, and the SDK
never stores them in the local blob cache. Each Flow keeps one stored copy
per instance key. The retry, concurrency, failed-start, Flow ID reuse, time
travel, retention, and backup limitations on Attribute apply to
each instance.
Attributes:
- name: The non-empty logical Attribute name without
/, unique within its Flow. - value_type: The Python type used for every map instance.
- index: Optional shared search-index configuration.
- sync_to_attribute_store: Whether every instance is projected to the selected
Attribute Store. Defaults to
False. - hard_delete_on_null: Whether every instance value is stored only in the
Dex blob store and permanently deleted when the instance is deleted or
set to
None. Defaults toFalse. Requires a string or objectvalue_typeand noindexorsync_to_attribute_store.
Raises:
- ValueError: If
nameis invalid, or ifhard_delete_on_nullis combined withindexorsync_to_attribute_store. - TypeError: If
hard_delete_on_nullis set for anint,bool, orfloatvalue type.
Examples:
>>> balances = AttributeMap("balance", int, sync_to_attribute_store=True) >>> balances.set(context, "merchant-7", 1200) >>> balances.get(context, "merchant-7") 1200RPCs rotate and revoke one provider's API key:
>>> class WaitForRevocationStep(Step[None]): ... def __init__(self, revoked: Channel[None]) -> None: ... self.revoked = revoked ... ... def wait_for(self, context: Context, input: None) -> Wait: ... return Wait.until(self.revoked.for_one()) ... ... def execute(self, context: Context, input: None) -> StepDecision: ... return graceful_complete("revoked") >>> class CredentialFlow(Flow[None]): ... def __init__(self) -> None: ... self.api_keys = AttributeMap( ... "provider-api-key", str, hard_delete_on_null=True ... ) ... self.revoked = Channel("revoked", type(None)) ... self.wait = WaitForRevocationStep(self.revoked) ... ... def get_steps(self) -> StepList[None]: ... return StepList.start_step(self.wait) ... ... def get_persistence_schema(self) -> PersistenceSchema: ... return PersistenceSchema.of(self.api_keys, self.revoked) ... ... @rpc ... def rotate_api_key(self, context: Context, new_key: str) -> None: ... self.api_keys.set(context, "payments", new_key) ... ... @rpc ... def revoke_api_key(self, context: Context) -> None: ... self.api_keys.delete(context, "payments") ... self.revoked.publish(context, None) >>> flow = CredentialFlow() >>> with Client(Registry([flow]), cache) as client: ... client.start_flow(flow, "credentials-7", None) ... client.invoke_rpc(flow.rotate_api_key, "credentials-7", "sk-live-456") ... client.invoke_rpc(flow.revoke_api_key, "credentials-7")
Select one instance for an RPC snapshot.
Pass the result through @rpc(load_attribute_map_instances=(...)).
Arguments:
- instance: The non-empty, slash-free logical map key to load.
Returns:
A load for this one instance.
Raises:
- ValueError: If
instanceis empty or contains/.
Select one typed instance for a Client Attribute read.
Pass the result to Client.get_attributes or
AsyncClient.get_attributes, and use the same selector, or an equal
one, to look up the value in the returned AttributeValues. The
selector keeps the AttributeMap value type, so type checkers infer the
value type of the lookup.
This differs from load(). load selects an instance that an RPC
handler receives in its snapshot. instance selects an instance that
a Client reads from outside the Flow.
Example::
notes = AttributeMap("notes", Note)
values = client.get_attributes("order-42", notes.instance("n1"))
note = values.get(notes.instance("n1"))
Arguments:
- instance: The non-empty, slash-free logical map key to read.
Returns:
A typed selector for this one instance.
Raises:
Return one map instance from a Step or RPC Context.
Arguments:
- context: The current handler Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
Returns:
The decoded instance value.
Raises:
- KeyError: If the instance has no value.
- ValueError: If
instanceis empty or contains/.
Stage one map-instance value for the current handler decision.
Arguments:
- context: The current handler Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
- value: A value compatible with
value_type.
Stage deletion of one map instance.
Arguments:
- context: The current handler Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
Return the number of existing instances, including buffered writes.
Arguments:
- context: The current Step or RPC Context.
Returns:
The number of keys visible after decision-local writes and deletions.
Return decoded existing instance keys in ascending order.
Arguments:
- context: The current Step or RPC Context.
Returns:
An immutable tuple reflecting decision-local writes and deletions.
Select one typed AttributeMap instance for a Client Attribute read.
Create a selector with AttributeMap.instance(). Pass it to
Client.get_attributes or AsyncClient.get_attributes together with
singleton Attributes. Then use it as the key of the returned
AttributeValues. Selectors are immutable and hashable. Two selectors are
equal when their AttributeMap definitions are equal and their instance keys
are the same string.
Example::
notes = AttributeMap("notes", Note)
first_note = notes.instance("n1")
values = client.get_attributes("order-42", first_note)
if first_note in values:
print(values[first_note].text)
Attributes:
- attribute_map: The AttributeMap definition that owns the instance.
- instance: The non-empty logical map key. It must not contain
/.
Raises:
- TypeError: If
attribute_mapis not an AttributeMap, orinstanceis not a string. - ValueError: If
instanceis empty or contains/.
Load one AttributeMap instance for an RPC snapshot.
Create a load with AttributeMap.load(). Loading provides a point-in-time
value snapshot; it does not lock the map against concurrent writers.
Attributes:
- attribute_map: The exact AttributeMap definition registered with the Flow.
- instance: One logical instance key.
Indicate that an RPC did not load the requested AttributeMap entries.
Describe a scalar Attribute predicate.
Create matches with the class methods and pass them to
Client.wait_for_attribute_match() or
AsyncClient.wait_for_attribute_match(). String and bool Attributes
support equality operators. Int and float Attributes support every operator.
A missing Attribute never matches.
Examples:
>>> match = AttributeMatch.greater_than(3)
Create an equality match.
Arguments:
- operand: Value the Attribute must equal.
Returns:
A match requiring equality with
operand.
Create an inequality match.
Arguments:
- operand: Value the Attribute must differ from.
Returns:
A match requiring an existing value different from
operand.
Create a numeric greater-than match.
Arguments:
- operand: Numeric lower bound excluded from the match.
Returns:
A match requiring a value greater than
operand.
Create a numeric greater-than-or-equal match.
Arguments:
- operand: Numeric lower bound included in the match.
Returns:
A match requiring a value greater than or equal to
operand.
Create a numeric less-than match.
Arguments:
- operand: Numeric upper bound excluded from the match.
Returns:
A match requiring a value less than
operand.
Hold the typed values returned by one get_attributes call.
Client.get_attributes and AsyncClient.get_attributes return an
AttributeValues. Look up each value with the Attribute or
AttributeMapInstance that was passed to the call. An equal definition or
selector finds the same value. Type checkers infer the value type from the
key, so values[ORDER_STATUS] has the Attribute's value type.
A read is set when the Flow run stores a value for it. A stored value that
decodes to None is still set. A read is unset when the run stores no
value for it. A key that was not part of the call behaves like an unset
read:
values[key]returns the value of a set read and raisesKeyErrorotherwise.values.get(key)returns the value of a set read andNoneotherwise.values.get(key, default)returns the value of a set read anddefaultotherwise.key in valuesisTrueonly for a set read.
AttributeValues is read-only and not iterable. Keep the reads you passed to
look up their values. Its representation and KeyError messages name each
read by its physical key, such as notes/n1, but never show values, so they
are safe to log for hard-delete Attributes. AttributeValues() creates an
empty result in which every read is unset.
Example::
ORDER_STATUS = Attribute("order-status", str)
NOTES = AttributeMap("notes", Note)
values = client.get_attributes("order-42", ORDER_STATUS, NOTES.instance("n1"))
status = values.get(ORDER_STATUS, "new")
if NOTES.instance("n1") in values:
print(values[NOTES.instance("n1")].text)
Return the value of a read, or a default when it is unset.
Use get when an unset Attribute has an application default. Use
key in values instead when a stored None must be told apart from an
unset read.
Arguments:
- read: The Attribute or AttributeMapInstance passed to
get_attributes, or an equal one. - default: The value returned when the read is unset or was not part of
the call. Defaults to
None.
Returns:
The decoded value of a set read, or
default.
Expose Context operations available to asynchronous Step handlers.
AsyncWorker supplies this protocol to coroutine wait_for and execute
methods. Stream writes enqueue synchronously; heartbeat waits only for the
Worker output queue and never for Dex Stream Store persistence.
Emit one heartbeat from the current asynchronous Step attempt.
Calling this method without value clears prior persisted details.
Passing any value, including Python None, persists a present Value.
Local activity execution ignores heartbeat frames.
Arguments:
- value: Optional codec-supported checkpoint value.
Raises:
- asyncio.CancelledError: If the active Worker invocation is canceled.
- ValueError: If called outside a Step handler.
- ValueMappingError: If
valuecannot be encoded.
Call Dex FlowService asynchronously through registered Flow definitions.
Methods perform non-blocking gRPC I/O and are safe to await from concurrent
tasks. The Client owns its asynchronous channel but not its Registry or BlobCache;
use async with or await close during application shutdown.
Construction performs no network I/O. The first service request negotiates Server protocol 4 with GetServerInfo. Service methods raise RuntimeError only when the Server reports a protocol range this SDK cannot use. The message names the Server version and says whether to upgrade the Server (server-zd newer than v0.1.16) or the SDK. That result is cached, and every later call raises it again. When GetServerInfo itself fails, for example because the Server is unreachable, the call raises the same DexServiceError as any other transport failure. Such failures are not cached, so the next call negotiates again. Negotiation counts against a call's request timeout.
Attributes:
- registry: Flow definitions used to validate typed calls.
- blob_cache: The open cache used to hydrate large values.
- options: The effective ClientOptions.
Examples:
>>> async with AsyncClient(registry, cache) as client: ... run_id = await client.start_flow(orders, "order-42", input) ... result = (await client.wait_for_flow("order-42")).single_output(OrderResult)
Construct an AsyncClient and its lazy plaintext gRPC channel.
Arguments:
- registry: The Flow Registry used for validation and codecs.
- blob_cache: An open cache used for asynchronous hydration.
- options: Service address and bearer credential;
Noneuses defaults and sends no authorization metadata.
Start a Flow and return its server-assigned run ID.
The await completes after Dex accepts the Flow, not after completion. flow
must be the exact registered instance and input must match its starting
Step annotation.
The first request negotiates Server protocol 4 and caches compatibility. Construction performs no network I/O. An incompatible Server fails this and every later request; transient negotiation failures can be retried.
Arguments:
- flow: The registered Flow instance to start.
- flow_id: A non-empty application ID stable across runs.
- input: The starting Step input, or
Nonewithout a starting Step. - options: Timeout, reuse, retry, idempotency, and initial-state options.
Returns:
The server-assigned run ID.
Raises:
- FlowAlreadyStartedError: If reuse policy rejects an existing Flow ID.
- FlowDefinitionError: If
flowis not registered. - ValueMappingError: If an input or initial Attribute cannot be encoded, including a numeric or Boolean value for a hard-delete Attribute.
- ValueError: If an initial Attribute uses a different
hard_delete_on_nullsetting than the Flow's registered definition of that name. - RuntimeError: If the Server reports a protocol range without protocol 4.
- DexServiceError: If FlowService rejects or cannot perform the request.
Invoke a registered RPC and await its typed result.
Pass the bound method from the registered Flow instance. RPC timeout and
Attribute locks come from @rpc configuration. A non-transactional RPC
without Attribute locks starts from a backend query. If the handler returns no
durable effects, a retained terminal execution can serve that query. Locks,
transactional execution, returned effects, or server policy can require an
active execution.
Arguments:
- rpc_method: A bound method decorated with
@rpc. - flow_id: The non-empty target Flow ID.
- input: The annotated input, or
Nonefor an input-free RPC. - run_id: Optional exact run. With
"", the server resolves the current execution. - options: Additive runtime AttributeMap locks and exact map-instance loads.
Returns:
The decoded
RPCResult.output, orNonefor a no-output RPC.
Raises:
- FlowDefinitionError: If the method is not registered.
- RpcLockConflictError: If Attribute locks cannot be acquired.
- WorkerInvocationError: If the application handler fails.
- ValueMappingError: If input or output mapping fails.
- FlowNotActiveOrNotFoundError: If the Flow is missing or the selected path cannot use its closed execution.
- DexServiceError: If the service call otherwise fails.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await one typed best-effort Stream append with source metadata.
Arguments:
- flow_id: Logical Flow instance ID; the Flow need not exist or be active.
- stream: Exact Stream object registered in one Flow schema.
- source: Non-empty informational source. Repeated values still append.
- value: Typed message to append.
Raises:
- ValueError: If the Flow ID or source is empty.
- FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If the message cannot be encoded.
- DexServiceError: If FlowService cannot append the message.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await the next retained Stream message after a resume token.
Arguments:
- flow_id: Logical Flow instance ID used as the Stream instance key.
- stream: Exact Stream object registered in one Flow schema.
- resume_token: Previous message token, or empty for the retained head.
- timeout: Optional non-negative server-side long-poll duration.
Returns:
The decoded message, next resume token, creation time, and source.
Raises:
- LongPollTimeoutError: If no message arrives before the server wait expires.
- FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If the retained message cannot be decoded.
- DexServiceError: If FlowService cannot perform the read.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await one newest-first page of retained Stream messages without waiting for writes.
An empty token starts at the retained tail. Pass the returned page token unchanged to read older messages. Trimming may remove messages between pages.
Arguments:
- flow_id: Logical Flow instance ID used as the Stream instance key.
- stream: Exact Stream object registered in one Flow schema.
- page_size: Positive maximum message count within the server-configured limit.
- before_page_token: Opaque token from the preceding page, or
""first.
Returns:
Decoded messages in newest-first order and the next-page token.
Raises:
- ValueError: If
page_sizeis not positive. - FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If a retained message cannot be decoded.
- DexServiceError: If FlowService cannot list the Stream.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await Flow closure and return its terminal result.
timeout is a server-side long-poll duration, not a local task deadline.
A LongPollTimeoutError is retryable by awaiting this method again. Parallel
completion order is not deterministic; select results by Step identity.
Arguments:
- flow_id: The non-empty target Flow ID.
- timeout: Optional non-negative server-side wait duration.
Returns:
The terminal status, failure detail, and output-bearing completions.
Raises:
- LongPollTimeoutError: If the Flow remains open when the wait expires.
- FlowNotFoundError: If
flow_iddoes not exist. - ValueMappingError: If Dex returns a malformed or incompatible hydrated value.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Request cancellation, termination, or failure of an active Flow.
The await completes after Dex accepts the request, not after terminal status.
Arguments:
- flow_id: The non-empty target Flow ID.
- options: Stop mode and optional recorded reason.
Raises:
- FlowNotActiveOrNotFoundError: If no active run exists.
- DexServiceError: If FlowService rejects or cannot perform the request.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Return summary metadata for the current or latest Flow run.
Arguments:
- flow_id: The non-empty Flow ID to describe.
Returns:
Flow ID, run ID, Flow type, status, and UTC start time.
Raises:
- FlowNotFoundError: If
flow_iddoes not exist. - DexServiceError: If FlowService cannot return the summary.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Read committed Attribute values from the current or latest Flow run.
Use get_attributes to read Flow state from outside the Flow without
defining an RPC. Pass one or more reads. A read is a singleton
Attribute or one AttributeMap instance selected with
AttributeMap.instance(key). To read one Attribute, pass a single read.
The Client sends every key in one FlowService request, so all values come
from the same committed state. No Worker runs.
The call targets the current run, or the latest run when no run is open. A closed run stays readable until Dex purges it. The call sees only committed state. Unlike reading inside an RPC handler, it never sees writes that a running Step or RPC has staged but not yet committed.
Each read must use a definition that some Flow in this Client's Registry
declares in its PersistenceSchema. The definition matches when it is
equal to the registered one: same class (Attribute or
AttributeMap), name, value_type, index,
sync_to_attribute_store, and hard_delete_on_null. The definition
may come from any registered Flow. The server does not check that the
target Flow's type declares it, so a same-named Attribute of another type
in the target Flow fails to decode.
Attributes defined with hard_delete_on_null=True are readable; the
server resolves their stored values. The Client resolves other large
values through LoadBlobs and the Client's BlobCache, and then decodes every
value. On any error the call returns nothing, so no partial result is
visible. Every error message names the Flow ID. An error about one read or
value also names its Attribute name or physical key.
Example::
ORDER_STATUS = Attribute("order-status", str)
NOTES = AttributeMap("notes", Note)
try:
values = await client.get_attributes(
"order-42", ORDER_STATUS, NOTES.instance("n1")
)
except FlowNotFoundError:
return None
status = values.get(ORDER_STATUS, "new")
if NOTES.instance("n1") in values:
note = values[NOTES.instance("n1")]
Arguments:
- flow_id: The non-empty Flow ID.
- *reads: At least one
AttributeorAttributeMapInstance. Two reads of the same Attribute or AttributeMap instance are rejected.
Returns:
The set values, keyed by the reads passed to this call. An Attribute or AttributeMap instance without a stored value is absent from the result.
Raises:
- ValueError: If
flow_idis empty, no read is given, two reads select the same Attribute or AttributeMap instance, or the response has an unexpected key, a duplicate key, or an entry without a value. - TypeError: If a read is not an
AttributeorAttributeMapInstance, such as anAttributeMapwithout an instance. - FlowDefinitionError: If no Flow in the Registry declares a matching definition, including a same-named definition with another value type.
- FlowNotFoundError: If the Flow does not exist or its latest run is being purged.
- ValueMappingError: If a returned value cannot be resolved or decoded as its value type. This includes an empty or non-finite value, an unsupported object encoding, and a large value that LoadBlobs omits.
- DexServiceError: If FlowService or LoadBlobs otherwise fails. A LoadBlobs
failure for a missing Flow raises
FlowNotFoundError. - RuntimeError: If the Server reports a protocol range without protocol 4.
Return one page of Flow runs matching a visibility query.
Arguments:
- query: A Dex visibility query; an empty string uses server defaults.
- page_size: The non-negative maximum result count requested.
- next_page_token: Opaque token from the preceding page, or
""first. - include_continued_as_new: Include earlier runs without overriding query filters.
Returns:
Server-ordered entries with hydrated indexed Attributes and a next token.
Raises:
- ValueError: If
page_sizeis negative. - ValueMappingError: If an indexed Attribute cannot be hydrated.
- DexServiceError: If FlowService cannot execute the query.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Create a new run from a selected point in existing Flow history.
Arguments:
- flow_id: The non-empty Flow ID whose history supplies the new run.
- options: Time travel selector, reason, and replay controls.
Returns:
The new server-assigned run ID; the Flow ID is unchanged.
Raises:
- FlowNotFoundError: If
flow_iddoes not exist. - ValueError: If time travel selector fields are invalid.
- DexServiceError: If FlowService cannot create the new run.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Make one waiting Timer condition immediately ready.
Arguments:
- flow_id: The non-empty active Flow ID.
- step_execution_id: The Step type and positive execution number.
- timer_id: Exactly one Timer condition ID or zero-based index.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- ValueError: If an identifier is invalid.
- DexServiceError: If FlowService cannot skip the Timer.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await one Step execution completion or its request timeout expiry.
The server derives a stable Request ID from the Step execution when none is supplied. Request timeout covers transparent transport reattachments. Internal handler timeout controls Update generation rollover without ending this request.
Arguments:
- flow_id: The non-empty active Flow ID.
- step_execution_id: The Step type and positive execution number.
- options: The optional Request ID, request timeout, and internal handler timeout controls.
Raises:
- ValueError: If the Request ID or request timeout is invalid.
- RequestTimeoutError: If a positive request timeout expires first.
- FlowNotActiveOrNotFoundError: If the Flow is missing or closes first.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await a singleton Attribute or AttributeMap instance satisfying a match.
Singleton form is wait_for_attribute_match(flow_id, attribute, match,
options); map form adds instance before match.
Arguments:
- flow_id: The non-empty active Flow ID.
- attribute: The registered Attribute or AttributeMap to observe.
- *args: Positional match and options, optionally preceded by a map instance.
- **kwargs: The same arguments supplied by name.
Returns:
The current value that satisfied the match.
Raises:
- TypeError: If arguments do not match the Attribute definition.
- ValueError: If an identifier, option, or expected value is invalid.
Also raised before transport when the Attribute uses
hard_delete_on_null. - RequestTimeoutError: If a positive request timeout expires first.
- FlowNotActiveOrNotFoundError: If the Flow is missing or closes first.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Update specified mutable configuration fields for an active Flow.
The update affects later decisions and does not recall work already
dispatched. Unspecified fields and the existing Worker target remain
unchanged. An empty FlowConfig() leaves all configuration unchanged.
Arguments:
- flow_id: The non-empty active Flow ID.
- config: New optional fields applied to later decisions.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- ValueError: If a configuration value is invalid.
- DexServiceError: If FlowService cannot apply the update.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Ask an active Flow to roll history into a new run.
The Flow ID remains stable and the run ID changes when rollover occurs.
Arguments:
- flow_id: The non-empty active Flow ID.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- DexServiceError: If FlowService cannot accept the request.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Await the FlowService health endpoint.
Returns:
Truewhen the service returns successfully.
Raises:
- DexServiceError: If the service is unhealthy or unreachable.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Close the owned asynchronous gRPC channel.
close is idempotent. Other methods must not be awaited afterward. The
Registry and BlobCache remain owned by the caller.
Batch text chunks for an asynchronous Step invocation.
Create this writer with Stream.buffered_text(). write is synchronous and returns
after local buffering or Worker-output enqueueing. AsyncWorker automatically stops its timer
and flushes remaining text before the invocation result or error.
Initialize an invocation-managed writer from validated factory inputs.
Arguments:
- stream: Registered string Stream receiving each batch.
- context: Current asynchronous Step Context.
- flush_interval: Positive one-shot timer interval.
- max_buffered_bytes: Positive soft UTF-8 batch threshold.
Append one text chunk and flush after reaching the soft size threshold.
Empty chunks are ignored. The first non-empty chunk starts the configured one-shot timer. This method does not wait for the timer or Stream Store acknowledgement.
Arguments:
- chunk: Text to append without modification.
Raises:
- TypeError: If
chunkis not a string. - BaseException: The previously latched timer or Worker-output failure.
- ValueError: If the invocation already finished.
Host asynchronous Step and RPC handlers through outbound pull streams.
A Worker is one-shot. Await start in a background task because it waits for
termination, and await stop or close during shutdown. Workers need
only an outbound connection to the Dex Server. Application handlers
may overlap on the event loop and must avoid blocking operations.
Attributes:
- registry: The Flow Registry served by this Worker.
- blob_cache: The shared cache used to hydrate large values.
- options: The effective WorkerOptions.
Examples:
>>> async with AsyncWorker(registry, cache) as worker: ... task = asyncio.create_task(worker.start()) ... await worker.stop() ... await task
Construct an AsyncWorker without starting its outbound pull streams.
Arguments:
- registry: A Registry created with
allow_async_handlers=True. - blob_cache: An open cache shared for asynchronous value hydration.
- options: Networking, startup, and bearer credential options;
Noneuses defaults and sends no authorization metadata.
Raises:
- ValueError: If the pull Worker ID is invalid.
Negotiate protocol, synchronize Attribute indexes, and serve.
start may be awaited exactly once. It contacts FlowService before
opening outbound task streams and completes only after stop
terminates those streams.
Permanent pull stream rejections raise PullRegistrationError when
stop_on_permanent_stream_rejection is enabled; otherwise streams retry.
Raises:
- PullRegistrationError: A permanent stream rejection stops this Worker.
- RuntimeError: If lifecycle state is invalid, Server protocol 4 is unsupported, concurrency exceeds matching.maxConcurrentTasksCap, or index synchronization fails.
Wait until the required execution slot kinds have registered.
Start the Worker separately before waiting, or start it concurrently.
Readiness requires one STEP_AND_RPC stream and, when RPC reservation is
configured, one RPC_ONLY stream. Successful registration latches after stop. Use
pull_registration_state for subsequent readiness probes. A permanent
rejection before readiness raises PullRegistrationError only when no
other stream is registered for that slot kind. Startup failures wake waiters.
When the first registration reports a Server per-Worker limit below
max_concurrent_tasks, every waiter raises that limit error and the
Worker never becomes ready.
Multiple callers may wait independently, including before start.
Arguments:
- timeout: Maximum seconds to wait; None waits indefinitely.
Raises:
- PullRegistrationError: A slot kind is permanently rejected before readiness.
- TimeoutError: The timeout expires.
- asyncio.CancelledError: The calling task is canceled; the Worker keeps running.
- RuntimeError: The Worker stops before readiness, or
max_concurrent_tasksexceeds the Server'smatching.maxConcurrentTasksCap. - BaseException: The original startup failure, if start fails.
Examples:
>>> start_task = asyncio.create_task(worker.start())>>> await worker.wait_pull_registered(timeout=30) >>> is_ready = worker.pull_registration_state().is_ready
Return the deprecated reserved RPC decline counter, always zero.
Reserved RPC capacity uses RPC_ONLY slots. Steps are never declined for capacity. This property remains available for source compatibility and is safe to read concurrently, including after stop.
Returns:
Zero.
Examples:
>>> declines = worker.pull_reserved_rpc_declines
Return an immutable snapshot of execution slot kind registrations.
Transient failures, reconnects, and stop preserve initial readiness. A permanent rejection makes readiness false only while that slot kind has no registered stream. Re-registration clears its error. Calls are safe concurrently with start, stop, and other waiters. See wait_pull_registered for an example.
Returns:
Current state grouped by STEP_AND_RPC and, when configured, RPC_ONLY.
Drain handlers and release asynchronous channel resources.
Calls before start and repeated calls after shutdown are safe. Pull
mode drains for PullWorkerOptions.stop_drain_timeout (30 seconds by
default), then cancels active tasks and waits for handlers to return.
Idle streams half-close immediately. Busy streams send final output before
half-closing. Tasks received after stop begins are dropped without running
handlers. A timed-out busy stream is reset before cancellation.
Half-closed streams keep reading server frames until EOF or the remaining drain
deadline before the channel closes. When the deadline expires, every stream
that is still open is reset, even one blocked on a send or without Server EOF,
so stop returns once handlers return. The Server may count a task it sent in
that window as an attempt that may have run.
One exception applies to a stream whose final output was queued or written
but not yet confirmed by Server EOF or by the next assignment. Such a stream
is not reset at the drain deadline, even if its handler finished or it
already half-closed. It gets up to 5 more seconds to send the output,
half-close, and receive Server EOF, and is reset after that. Stop can
therefore return up to 5 seconds after the drain deadline.
Stop the AsyncWorker and permanently mark it closed.
Uses the same shutdown contract as stop: idle streams half-close, busy streams send final output before half-closing, and later tasks are dropped. A handler exceeding the drain timeout has its stream reset. Pull streams finish their bounded EOF wait before the channel closes; a stream still open at the deadline is reset. Repeated calls are safe; a closed Worker cannot be started again.
Exchange an API key for short-lived bearer tokens before Dex calls.
Use this when an application holds a long-lived API key and a token endpoint issues access tokens for a Dex server ID. Use FileCredential when another process writes and rotates a bearer token file instead. The first token() call exchanges synchronously. Later calls start one background refresh after 80% of the token lifetime and keep serving the valid token. Concurrent callers share one exchange. Failed refreshes retry no more often than every five seconds while the old token remains valid. An expired token makes callers wait up to exchange_timeout for the in-flight exchange. Dex unary calls retry once with a fresh token after UNAUTHENTICATED. Pull Worker streams reconnect once immediately with a fresh token after UNAUTHENTICATED; a second rejection uses the normal backoff.
The token URL must use HTTPS. HTTP is allowed only on localhost, 127.0.0.0/8, or ::1. Loopback HTTP bypasses environment proxies; HTTPS honors them. Redirects are refused. The instance is thread-safe. close() is optional and can be called after all Clients and Workers sharing the credential are closed. It waits only through the current exchange deadline. Formatting this credential or an exchange error does not reveal secrets.
Example:
>>> credential = ApiKeyCredential( ... "https://auth.example.com/token", "dex-server", "application-api-key" ... ) >>> client = Client(registry, cache, ClientOptions(credential=credential)) >>> try: ... client.health_check() ... finally: ... client.close() ... credential.close()
Arguments:
- token_url: Absolute token endpoint URL.
- server_id: Server ID used to scope the issued access token.
- api_key: API key exchanged for access tokens.
- exchange_timeout: Maximum seconds for each exchange, including connect and response; defaults to 10 seconds.
Raises:
- ValueError: An input is empty, the URL is invalid or insecure, or the timeout is not positive.
Validate exchange settings without contacting the token endpoint.
Arguments:
- token_url: Absolute HTTPS URL, or loopback HTTP URL.
- server_id: Server ID to send in the exchange request.
- api_key: API key to exchange for an access token.
- exchange_timeout: Positive maximum exchange time in seconds.
Raises:
- ValueError: A required string is empty, the URL is invalid or insecure, or the timeout is not positive and finite.
Return an access token, exchanging or refreshing it when necessary.
A valid token returns immediately, including during background refresh. First and expired-token calls wait up to exchange_timeout. Concurrent calls share the same exchange. Exchange failures raise ApiKeyExchangeError. Calls after close() raise RuntimeError.
Returns:
Access token without the Bearer prefix.
Raises:
- ApiKeyExchangeError: The endpoint, response, or transport failed.
- RuntimeError: The credential is closed.
Stop using this credential and wait up to its current exchange deadline.
Closing is optional. Close a shared credential after all Clients and Workers using it have closed. Subsequent token() calls raise RuntimeError. A network operation may continue in a daemon thread after the deadline when the operating system cannot interrupt it.
Report a failed API key exchange without exposing credential secrets.
The message identifies the token URL and HTTP status when available. It includes the endpoint's error and error_description fields, each limited to 200 characters. It never includes the response body, API key, issued access tokens, or JWT-shaped strings. Client calls raise this error unchanged. Pull Workers log it and retry.
Provide local, bounded storage for content-addressed Dex blobs.
Implementations may be called concurrently by Clients and Workers. Blob IDs are opaque server identifiers; callers should not derive their own IDs.
Return the configuration used to open this cache.
Returns:
The effective cache configuration.
Read a cached payload without contacting Dex.
Arguments:
- blob_id: The non-empty, opaque blob identifier.
Returns:
The payload bytes, or
Nonewhen the entry is absent.
Offer a payload to the bounded cache.
Arguments:
- blob_id: The non-empty, opaque blob identifier.
- payload: The bytes to retain; the cache does not take mutable ownership.
Returns:
Truewhen admitted, orFalsewhen the policy rejects the entry.
Configure the local persistent cache for large Dex values.
Attributes:
- directory: The writable directory that stores cache data and metadata.
- max_bytes: The positive maximum on-disk payload size in bytes.
- frequency_counters: Admission-policy counter count. Zero selects the default of 10,000; larger values improve frequency estimates at a memory cost.
Batch text chunks for a synchronous Step generator.
Create this writer with Stream.buffered_text(). Yield every output returned by write
and flush. The interval is cooperative: elapsed time is checked when another chunk arrives.
Initialize a cooperative writer from validated factory inputs.
Arguments:
- stream: Registered string Stream receiving each batch.
- context: Current synchronous Step Context.
- flush_interval: Positive cooperative flush interval.
- max_buffered_bytes: Positive soft UTF-8 batch threshold.
Append one chunk and return an output when a threshold is reached.
Arguments:
- chunk: Text to append without modification.
Returns:
An empty tuple while buffering, or one StepOutput that the handler must yield.
Raises:
- TypeError: If
chunkis not a string. - ValueMappingError: If the combined text cannot be encoded.
Define a typed, durable singleton message stream owned by a Flow.
Add the Channel to the Flow's PersistenceSchema. Publishers append values;
Steps use the condition factories to wait for a count and then inspect the
selected values through results.
Attributes:
- name: The non-empty Channel name without
/, unique within its Flow. - value_type: The Python type of every published value.
Examples:
>>> approvals = Channel("approvals", str) >>> wait = Wait.until(approvals.for_n(2, condition_id="two-approvals")) >>> approvals.publish(context, "reviewer@example.com")
Stage one value to append with the current handler decision.
Arguments:
- context: The current Step or RPC Context.
- value: A value compatible with
value_type.
Stage deletion of one pending message from an RPC handler.
Use @rpc(is_transactional=True) when a missing message must fail the
entire RPC without committing its other writes. Step Contexts reject this
operation.
Arguments:
- context: The current RPC Context.
- message_id: Non-empty ID returned by a Client pending-message read.
Return the current number of queued values.
Arguments:
- context: The current Step or RPC Context.
Returns:
The non-negative number of queued Channel values.
Return the pending messages loaded for the current RPC snapshot.
Configure the RPC with load_channels=(this_channel,).
The returned sequence preserves FIFO order and does not change after staged
publications or deletions.
Arguments:
- context: The current RPC Context.
Returns:
Pending message IDs and decoded values in FIFO order.
Raises:
- ChannelMessagesNotLoadedError: If the RPC did not load this Channel's messages.
Find one loaded pending message by server-assigned ID.
Arguments:
- context: The current RPC Context.
- message_id: The message ID to find.
Returns:
The matching message, or
Nonewhen absent from the snapshot.
Return values selected by the Step's satisfied Channel condition.
Arguments:
- context: The current Step Context.
Returns:
An ordered, read-only sequence for this Step execution; it is empty when the Step did not wait on this Channel.
Create a condition that consumes exactly one value.
Arguments:
- condition_id: Optional stable identifier used by Timer and wait APIs.
Returns:
A Channel condition equivalent to
for_range(at_least=1, at_most=1).
Create a condition that consumes exactly count values.
Arguments:
- count: The required non-negative value count.
- condition_id: Optional stable condition identifier.
Returns:
A Channel condition with equal lower and upper bounds.
Create a condition requiring at least count values.
Arguments:
- count: The inclusive, non-negative lower bound.
- condition_id: Optional stable condition identifier.
Returns:
A Channel condition with no upper bound.
Create a non-blocking condition consuming up to count queued values.
The condition does not wait on its own for values to accumulate. When
its surrounding Wait completes, Dex consumes up to count values
queued at that time. An empty queue produces no values.
Arguments:
- count: The inclusive, non-negative upper bound.
- condition_id: Optional stable condition identifier.
Returns:
A Channel condition with no positive lower bound.
Create a bounded condition for queued Channel values.
At least one bound is required. When both are present, at_least
cannot exceed at_most. Dex waits only for at_least. Once that
bound is met, it consumes currently queued values up to at_most.
Omitting at_least makes the condition complete immediately.
Arguments:
- at_least: Optional inclusive lower bound.
- at_most: Optional inclusive upper bound.
- condition_id: Optional stable identifier unique within the Wait tree.
Returns:
A condition accepted by
Waitfactories.
Define keyed Channel instances that share one typed schema.
Each instance has an independent queue and may be waited on separately.
Add the ChannelMap definition to the Flow's PersistenceSchema.
Instance keys must be non-empty and must not contain /.
Attributes:
- name: The non-empty Channel name without
/, unique within its Flow. - value_type: The Python type of every published value.
Examples:
>>> events = ChannelMap("events", dict[str, str]) >>> events.publish(context, "customer-42", {"kind": "paid"}) >>> wait = Wait.until(events.for_one("customer-42"))
Select one instance's pending messages for an RPC snapshot.
Arguments:
- instance: The non-empty, slash-free logical ChannelMap instance key.
Returns:
A load for this one instance.
Raises:
- ValueError: If
instanceis empty or contains/.
Stage a value to append to one ChannelMap instance.
Arguments:
- context: The current Step or RPC Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
- value: A value compatible with
value_type.
Stage deletion of one pending message from a ChannelMap instance.
Arguments:
- context: The current RPC Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
- message_id: Non-empty ID returned by a Client pending-message read.
Return the queued value count for one instance.
Arguments:
- context: The current Step or RPC Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
Returns:
The non-negative number of queued values for
instance.
Return one instance's pending messages from the loaded RPC snapshot.
Configure the RPC with
load_channel_map_instances=(this_channel_map.load_messages(instance),) or
load the whole map with load_channel_maps=(this_channel_map,).
Arguments:
- context: The current RPC Context.
- instance: The non-empty logical map key.
Returns:
Pending message IDs and decoded values in FIFO order.
Raises:
- ChannelMessagesNotLoadedError: If the RPC did not load this ChannelMap's messages.
Find one loaded ChannelMap message by server-assigned ID.
Arguments:
- context: The current RPC Context.
- instance: The non-empty logical map key.
- message_id: The message ID to find.
Returns:
The matching message, or
Nonewhen absent from the snapshot.
Return the number of non-empty instances visible to the current RPC.
Arguments:
- context: The current RPC Context.
Returns:
The number of keys after including publications buffered by this RPC.
Return decoded non-empty instance keys in ascending order.
Arguments:
- context: The current RPC Context.
Returns:
An immutable tuple including publications buffered by this RPC.
Return values selected for one instance by the satisfied condition.
Arguments:
- context: The current Step Context.
- instance: The map instance. Slash is prohibited because it is a reserved character.
Returns:
An ordered, read-only sequence for this Step execution.
Create an instance condition that consumes exactly one value.
Arguments:
- instance: The map instance. Slash is prohibited because it is a reserved character.
- condition_id: Optional stable condition identifier.
Returns:
A ChannelMap condition for one value.
Create an instance condition that consumes exactly count values.
Arguments:
- instance: The map instance. Slash is prohibited because it is a reserved character.
- count: The required non-negative value count.
- condition_id: Optional stable condition identifier.
Returns:
A condition with equal lower and upper bounds.
Create an instance condition requiring at least count values.
Arguments:
- instance: The map instance. Slash is prohibited because it is a reserved character.
- count: The inclusive, non-negative lower bound.
- condition_id: Optional stable condition identifier.
Returns:
A condition with no upper bound.
Create a non-blocking instance condition consuming up to count values.
The condition does not wait on its own for values to accumulate. When
its surrounding Wait completes, Dex consumes up to count values
queued for the instance at that time. An empty queue produces no values.
Arguments:
- instance: The map instance. Slash is prohibited because it is a reserved character.
- count: The inclusive, non-negative upper bound.
- condition_id: Optional stable condition identifier.
Returns:
A condition with no positive lower bound.
Create a bounded condition for one ChannelMap instance.
Dex waits only for at_least. Once that bound is met, it consumes
currently queued values up to at_most. Omitting at_least makes
the condition complete immediately.
Arguments:
- instance: The map instance. Slash is prohibited because it is a reserved character.
- at_least: Optional inclusive lower bound.
- at_most: Optional inclusive upper bound.
- condition_id: Optional stable identifier unique within the Wait tree.
Returns:
A condition accepted by
Waitfactories.
Load one ChannelMap instance's pending messages for an RPC snapshot.
Attributes:
- channel_map: The exact ChannelMap definition registered with the Flow.
- instance: One logical instance key.
Represent one pending Channel message and its server-assigned identity.
Attributes:
- message_id: UUIDv7 assigned by Dex when the message was published.
- value: The decoded Channel value.
Indicate that a pending Channel message ID no longer exists.
Indicate that an RPC did not load the requested pending Channel messages.
Call Dex FlowService through registered, typed Flow definitions.
Client methods are synchronous and may block on network I/O. Create one Client per Registry, share it across calling threads, and close it at application shutdown. The Client owns its gRPC channel but not the Registry or BlobCache.
Construction performs no network I/O. The first service request negotiates Server protocol 4 with GetServerInfo. Service methods raise RuntimeError only when the Server reports a protocol range this SDK cannot use. The message names the Server version and says whether to upgrade the Server (server-zd newer than v0.1.16) or the SDK. That result is cached, and every later call raises it again. When GetServerInfo itself fails, for example because the Server is unreachable, the call raises the same DexServiceError as any other transport failure. Such failures are not cached, so the next call negotiates again. Negotiation counts against a call's request timeout.
Attributes:
- registry: Flow definitions used to validate typed calls.
- blob_cache: The open cache used to hydrate large values.
- options: The effective ClientOptions.
Examples:
>>> with Client(registry, cache) as client: ... run_id = client.start_flow(orders, "order-42", OrderInput("42")) ... result = client.wait_for_flow("order-42").single_output(OrderResult)
Construct a Client and its lazy plaintext gRPC channel.
Arguments:
- registry: The Flow Registry used for call validation and codecs.
- blob_cache: An open cache used to hydrate large response values.
- options: Service address and bearer credential;
Noneuses defaults and sends no authorization metadata.
Start a Flow and return its server-assigned run ID.
The call returns after Dex accepts the Flow, not after completion. flow
must be the exact instance registered with this Client, and input must
match its starting Step annotation. Options control timeout, reuse, retries,
configuration, idempotency, and initial Attributes.
The first request negotiates Server protocol 4 and caches compatibility. Construction performs no network I/O. An incompatible Server fails this and every later request; transient negotiation failures can be retried.
Arguments:
- flow: The registered Flow instance to start.
- flow_id: A non-empty application ID stable across runs.
- input: The starting Step input, or
Nonewhen no starting Step exists. - options: Settings for the new Flow execution.
Returns:
The server-assigned run ID.
Raises:
- FlowAlreadyStartedError: If reuse policy rejects an existing Flow ID.
- FlowDefinitionError: If
flowis not registered. - ValueMappingError: If
inputor an initial Attribute cannot be encoded, including a numeric or Boolean value for a hard-delete Attribute. - ValueError: If an initial Attribute uses a different
hard_delete_on_nullsetting than the Flow's registered definition of that name. - RuntimeError: If the Server reports a protocol range without protocol 4.
- DexServiceError: If FlowService rejects or cannot perform the request.
Synchronously invoke a registered RPC on a Flow execution.
Pass the bound method from the same registered Flow instance. RPC timeout and
Attribute locks come from its @rpc decorator. The call blocks until the
handler returns, fails, or times out. A non-transactional RPC without Attribute
locks starts from a backend query. If the handler returns no durable effects, a
retained terminal execution can serve that query. Locks, transactional execution,
returned effects, or server policy can require an active execution.
Arguments:
- rpc_method: A bound method decorated with
@rpc. - flow_id: The non-empty target Flow ID.
- input: The annotated RPC input, or
Nonefor an input-free RPC. - run_id: Optional exact run. With
"", the server resolves the current execution. - options: Additive runtime AttributeMap locks and exact map-instance loads.
Returns:
The decoded
RPCResult.output, orNonefor a no-output RPC.
Raises:
- FlowDefinitionError: If the method is not registered.
- RpcLockConflictError: If requested Attribute locks cannot be acquired.
- WorkerInvocationError: If the application handler fails.
- ValueMappingError: If input or output mapping fails.
- FlowNotActiveOrNotFoundError: If the Flow is missing or the selected path cannot use its closed execution.
- DexServiceError: If the service call otherwise fails.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Append one typed best-effort Stream message with source metadata.
Arguments:
- flow_id: Logical Flow instance ID; the Flow need not exist or be active.
- stream: Exact Stream object registered in one Flow schema.
- source: Non-empty informational source. Repeated values still append.
- value: Typed message to append.
Raises:
- ValueError: If the Flow ID or source is empty.
- FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If the message cannot be encoded.
- DexServiceError: If FlowService cannot append the message.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Block for the next retained Stream message after a resume token.
Arguments:
- flow_id: Logical Flow instance ID used as the Stream instance key.
- stream: Exact Stream object registered in one Flow schema.
- resume_token: Previous message token, or empty for the retained head.
- timeout: Optional non-negative server-side long-poll duration.
Returns:
The decoded message, next resume token, creation time, and source.
Raises:
- LongPollTimeoutError: If no message arrives before the server wait expires.
- FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If the retained message cannot be decoded.
- DexServiceError: If FlowService cannot perform the read.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Return one newest-first page of retained Stream messages without waiting.
An empty token starts at the retained tail. Pass the returned page token unchanged to read older messages. Trimming may remove messages between pages.
Arguments:
- flow_id: Logical Flow instance ID used as the Stream instance key.
- stream: Exact Stream object registered in one Flow schema.
- page_size: Positive maximum message count within the server-configured limit.
- before_page_token: Opaque token from the preceding page, or
""first.
Returns:
Decoded messages in newest-first order and the next-page token.
Raises:
- ValueError: If
page_sizeis not positive. - FlowDefinitionError: If the Stream is not registered.
- ValueMappingError: If a retained message cannot be decoded.
- DexServiceError: If FlowService cannot list the Stream.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Block until a Flow closes and return its terminal result.
timeout is the server-side long-poll duration. A long-poll timeout is
retryable by calling this method again. Parallel completion order is not
deterministic; select results by Step identity rather than position.
Arguments:
- flow_id: The non-empty target Flow ID.
- timeout: Optional non-negative server-side wait duration.
Returns:
The terminal status, failure detail, and output-bearing completions.
Raises:
- LongPollTimeoutError: If the Flow remains open when the wait expires.
- FlowNotFoundError: If
flow_iddoes not exist. - ValueMappingError: If Dex returns a malformed or incompatible hydrated value.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Request cancellation, termination, or failure of an active Flow.
The call returns after Dex accepts the request and does not wait for terminal status.
Arguments:
- flow_id: The non-empty target Flow ID.
- options: Stop mode and optional recorded reason.
Raises:
- FlowNotActiveOrNotFoundError: If no active run exists.
- DexServiceError: If FlowService rejects or cannot perform the request.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Return summary metadata for the current or latest Flow run.
Arguments:
- flow_id: The non-empty Flow ID to describe.
Returns:
Flow ID, run ID, Flow type, status, and UTC start time.
Raises:
- FlowNotFoundError: If
flow_iddoes not exist. - DexServiceError: If FlowService cannot return the summary.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Read committed Attribute values from the current or latest Flow run.
Use get_attributes to read Flow state from outside the Flow without
defining an RPC. Pass one or more reads. A read is a singleton
Attribute or one AttributeMap instance selected with
AttributeMap.instance(key). To read one Attribute, pass a single read.
The Client sends every key in one FlowService request, so all values come
from the same committed state. No Worker runs.
The call targets the current run, or the latest run when no run is open. A closed run stays readable until Dex purges it. The call sees only committed state. Unlike reading inside an RPC handler, it never sees writes that a running Step or RPC has staged but not yet committed.
Each read must use a definition that some Flow in this Client's Registry
declares in its PersistenceSchema. The definition matches when it is
equal to the registered one: same class (Attribute or
AttributeMap), name, value_type, index,
sync_to_attribute_store, and hard_delete_on_null. The definition
may come from any registered Flow. The server does not check that the
target Flow's type declares it, so a same-named Attribute of another type
in the target Flow fails to decode.
Attributes defined with hard_delete_on_null=True are readable; the
server resolves their stored values. The Client resolves other large
values through LoadBlobs and the Client's BlobCache, and then decodes every
value. On any error the call returns nothing, so no partial result is
visible. Every error message names the Flow ID. An error about one read or
value also names its Attribute name or physical key.
Example::
ORDER_STATUS = Attribute("order-status", str)
NOTES = AttributeMap("notes", Note)
try:
values = client.get_attributes(
"order-42", ORDER_STATUS, NOTES.instance("n1")
)
except FlowNotFoundError:
return None
status = values.get(ORDER_STATUS, "new")
if NOTES.instance("n1") in values:
note = values[NOTES.instance("n1")]
Arguments:
- flow_id: The non-empty Flow ID.
- *reads: At least one
AttributeorAttributeMapInstance. Two reads of the same Attribute or AttributeMap instance are rejected.
Returns:
The set values, keyed by the reads passed to this call. An Attribute or AttributeMap instance without a stored value is absent from the result.
Raises:
- ValueError: If
flow_idis empty, no read is given, two reads select the same Attribute or AttributeMap instance, or the response has an unexpected key, a duplicate key, or an entry without a value. - TypeError: If a read is not an
AttributeorAttributeMapInstance, such as anAttributeMapwithout an instance. - FlowDefinitionError: If no Flow in the Registry declares a matching definition, including a same-named definition with another value type.
- FlowNotFoundError: If the Flow does not exist or its latest run is being purged.
- ValueMappingError: If a returned value cannot be resolved or decoded as its value type. This includes an empty or non-finite value, an unsupported object encoding, and a large value that LoadBlobs omits.
- DexServiceError: If FlowService or LoadBlobs otherwise fails. A LoadBlobs
failure for a missing Flow raises
FlowNotFoundError. - RuntimeError: If the Server reports a protocol range without protocol 4.
Return one page of Flow runs matching a visibility query.
Arguments:
- query: A Dex visibility query; an empty string matches server defaults.
- page_size: The non-negative maximum result count requested.
- next_page_token: Opaque token from the preceding page, or
""first. - include_continued_as_new: Include earlier runs without overriding query filters.
Returns:
Server-ordered entries with hydrated indexed Attributes and a next token.
Raises:
- ValueError: If
page_sizeis negative. - ValueMappingError: If an indexed Attribute cannot be hydrated.
- DexServiceError: If FlowService cannot execute the query.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Create a new run from a selected point in existing Flow history.
Arguments:
- flow_id: The non-empty Flow ID whose history supplies the new run.
- options: Time travel selector, reason, and replay controls.
Returns:
The new server-assigned run ID; the Flow ID is unchanged.
Raises:
- FlowNotFoundError: If
flow_iddoes not exist. - ValueError: If time travel selector fields are invalid.
- DexServiceError: If FlowService cannot create the new run.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Make one waiting Timer condition immediately ready.
Arguments:
- flow_id: The non-empty active Flow ID.
- step_execution_id: The Step type and positive execution number.
- timer_id: Exactly one Timer condition ID or zero-based index.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- ValueError: If an identifier is invalid.
- DexServiceError: If FlowService cannot skip the Timer.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Block until one Step execution completes or its request timeout expires.
The server derives a stable Request ID from the Step execution when none is supplied. Request timeout covers transparent transport reattachments. Internal handler timeout controls Update generation rollover without ending this request.
Arguments:
- flow_id: The non-empty active Flow ID.
- step_execution_id: The Step type and positive execution number.
- options: The optional Request ID, request timeout, and internal handler timeout controls.
Raises:
- ValueError: If the Request ID or request timeout is invalid.
- RequestTimeoutError: If a positive request timeout expires first.
- FlowNotActiveOrNotFoundError: If the Flow is missing or closes first.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Wait for a singleton Attribute or AttributeMap instance to match.
Singleton form is wait_for_attribute_match(flow_id, attribute, match,
options); map form adds instance before match.
Arguments:
- flow_id: The non-empty active Flow ID.
- attribute: The registered Attribute or AttributeMap to observe.
- *args: Positional match and options, optionally preceded by a map instance.
- **kwargs: The same arguments supplied by name.
Returns:
The current value that satisfied the match.
Raises:
- TypeError: If arguments do not match the Attribute definition.
- ValueError: If an identifier, option, or expected value is invalid.
Also raised before transport when the Attribute uses
hard_delete_on_null. - RequestTimeoutError: If a positive request timeout expires first.
- FlowNotActiveOrNotFoundError: If the Flow is missing or closes first.
- DexServiceError: If FlowService cannot perform the wait.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Update specified mutable configuration fields for an active Flow.
The update affects later decisions and does not recall work already
dispatched. Unspecified fields and the existing Worker target remain
unchanged. An empty FlowConfig() leaves all configuration unchanged.
Arguments:
- flow_id: The non-empty active Flow ID.
- config: The new optional configuration fields.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- ValueError: If a configuration value is invalid.
- DexServiceError: If FlowService cannot apply the update.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Ask an active Flow to roll history into a new run.
The request returns after acceptance; the Flow ID remains stable and the run ID changes when rollover occurs.
Arguments:
- flow_id: The non-empty active Flow ID.
Raises:
- FlowNotActiveOrNotFoundError: If the Flow is missing or closed.
- DexServiceError: If FlowService cannot accept the request.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Call the FlowService health endpoint.
Returns:
Truewhen the service returns successfully.
Raises:
- DexServiceError: If the service is unhealthy or unreachable.
- RuntimeError: If the Server reports a protocol range without protocol 4.
Close the owned gRPC channel.
close is idempotent. Other Client methods must not be called afterward.
The Registry and BlobCache remain owned by the caller.
Configure FlowService connectivity and bearer credentials.
Attributes:
- server_address: The plaintext gRPC target for Dex. Defaults to
"localhost:8801"and must not include an HTTP scheme. - credential: Supplies the current bearer token for every FlowService call.
Nonesends no authorization metadata. A token error fails the call before contacting Dex. AsyncClient accepts an awaitable token and bounds its lookup by the RPC timeout when set. Synchronous token lookup must return promptly. Protect the plaintext network path.
Supply a bearer token for each Client or Worker service call.
token may be called concurrently. AsyncClient and AsyncWorker accept an awaitable token; synchronous callers require a string. Synchronous tokens run on the calling thread for sync APIs and in an SDK-owned, four-thread executor for async APIs. Each Client or Worker creates its executor lazily and closes it without waiting for a blocked token call. A blocked token keeps one executor thread busy until it returns. An unclosed Client or Worker may retain up to four idle credential threads. Declare token with async def to invoke it on the event loop. A synchronous token method may return a coroutine, which is awaited on the event loop. A loop-bound Future from another loop raises TypeError. Async calls bound token lookup by the RPC deadline when one is set. Pull stream opens use a 30-second lookup budget. Sync calls do not bound token lookup separately, so synchronous implementations must return promptly. Client calls raise a token exception unchanged, before contacting Dex. Worker startup may wrap it with startup context. The token travels over Dex's plaintext gRPC connection; protect the network path.
Example:
>>> class FixedCredential: ... def token(self) -> str: ... return "my-token" >>> credential = FixedCredential() >>> options = ClientOptions(credential=credential)
Define bidirectional conversion between a Python type and Dex Values.
Custom codecs must be deterministic: equivalent inputs should produce the same Value so durable replays observe identical data.
Return the application-facing type name used in error messages.
Returns:
A stable, non-empty type name.
Return the single wire kind emitted and accepted by this codec.
Returns:
The codec's protocol representation.
Encode one typed Python value.
Arguments:
- value: The application value to encode.
Returns:
A Value whose kind equals
wire_kind.
Raises:
- TypeError: If
valueis incompatible with the codec. - ValueError: If the value is not representable by Dex.
Resolve codecs for type hints used by registered SDK definitions.
Explicit registrations take precedence over built-ins. The default registry
supports primitive types, None, datetime, dataclasses, enums, and typed
list, tuple, mapping, and sequence containers. Parameterized generic
dataclasses substitute their type arguments recursively. Optional types and
unions use JSON, including when their alternatives have primitive codecs.
Unions select an alternative by JSON kind: null, boolean, number, string,
array, or object. Boolean values never select an integer alternative. A
union passed directly to resolve() needs alternatives with disjoint
kinds. For example, Receipt | None and int | str work, but
int | float, str | datetime, and unions of two dataclasses are
ambiguous and raise TypeError during resolution. A custom codec can
implement a discriminator. A Literal alternative, such as
Literal["draft", "sent"] | None, uses the JSON kinds of its members.
Decoding raises ValueError for a value outside the Literal. Members
match by exact type, so JSON true never matches Literal[1].
Unions inside dataclass fields and containers keep the decoding of earlier
releases and never raise during resolution. Such a union decodes a value
into a dataclass, Enum, or datetime alternative, or a container of those,
when the value's JSON kind matches only that alternative. Otherwise it
returns the JSON value unchanged, without a type check. A nested
Literal also returns the JSON value unchanged. Fields typed by a
type variable and Mapping or Sequence types follow the same rule:
Result[Receipt] decodes a Receipt, but Result[int] returns the
JSON value of its type-variable field unchanged. A float accepts a JSON
integer and keeps it as an int.
Dataclass fields use their Python names unless
field(metadata={"dex_json_name": "callId"}) specifies a non-empty wire
name. Names must be unique within the dataclass. Unknown input keys are
ignored. Absent keys use the declared default or default factory; a missing
required field raises ValueError. Explicit null for a defaulted field still
decodes as null. It raises TypeError when the field type checks values,
such as int, a dataclass, or datetime.
dex_json_omit_default=True in field metadata omits values equal to the
field's default. It requires a default or default factory. A factory is
called again for comparison on every encode, so it must be deterministic
and free of side effects. Without this opt-in, defaults and nulls are
included in encoded JSON.
Datetime fields decode ISO 8601 strings, including Go RFC3339 strings with
Z or numeric timezone offsets. Python retains microsecond precision;
additional fractional digits are truncated on decode. Encoding retains
datetime.isoformat() by default. Field metadata
dex_json_format="rfc3339" instead emits Go-compatible timestamps with
Z for UTC and no trailing fractional zeroes. This opt-in requires a
datetime or optional datetime field and an aware value whose timezone offset
is a whole number of minutes.
Examples:
>>> registry = CodecRegistry({Money: money_codec}) >>> registry.resolve(Money) is money_codec TrueA generic dataclass needs no explicit codec registration::
from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Generic, TypeVar from dex import CodecRegistry ResultT = TypeVar("ResultT") @dataclass class Receipt: call_id: str = field(metadata={"dex_json_name": "callId"}) observed_at: datetime = field(metadata={ "dex_json_name": "observedAt", "dex_json_format": "rfc3339" }) @dataclass class Result(Generic[ResultT]): value: ResultT receipt: Receipt | None = None failure: str | None = field(default=None, metadata={ "dex_json_omit_default": True }) codec = CodecRegistry().resolve(Result[int]) result = Result(42, Receipt("call-1", datetime.now(timezone.utc))) assert codec.decode(codec.encode(result)) == result
Create a registry with optional custom type-hint mappings.
Arguments:
- codecs: Custom codecs keyed by the exact type hint used in definitions. The mapping is copied and may be mutated after construction.
Return the codec for an exact type hint.
Arguments:
- type_hint: A runtime type or supported parameterized type hint.
Returns:
The registered, built-in, or automatically derived codec.
Raises:
- TypeError: If no codec can represent
type_hint,type_hintis a union with overlapping or unsupported alternatives, or dataclass JSON metadata is invalid.
Group Conditions that must become ready together.
Pass combinations to Wait.any_combination_of() when several alternative
groups are acceptable.
Attributes:
- conditions: The ordered Conditions in this all-of group.
Expose execution metadata and decision-local Step operations.
Dex supplies a Context to every wait_for and execute call. Do not retain
it after the handler returns. Attribute and Channel definition methods provide
the typed persistence API; the methods below expose execution diagnostics and
per-attempt local state.
Return the stable Flow ID shared by all runs.
Returns:
The application-assigned Flow ID.
Return the current server-assigned run ID.
Returns:
The run ID, which changes after time travel or continue-as-new.
Return when the current Flow run started.
The server supplies the same value across Steps and handler retries in
this run. It is available in wait_for, execute, Flow timeout
handlers, and RPC handlers. This is the run start time, rather than the
start time of the current Step or attempt.
Returns:
A timezone-aware UTC datetime with one-second precision.
Examples:
Measure the current run's age inside a handler::
from datetime import datetime, timezone elapsed = datetime.now(timezone.utc) - context.flow_started_at elapsed_seconds = elapsed.total_seconds()
Return the current Step execution's protocol identifier.
Returns:
The Step type and execution number encoded by Dex.
Return the predecessor Step execution identifier.
Returns:
The originating identifier, or an empty string at Flow start.
Return the final failure that selected this recovery path.
Returns:
Recovery information, or
Noneoutside a recovery path.
Return when the first attempt of this handler invocation started.
The value is stable across retries of the same invocation, so a handler can derive a deadline that a retry does not reset.
Returns:
A timezone-aware UTC time with one-second precision.
Return the current Step method attempt's absolute deadline.
RetryPolicy total duration does not shorten a running attempt. Use this
inside wait_for, execute, or a Flow timeout handler to budget
external work; each retry receives its own effective deadline.
Returns:
A timezone-aware UTC datetime with microsecond precision, or
Nonefor RPC handlers and tasks without a server-supplied deadline.
Examples:
Compute the remaining attempt budget in seconds inside a handler::
from datetime import datetime, timezone deadline = context.deadline if deadline is not None: remaining_seconds = (deadline - datetime.now(timezone.utc)).total_seconds()
Return the one-based handler retry attempt.
Returns:
1for the initial attempt and a larger value after retries.
Report whether the previous regular attempt persisted heartbeat details.
Returns:
Truewhen a heartbeat Value is present. A persisted PythonNonestill counts as present.
Decode the previous regular attempt's heartbeat checkpoint.
Use has_last_heartbeat_value() to distinguish an absent checkpoint
from a persisted Python None.
Arguments:
- value_type: The expected Python type and codec selection.
Returns:
The decoded checkpoint, or
Nonewhen no Value is present.
Raises:
- ValueMappingError: If the Value cannot be decoded as
value_type.
Report whether a Timer made the current Wait ready.
Arguments:
- index: Optional zero-based Timer index;
Nonechecks any Timer.
Returns:
Truewhen the selected Timer fired for this execution.
Report whether wait_for failed before this execute call.
Returns:
Truewhen the Step's failure policy proceeded after a wait error.
Report whether Dex canceled the active handler call.
Async handlers are normally canceled by their task. Synchronous handlers cannot be forcefully interrupted by Python; long CPU-bound work may check this at natural boundaries. External effects still require idempotency or compensation.
Returns:
Trueafter the Worker gRPC call has been canceled.
Store process-local data for this Step execution.
Local data is not persisted, replicated, or available after worker restart; use Attributes for durable state.
Arguments:
- key: A non-empty key scoped to the Step execution.
- value: The in-memory object to retain by reference.
Return typed process-local data for this Step execution.
Arguments:
- key: The key passed to
set_step_execution_local. - value_type: The required runtime type.
Returns:
The stored object, or
Nonewhen the key is absent.
Raises:
- TypeError: If the stored object is not an instance of
value_type.
Represent a structured error returned by a Dex FlowService operation.
Attributes:
- code: The outer gRPC status code.
- sub_status: The stable Dex-specific error classification.
- detail: The human-readable service detail.
- operation: The Client operation that failed.
- flow_id: The targeted Flow ID, or
Nonefor service-wide calls.
Create a structured FlowService error.
Arguments:
- code: The outer gRPC status code.
- sub_status: The Dex-specific classification.
- detail: Human-readable error detail.
- operation: The Client operation name.
- flow_id: The targeted Flow ID, if any.
Classify a FlowService error more precisely than its gRPC status.
Attributes:
- UNCATEGORIZED: Dex returned no specific classification.
- FLOW_ALREADY_STARTED: The requested Flow ID conflicts with an existing run.
- FLOW_NOT_EXISTS: No Flow with the requested ID exists.
- WORKER_API_ERROR: An application Worker rejected or failed an invocation.
- LONG_POLL_TIMEOUT: A wait ended without observing its condition.
- CHANNEL_MESSAGE_NOT_FOUND: A pending Channel message ID no longer exists.
- REQUEST_TIMEOUT: A caller-defined request budget expired.
- FLOW_CLOSING: Dex rejected an update-class request during Flow close cleanup.
Define a durable application Flow and its registered API surface.
Subclasses return their Steps and persistence schema and may expose methods
decorated with rpc(). Construct one Flow instance and pass it to Registry;
Client calls must use that same registered instance.
Return the protocol Flow type used by Client and Dex.
Override it to decouple the type from the Python class name.
Returns:
A non-empty Flow type unique within the Registry.
Return the Step definitions for this Flow.
Returns:
A StepList with zero or one starting Step and all reachable Steps.
Return the Attributes and Channels owned by this Flow.
Returns:
The persistence schema; the default is empty.
Handle expiration of this Flow's durable soft-timeout timer.
Override this method to make a positive timeout default to HANDLER.
Async Flow implementations may use async def with the same annotations.
The returned StepDecision uses ordinary Execute validation and may transition,
dead-end, complete, fail, or request graceful completion.
Arguments:
- context: The timeout-handler invocation Context; do not retain it.
Returns:
The decision applied after the timeout timer completes or is skipped.
Raises:
- NotImplementedError: If called without an application override.
Read a bearer token from a file and pick up rotations automatically.
The file is read during construction. Its mtime and size are checked at most once per second, and changed contents are reloaded before the next call. Surrounding whitespace is removed. A file that becomes empty or unreadable fails subsequent calls until restored. Concurrent calls share a lock. Async callers check the file in an executor, outside the event loop.
Example:
>>> credential = FileCredential("/var/run/secrets/dex/token") >>> client = Client(registry, cache, ClientOptions(credential=credential))
Arguments:
- path: Path to the token file.
Raises:
- OSError: The file cannot be read during construction.
- ValueError: The file has no token after trimming whitespace.
- UnicodeError: The file does not contain valid UTF-8 text.
Read the initial token from path and fail if it is unavailable or empty.
Arguments:
- path: File containing the bearer token.
Raises:
- OSError: The file cannot be read.
- ValueError: The file has no token after trimming whitespace.
- UnicodeError: The file does not contain valid UTF-8 text.
Return the current trimmed token, checking for file changes once per second.
Returns:
The token without the Bearer prefix.
Raises:
- OSError: The file is no longer readable.
- ValueError: The file is empty after trimming whitespace.
- UnicodeError: The file does not contain valid UTF-8 text.
Indicate that start_flow conflicts with an existing Flow ID.
Report a request rejected while Dex closes a Flow.
Re-read the retained terminal Flow state before deciding what to do next. Catch this error directly instead of comparing the service detail::
from dex import FlowClosingError, WaitForStepCompletionOptions
try:
await client.wait_for_step_completion(
flow_id, step_execution_id, WaitForStepCompletionOptions()
)
except FlowClosingError:
terminal_result = await client.wait_for_flow(flow_id)
Override server behavior for one Flow execution.
At start time, every None field uses the registered or server default.
Client configuration updates preserve existing values for None fields.
Attribute Store projection is asynchronous. Failures never roll back Flow Attributes, and already queued projections retain their original target.
Attributes:
- active_step_search_mode: Optional active-Step visibility indexing policy.
- continue_as_new_threshold: Optional positive history-event threshold that requests continue-as-new.
- continue_as_new_page_size_bytes: Optional positive history page-size budget in bytes used by continue-as-new decisions.
- step_durability: Optional default durability for Step handlers.
- attribute_store_names: Optional Server-configured Attribute Store names.
Noneleaves the field absent; an empty list disables future projections.
Indicate an invalid Flow definition or a definition the Registry lacks.
Registry construction raises it for an invalid Flow definition. Client calls
raise it before contacting Dex when an argument is not registered or does not
match the registered definition. For example, get_attributes raises it for
an Attribute whose value type differs from the one a Flow declares.
Identify the terminal failure category reported for a Flow.
Attributes:
- STEP_DECISION_FAILED: A Step decision could not be applied.
- CLIENT_API_FAILED: A Client-originated operation failed the Flow.
- WORKER_API_FAILED: A Worker handler or dispatch failed.
- INVALID_USER_FLOW_CODE: Application Flow definitions or results were invalid.
- INTERNAL: Dex encountered an internal invariant or infrastructure failure.
- FLOW_TIMEOUT: A Dex soft Flow timeout expired under the fail policy.
Summarize the current or latest run of one Flow ID.
Attributes:
- flow_id: The stable application-assigned Flow ID.
- run_id: The server-assigned run ID.
- flow_type: The registered Flow type name.
- status: The run's current lifecycle status.
- started_at: The timezone-aware server start timestamp.
Describe an observed Flow status and its output-bearing completions.
Client results are terminal. A SubFlow result can be a running snapshot when another AnyOf Condition won; that status is not a live backend query.
Attributes:
- status: The observed lifecycle state.
- error_type: Dex failure category when available.
- error_message: Server failure detail when available.
- completions: An immutable sequence of Step completions.
Create an immutable Flow result snapshot.
Arguments:
- status: The observed Flow status.
- completions: Output-bearing Step completions in server collection order.
- error_type: Optional Dex failure category.
- error_message: Optional server failure detail.
Return the observed Flow lifecycle state.
Returns:
The status captured by this result.
Return the Dex failure category, if one was reported.
Returns:
The failure category, or
Nonewhen Dex reported none.
Return the server failure detail, if one was reported.
Returns:
The failure detail, or
Nonewhen Dex reported none.
Return whether this observed run can no longer execute.
Returns:
Truefor a terminal snapshot; otherwiseFalse.
Return completions in server collection order.
Returns:
An immutable tuple of output-bearing Step completions.
Decode the output when exactly one completion exists.
Arguments:
- output_type: The concrete Python type expected by the caller.
Returns:
The only decoded Step output.
Raises:
- ValueError: If the result is nonterminal or has zero or multiple outputs.
- ValueMappingError: If the output cannot be decoded as
output_type.
Report a missing Flow or an operation that cannot use a closed Flow.
RPC calls use this error for missing targets, including query-only reads. A query-only RPC can read a retained terminal execution when it has no locks, transactionality, durable effects, or Server-forced Update routing. At that confirmed read boundary, handle this error as not found without adding a lifecycle probe. Preserve other service and Worker failures.
Mutations and active-only RPCs also use this error for closed targets. The error does not distinguish a missing Flow from a closed Flow or prove that the requested action succeeded. The inherited service metadata and chained transport cause remain available. For example::
try:
client.invoke_rpc(orders.update_order, order_id, update)
except FlowNotActiveOrNotFoundError:
raise OrderUnavailableError(order_id)
Indicate that an operation targeted a Flow ID that does not exist.
Describe the lifecycle state of a Flow run.
Attributes:
- RUNNING: The Flow can still accept work and state changes.
- COMPLETED: The Flow completed successfully.
- FAILED: The Flow ended because application or worker processing failed.
- CANCELED: The Flow cooperatively accepted a cancellation request.
- TERMINATED: The Flow was forcibly terminated.
- SERVER_SIDE_TIMEOUT_INTERNAL_ONLY: Reserved backend hard-timeout status.
- CONTINUED_AS_NEW: This run rolled forward into a new run.
Configure timeout-handler execution and selective state loading.
Ordinary Attributes and Channel size metadata load automatically. AttributeMap values and pending Channel messages require an explicit selection. One logical handler execution may contain multiple retry attempts.
Attributes:
- method_timeout: Maximum duration of one handler attempt.
- heartbeat_timeout: Progress deadline for one regular attempt.
- retry: Optional retry policy;
Noneuses server defaults. - durability: Durability for successful handler side effects.
- lock_attributes: Attribute locks held during the handler invocation.
- load_attribute_maps: AttributeMaps whose current instances load.
- load_attribute_map_instances: Exact AttributeMap instances to load.
- load_channels: Channels whose pending messages load.
- load_channel_maps: ChannelMaps whose current instance messages load.
- load_channel_map_instances: Exact ChannelMap instances to load.
Examples:
>>> options = FlowTimeoutHandlerOptions( ... method_timeout=timedelta(seconds=30), ... retry=RetryPolicy(maximum_attempts=3), ... load_channels=(commands,), ... ).on_failure_proceed_to(TimeoutRecovery)
Return a copy routing exhausted retries to a registered no-input Step.
The recovery Step receives None and reads the final handler failure from
Context.recovery_error. Select it by class, registered instance, or
StepRef. Use an instance or StepRef when the Flow registers more than one
instance of the recovery Step class. The target is validated when Client
or SubFlow start options are mapped, not when this method is called.
Arguments:
- step: Recovery class, registered instance, or StepRef. It must belong to
the timed-out Flow and use
Noneinput. - options: Optional execution overrides for the recovery movement.
Returns:
A new options value with failure routing configured.
Examples:
>>> options = FlowTimeoutHandlerOptions( ... retry=RetryPolicy(maximum_attempts=1), ... ).on_failure_proceed_to(StepRef("TimeoutRecovery"))
Control how Dex responds when a positive soft Flow timeout expires.
Attributes:
- DEFAULT: Invoke
Flow.handle_timeoutwhen overridden; otherwise fail. - FAIL: Fail with
FlowErrorType.FLOW_TIMEOUTand permit Flow retries. - CANCEL: Cancel without retrying the Flow.
- HANDLER: Start one retryable
Flow.handle_timeoutexecution after its timer.
Describe one FlowService health-check response.
Attributes:
- condition: The service-reported health condition.
- hostname: The hostname of the responding Dex server.
- duration_seconds: The server-reported response duration in seconds.
Control whether start_flow may reuse an existing Flow ID.
Attributes:
- DEFAULT: Use the Dex server default, which rejects every previously used Flow ID.
- ALLOW_IF_PREVIOUS_FAILED: Reuse only after an unsuccessful closed run.
- ALLOW_IF_NOT_RUNNING: Reuse after any closed run.
- ALLOW_TERMINATE_IF_RUNNING: Terminate an active run and start the new run.
- DISALLOW: Reject every previously used Flow ID.
Identify a Step or RPC handler result that violates the SDK contract.
Attributes:
- flow_type: The containing Flow type.
- step_type: The Step type, or
Nonefor an RPC. - method: The handler method that returned the invalid value.
- detail: A precise description of the contract violation.
Create an invalid-result definition error.
Arguments:
- flow_type: The containing Flow type.
- step_type: The Step type, or
Nonefor an RPC. - method:
"wait_for","execute", or"rpc". - detail: A precise description of the invalid result.
Select how Dex indexes an Attribute for Flow search.
Attributes:
- KEYWORD: Index an exact string value.
- FULL_TEXT: Index a string for tokenized full-text matching.
- KEYWORD_ARRAY: Index every string in a sequence as a keyword.
- INT: Index a signed 64-bit integer.
- DOUBLE: Index a finite floating-point number.
- BOOL: Index a Boolean value.
- DATETIME: Index a
datetime.datetimevalue.
Encode an application type as deterministic JSON text.
encoder first converts the typed value to JSON-compatible data; decoder
performs the reverse conversion. JSON is emitted with sorted keys, compact
separators, and no NaN or infinity values.
Attributes:
- type_name: The stable type name used in diagnostics.
- decoder: A callable from parsed JSON-compatible data to
ValueT. - encoder: A callable from
ValueTto JSON-compatible data. - expected_type: Optional runtime type hint validated before encoding.
- wire_kind: Always
WireKind.JSON.
Examples:
>>> codec = JsonCodec("Point", lambda data: Point(**data), vars, Point) >>> codec.decode(codec.encode(Point(x=1, y=2))) Point(x=1, y=2)
Encode a typed value as canonical JSON.
Arguments:
- value: The application value accepted by
encoder.
Returns:
A JSON-kind Value containing compact, sorted JSON text.
Raises:
- TypeError: If
expected_typedoes not match. - ValueError: If the encoded data contains unsupported JSON values.
Indicate that a retryable long poll ended before its condition was observed.
Indicate that a caller-defined request budget expired.
Declare the Attributes, Channels, and Streams owned by a Flow type.
Definitions must have unique names and are immutable after Registry creation.
Attributes:
- attributes: Singleton and map Attribute definitions.
- channels: Singleton and map Channel definitions.
- streams: Best-effort Stream definitions.
Examples:
>>> schema = PersistenceSchema.of(status, balances, approvals, events)
Partition persistence definitions into Attributes, Channels, and Streams.
Arguments:
- *definitions: Attribute, AttributeMap, Channel, ChannelMap, or Stream definitions.
Returns:
A schema with each category kept in argument order.
Raises:
- TypeError: If a value is not a supported persistence definition.
Return typed RPC output, next-Step movements, and Step cancellation.
Attributes:
- output: The value decoded into the Client caller's expected output type.
- next_steps: Step movements applied atomically with handler persistence writes.
- canceling_steps: Flow-wide Step types canceled before next Steps are queued. Each entry is a class, registered instance, or StepRef; narrow it before use. The Worker validates entries when it applies this result, and an invalid entry raises FlowDefinitionError.
Return a result canceling all current executions of selected Step types.
Dex resolves the selection after RPC persistence commits and before next_steps.
Finished, already-canceled, and absent executions are no-ops. RPCs cannot select
siblings because an RPC has no Step execution lineage. The Worker validates
each target when it applies the returned result.
Arguments:
- *steps: Registered instances, StepRef values, or unambiguous Step classes.
Returns:
A new RPCResult containing the combined cancellation selectors.
Add runtime-selected map instances to one RPC invocation.
Dex unions these selections with the RPC decorator's fixed locks and state loads, then sorts and deduplicates their physical names. Locking and loading are independent. A read-modify-write handler must include the same AttributeMap instance in both collections.
Attributes:
- lock_attribute_map_instances: AttributeMap instance locks for this call.
- load_attribute_map_instances: Exact AttributeMap instance snapshots.
- load_channel_map_instances: Exact ChannelMap pending-message snapshots.
Examples:
>>> options = RPCInvokeOptions( ... lock_attribute_map_instances=(profiles.lock(partition),), ... load_attribute_map_instances=(profiles.load(partition),), ... ) >>> client.invoke_rpc(directory.upsert_customer_profile, flow_id, profile, ... options=options)
Describe the final failure that selected a recovery path.
Attributes:
- detail: Failure message supplied by the Worker or backend.
- error_type: Original Worker error type or backend failure type.
- attempt_progress: Furthest progress across all attempts of this Step
method. Defaults to
StepAttemptProgress.UNSPECIFIEDfor older records and for construction without progress. Treat that default likeNOT_COMPLETED. OnlyNOT_STARTEDguarantees the handler did not run. SeeStepAttemptProgressfor a recovery example.
Describe the furthest progress across attempts of a failed Step method.
Inspect Context.recovery_error.attempt_progress before deciding whether
recovery may safely repeat a side effect. Only NOT_STARTED guarantees
the handler did not run. Other values require idempotent recovery or an
application check for completed side effects.
Attributes:
- UNSPECIFIED: An older Server or a failure recorded before upgrade supplied
no progress. Treat it like
NOT_COMPLETEDbecause an attempt may have run. - NOT_STARTED: The handler did not run in any attempt.
- NOT_COMPLETED: Some attempt may have run, but none completed.
Examples:
>>> error = RecoveryErrorInfo("unavailable", "dispatch", StepAttemptProgress.NOT_STARTED) >>> can_repeat_side_effect = error.attempt_progress is StepAttemptProgress.NOT_STARTED
Requests a delay before Dex retries a failed worker method.
Raise an instance with retry_after() from a Step method when the
failure is retryable but the next attempt should happen at a known time.
Dex reports the original cause as the worker failure and uses the requested
delay instead of the retry policy's calculated interval.
Attributes:
- after_seconds (int): Positive delay in seconds before the next retry.
- cause (BaseException): Original failure reported to Dex and re-raised as the retry cause.
Indicate that an RPC could not acquire all requested Attribute locks.
Validate and store Flow definitions shared by Client and Worker.
Registry construction inspects Step and RPC signatures, resolves codecs, checks names and locks, and assembles Attribute indexes atomically. The result is safe for concurrent reads.
Attributes:
- flows: The registered Flow instances in input order.
- codec_registry: The codec registry used for every handler and persistence type.
Examples:
>>> orders = Orders() >>> registry = Registry([orders], CodecRegistry({Money: money_codec}))
Validate and assemble a set of Flow definitions.
Arguments:
- flows: Flow instances with unique Flow types.
- codec_registry: Optional custom codecs; defaults to
CodecRegistry(). - allow_async_handlers: Accept
async defStep and RPC handlers. Async Steps annotate AsyncContext; async RPCs and timeout handlers keep Context. Sync Client/Worker construction leaves thisFalse.
Raises:
- FlowDefinitionError: If any Flow, Step, RPC, persistence definition, annotation, codec, lock, or index is invalid.
Return the physical name for a map instance.
Arguments:
- name: The AttributeMap or ChannelMap name.
- instance: The map instance. Slash is prohibited because it is a reserved character.
Returns:
The physical name.
Raises:
- ValueError: If
instanceis empty or contains/.
Configure creation of a new run from existing Flow history.
Exactly one selector matching type must be set. The Client
validates combinations before sending the request.
Attributes:
- type: The time travel point selector kind.
- history_event_time: Timestamp used by
HISTORY_EVENT_TIME. - step_type: Registered Step type used by
STEP_TYPE. - step_execution_id: Exact execution ID used by
STEP_EXECUTION_ID. - step_method: WaitFor or Execute boundary required by
STEP_EXECUTION_ID. - reason: Optional operator-readable time travel reason.
- skip_writes_reapply: Do not replay RPCs, Channel publications, or Attribute writes after the selected point.
Select the Step method used as a Step execution time travel boundary.
Attributes:
- WAIT_FOR: Rerun WaitFor and everything after it.
- EXECUTE: Keep the WaitFor result and rerun Execute and everything after it.
Select the historical point from which time travel should create a new run.
Attributes:
- BEGINNING: Restart from the beginning of the Flow.
- HISTORY_EVENT_TIME: Resume at the first event at or after a timestamp.
- STEP_TYPE: Resume at the latest execution of a Step type.
- STEP_EXECUTION_ID: Resume at one exact Step execution ID.
Configure exponential retries for a Step handler or Flow.
A None duration or zero numeric value uses the server default. The default
total duration is four hours.
Attempts include the initial call. Retries stop at the first configured limit.
Step total duration starts when the Step is first handed to a Worker.
After an attempt fails, Dex uses the elapsed duration to decide whether to
schedule another retry. It never ends a running attempt.
Attributes:
- initial_interval: Delay before the first retry.
- backoff_coefficient: Interval multiplier; zero uses the server default.
- maximum_interval: Upper bound for one retry delay.
- maximum_attempts: Total attempt limit; zero uses the server default.
- total_duration: Retry scheduling budget counted from the first hand-out
to a Worker. Checked only after an attempt fails; it does not end a
running attempt.
Noneuses four hours.
Represent one indexed Flow run returned by search_flows.
Attributes:
- flow_id: The stable application-assigned Flow ID.
- run_id: The server-assigned run ID for this result.
- flow_type: The registered Flow type name.
- status: The indexed run status.
- started_at: The timezone-aware start timestamp.
- closed_at: The close timestamp, or
Nonefor open or unknown runs. - indexed_attributes: Hydrated Values keyed by physical search-index name.
Contain one server-ordered page of Flow search results.
Attributes:
- flows: The result entries in server-defined query order.
- next_page_token: An opaque token for the next request, or
""on the final page.
Contain one output-bearing Step completion returned by wait_for_flow.
Attributes:
- step_type: The registered Step type that produced the output.
- step_execution_id: The exact server Step execution identity.
Create a completion around an already hydrated wire value.
Arguments:
- completion: The hydrated completion message returned by Dex.
- decoder: The SDK value decoder used by
decode().
Return the registered Step type that produced this output.
Returns:
The registered Step type.
Configure creation of a new Flow execution.
Durations are datetime.timedelta values. None uses the registered
Flow or server default. Use with_attribute() to add initial Attribute writes.
To retry one logical start safely, for example after a lost response, set a
stable request_id and ignore_already_started=True together. Reuse the
same values on every retry.
Attributes:
- timeout: Optional durable soft timeout.
Noneor zero disables it. - timeout_policy: Action taken when a positive timeout expires.
- timeout_handler_options: Execute policy used when timeout policy is
HANDLER. - start_delay: Optional delay before the starting Step becomes eligible.
- id_reuse_policy: Flow ID reuse policy; defaults to
DEFAULT. - retry_policy: Optional Flow-level retry policy.
- config_override: Optional FlowConfig applied over registered defaults.
- ignore_already_started: Return the existing run rather than raising
FlowAlreadyStartedErrorwhen supported by the server. It applies only whenrequest_idmatches the request ID that started the existing run. A different or generated request ID still raisesFlowAlreadyStartedError. - request_id: Optional non-empty idempotency key.
Nonegenerates one. A retried start returns the existing run only whenignore_already_startedisTrueand this request ID matches the request that started that run. Without that option, the retry raisesFlowAlreadyStartedErroreven when the first attempt created the Flow. The match applies only when the ID reuse policy rejects the start. A policy that permits another run starts a new run instead. Dex compares only request IDs, not input or other options, so never reuse a request ID for a different logical start.
Examples:
>>> options = ( ... StartFlowOptions(timeout=timedelta(hours=1)) ... .with_attribute(status, "pending") ... .with_attribute(balances, "merchant-7", 1200) ... ) >>> retry_safe_options = StartFlowOptions( ... ignore_already_started=True, ... request_id=f"start-order-{order_id}", ... ) >>> run_id = client.start_flow( ... order_flow, f"order-{order_id}", order_input, retry_safe_options ... )
Return a copy with one initial Attribute value appended.
Singleton Attributes take (attribute, value); AttributeMaps take
(attribute, instance, value). Calls preserve initialization order.
Arguments:
- attribute: A registered Attribute or AttributeMap definition.
- *args: The value, or the AttributeMap instance followed by its value.
Returns:
A new options object containing the additional initialization.
Raises:
- TypeError: If arguments do not match either supported form.
- ValueError: If an AttributeMap instance is empty or contains
/.
Create durable SubFlow Conditions and read their Execute results.
Create a Condition that starts or reuses flow and awaits completion.
Arguments:
- flow: The exact target Flow instance registered by the Worker.
- input: Input accepted by the target Flow's starting Step.
- options: Optional reuse, timing, Attribute, configuration, and ID settings.
Returns:
A durable Condition accepted by
dex.Wait.
Return one stable-indexed SubFlow result during Step execute.
Arguments:
- context: The current Dex Execute context.
- index: Zero-based SubFlow order within the surrounding Wait.
Returns:
A terminal result or an AnyOf loser's running snapshot.
Raises:
- TypeError: If
contextis not managed by Dex. - ValueError: If called outside Execute or
indexis unavailable.
Return the generated Flow ID for one SubFlow Condition.
The ID remains usable after an AnyOf winner completes and may be passed to
Client.stop_flow to stop a still-running loser.
Arguments:
- context: The current Dex Execute context.
- index: Zero-based SubFlow order within the surrounding Wait.
Returns:
The stable server-generated SubFlow Flow ID.
Configure one durable SubFlow Condition.
The SubFlow inherits the parent's effective Flow configuration. None values
preserve normal start defaults. Dex generates the Flow ID and request ID.
Attributes:
- timeout: Optional maximum SubFlow lifetime.
- timeout_policy: Action taken when a positive timeout expires.
- timeout_handler_options: Execute policy used when timeout policy is
HANDLER. - start_delay: Optional delay before its starting Step.
- retry_policy: Optional whole-Flow retry policy.
- config_override: Fields applied over the inherited parent configuration.
- reuse_policy: Existing-execution resolution policy.
- condition_id: Stable ID required by
Wait.any_combination_of.
Return a copy with one target-Flow Attribute initialization appended.
Arguments:
- attribute: A singleton Attribute or AttributeMap owned by the SubFlow.
- *args: Its value, or a map instance followed by its value.
Returns:
A new immutable options value.
Raises:
- TypeError: If arguments do not match either supported form.
- ValueError: If an AttributeMap instance is empty or contains
/.
Control how a generated SubFlow Flow ID resolves an existing execution.
Attributes:
- ATTACH: Attach to a running execution or return its terminal result.
- RESTART_IF_PREVIOUS_EXITS_ABNORMALLY: Attach while running, return a successful result, and restart an abnormal previous execution.
- ALWAYS_RESTART: Replace any different existing execution.
Identify one numbered execution of a Step type.
Attributes:
- step_type: The registered Step type name.
- number: The positive execution number; defaults to the first execution.
Describe the durable state transition returned by Step.execute.
Prefer the module-level factory functions instead of constructing this class directly; they populate the fields required for each decision kind.
Attributes:
- kind: The transition behavior interpreted by Dex.
- movements: Destination Steps for a next decision.
- output: Flow completion value, or an internal marker when omitted.
- reason: Human-readable force-failure reason.
- empty_channels: Channels that must be empty for conditional completion.
- fallback: Movement used when conditional completion cannot proceed.
- canceling_steps: Flow-wide Step type cancellation selectors. Each entry is a class, registered instance, or StepRef; narrow it before use.
- canceling_sibling_steps: Same-source Step type cancellation selectors with
the same entry types as
canceling_steps.
Return a decision canceling all current executions of selected Step types.
Dex resolves one snapshot after this Execute succeeds. Finished, already-canceled, and absent executions are no-ops. Steps scheduled by this decision are excluded. Repeated calls take the union, and Flow-wide selection supersedes sibling selection. The Worker validates each target when it applies the returned decision. An invalid target raises FlowDefinitionError at that point.
Arguments:
- *steps: Registered instances, StepRef values, or unambiguous Step classes.
Returns:
A new decision containing the combined Flow-wide selectors.
Return a decision canceling selected same-source Step executions.
A sibling has the same Context.from_step_execution_id as the execution
returning this decision. Snapshot, no-op, and validation behavior match
with_canceling_steps. Flow-wide selection wins for the same Step type.
Arguments:
- *steps: Registered instances, StepRef values, or unambiguous Step classes.
Returns:
A new decision containing the combined sibling selectors.
Define one typed unit of durable Flow application logic.
Subclasses implement execute and may implement wait_for. Handler methods
must remain deterministic with respect to their inputs and Context state; external
side effects belong behind idempotent application integrations.
Examples:
>>> class Charge(Step[ChargeInput]): ... def execute(self, context: Context, input: ChargeInput) -> StepDecision: ... status.set(context, "charged") ... return graceful_complete(input.order_id)
Execute application logic and produce the next durable decision.
An ordinary handler returns StepDecision directly. A progress handler yields heartbeat and Stream StepOutput values, then returns StepDecision as its generator return value.
Arguments:
- context: Execution metadata and decision-local persistence operations.
- input: The decoded value passed by the incoming Step movement.
Returns:
A StepDecision, or a generator that yields StepOutput and returns the StepDecision describing movements or Flow completion.
Raises:
- Exception: Application exceptions are retried according to StepOptions and may ultimately fail or reroute the Flow.
Describe durable conditions that must be ready before execute.
Override this method only when the Step waits. Dex skips the base
implementation and invokes execute immediately. A progress handler
yields heartbeat and Stream StepOutput values, then returns Wait as its
generator return value.
Arguments:
- context: Execution metadata and decision-local persistence operations.
- input: The same decoded input later passed to
execute.
Returns:
A durable Wait condition tree, or a generator that yields StepOutput and returns that Wait.
Return the name used to register and address this Step.
Override it to decouple the protocol Step type from the Python class name.
Returns:
A non-empty name unique within the containing Flow.
Return the input type that overrides this Step's handler annotations.
Override it in a generic Step whose input annotation is a TypeVar, so each
registered instance can declare its concrete input type. Registry calls it
during registration and resolves the returned type through its
CodecRegistry for both execute and wait_for. Return type(None)
for a Step that receives no input value. Context and return annotations
remain required.
Returns:
The input type for this instance, or
Noneto infer it from the handler input annotations. The default returnsNone.
Raises:
- FlowDefinitionError: Raised by Registry, not by this method, when the CodecRegistry has no codec for the returned type.
Examples:
>>> class Relay(Step[InputT], Generic[InputT]): ... def __init__(self, step_type: str, input_type: type[InputT]) -> None: ... self._step_type = step_type ... self._input_type = input_type ... ... def get_step_type(self) -> str: ... return self._step_type ... ... def get_input_type(self) -> object | None: ... return self._input_type ... ... def execute(self, context: Context, input: InputT) -> StepDecision: ... return graceful_complete(input) >>> numbers = Relay("RelayNumber", int) >>> texts = Relay("RelayText", str)
Build the ordered Step definitions for a Flow.
A Flow may have zero or one starting Step. Other Steps are reachable only through decisions, RPC results, or failure routing.
Create a StepList whose first definition is the Flow starting Step.
Arguments:
- step: The Step receiving
start_flowinput.
Returns:
A StepList with exactly one starting Step.
Control when a Step handler result is durably acknowledged.
ASYNC gives lower latency and faster single Flows. Sustained throughput is about the same as SYNC.
Attributes:
- DEFAULT: Use FlowConfig, then the server's synchronous default.
- SYNC: Persist the result before acknowledging the Worker call.
- ASYNC: Allow acknowledgement before persistence completes.
Schedule one registered Step with typed input and optional options.
Code that reads step must narrow it before using class members, because it
may hold a class, a registered instance, or a StepRef.
Attributes:
- step: Destination class, registered instance, or StepRef in this Flow.
- input: The value decoded by the destination Step's input codec.
- options: Optional per-movement StepOptions override.
Create a typed movement to a Step.
The Worker validates the target when it applies the handler result that returns this movement. An unknown StepRef, ambiguous class, or unregistered instance raises FlowDefinitionError at that point.
Arguments:
- step: Destination class, registered instance, or StepRef. A class must be unambiguous.
- input: A value compatible with the Step's input annotation.
- options: Optional per-movement options.
Returns:
A StepMovement.
Represent one progress frame yielded by a synchronous Step handler.
Create outputs with heartbeat() or dex.stream.Stream.write().
A synchronous generator yields these frames and returns its final Wait or
StepDecision through the generator return value.
Address a registered Step by its stable Step type without an implementation.
Use this reference with go_to, StepMovement, failure routing, or cancellation. It cannot execute or appear in a StepList. The constructor never checks that the Step type exists. Registry validates execute-failure routes declared by get_step_options. Client validates timeout-handler failure routes when it maps start options. A StepRef returned from a handler, in a movement or cancellation selection, is validated only when the Worker applies that returned result. Unknown types raise FlowDefinitionError. Input is checked by the destination's codec rather than inferred from the reference.
Attributes:
- step_type: Non-empty Step type returned by the destination's get_step_type.
Examples:
>>> movement = StepMovement.of(StepRef("Charge"), 42)
Configure timeouts, retries, durability, locks, and failure routing for a Step.
Fields set to None or DEFAULT use Flow or server policy. Attribute
locks are acquired separately for wait_for and execute and must reference
definitions in the containing Flow's PersistenceSchema. Asynchronous
durability gives lower latency and faster single Flows. Sustained throughput
is about the same as synchronous durability.
Attributes:
- wait_for_method_timeout: Maximum duration of one
wait_forattempt;Noneuses two hours. - execute_method_timeout: Maximum duration of one
executeattempt;Noneuses two hours. - heartbeat_timeout: Handler progress deadline;
Noneor zero uses one minute. The server-configured explicit minimum defaults to ten seconds. - wait_for_retry: Optional retry policy for
wait_for. - execute_retry: Optional retry policy for
execute. - wait_for_failure: Behavior after all
wait_forattempts fail. - wait_for_durability: Durability used for the
wait_forresult. - execute_durability: Durability used for the
executeresult. - wait_for_lock_attributes: Attribute locks held during
wait_for. - execute_lock_attributes: Attribute locks held during
execute. - wait_for_load_attribute_maps: AttributeMaps fully loaded for
wait_for. - wait_for_load_attribute_map_instances: Exact AttributeMap instances loaded for
wait_for. - wait_for_load_channels: Channels whose pending messages load for
wait_for. - wait_for_load_channel_maps: ChannelMaps fully loaded for
wait_for. - wait_for_load_channel_map_instances: Exact ChannelMap instances loaded for
wait_for. - execute_load_attribute_maps: AttributeMaps fully loaded for
execute. - execute_load_attribute_map_instances: Exact AttributeMap instances loaded for
execute. - execute_load_channels: Channels whose pending messages load for
execute. - execute_load_channel_maps: ChannelMaps fully loaded for
execute. - execute_load_channel_map_instances: Exact ChannelMap instances loaded for
execute.
Return a copy that routes exhausted execute retries to another Step.
The fallback Step receives the failing Step's input, so Registry rejects a
fallback whose input type differs. The check is skipped for Any,
object, and unresolved TypeVar input types.
Arguments:
- step: Fallback class, registered instance, or StepRef; validated at registration.
- options: Optional options applied to the fallback movement.
Returns:
A new StepOptions object with failure routing configured.
Define a typed best-effort resumable Stream owned by one Flow type.
Attributes:
- name: Logical name unique within the Flow persistence schema.
- value_type: Python type encoded for every message.
- stream_capacity_bytes: Positive approximate budget shared by all Flow instances.
Emit one best-effort message from the current Step execution.
A synchronous generator must yield the returned StepOutput. An asynchronous
Step calls this method without await; the message enters the invocation's
ordered output queue immediately. Neither form waits for Dex Stream Store
persistence. Calls may write the same Stream any number of times. RPC and
Flow-timeout Contexts reject Stream writes.
Arguments:
- context: Current synchronous or asynchronous Step Context.
- value: Typed message to append.
Returns:
A StepOutput for a synchronous Context, or
Nonefor AsyncContext.
Raises:
- ValueError: If the Stream is unregistered or the Context is not a Step.
- ValueMappingError: If
valuecannot be encoded.
Create an invocation-managed writer that batches text chunks.
The first non-empty chunk starts a one-shot timer. Async Step writers append their current
batch after flush_interval even without another chunk. Synchronous generator writers
check the interval only when write is called and require an explicit final flush.
Both forms flush early after crossing the soft UTF-8 byte threshold. Chunks are never split
or modified.
AsyncWorker finalizes the writer before sending the invocation result or error. Its
synchronous write method can be passed directly as a text-delta callback. Neither form
waits for Dex Stream Store acknowledgement.
Arguments:
- context: Current asynchronous or synchronous Step Context.
- flush_interval: Positive maximum async buffering interval. Defaults to one second.
- max_buffered_bytes: Positive soft UTF-8 threshold. Defaults to 16 KiB.
Returns:
An async invocation-managed writer, or a cooperative synchronous writer.
Raises:
- TypeError: If this Stream does not carry
strvalues or an option has the wrong type. - ValueError: If an option is not positive, the Stream is unregistered, or the Context is not a Step invocation.
Describe one retained Stream message returned by a Client.
Attributes:
- value: Decoded application message.
- resume_token: Opaque token for the next read.
- created_time: Server-assigned UTC creation time.
- source: Informational client source or Step-generated
#stepExecutionID.
Contain one newest-first page of retained Stream messages.
Pass next_page_token unchanged to the next Client list call. An empty
token marks the final page. Stream trimming may remove messages between pages.
Attributes:
- messages: Retained messages in newest-first order.
- next_page_token: Opaque token for the next older page, or
"".
Configure a stop_flow request.
Attributes:
- type: The stop behavior; defaults to cooperative cancellation.
- reason: Optional operator-readable reason recorded by Dex.
Select how an active Flow should close.
Attributes:
- CANCEL: Request cooperative cancellation.
- TERMINATE: Force immediate termination.
- FAIL: Close the Flow as failed with the reason in
StopFlowOptions.
Create durable Timer conditions for Step waits.
Timer conditions measure durable workflow time rather than blocking a Python
worker thread. Use a stable condition_id when external code may skip the
Timer by ID.
Create a Timer that becomes ready after duration.
Arguments:
- duration: A non-negative delay measured from the wait's start.
- condition_id: Optional stable identifier unique within the Wait tree.
Returns:
A durable Timer condition accepted by
Wait.
Raises:
- ValueError: If
durationis negative or the identifier is invalid.
Examples:
>>> reminder = Timer.by_duration(timedelta(minutes=15), condition_id="remind") >>> wait = Wait.until(reminder)
Select a Timer condition within a Step execution.
Exactly one selector must be set. Prefer condition_id for definitions that
can evolve; condition_index is zero-based and follows flattened Wait order.
Attributes:
- condition_id: A stable Timer condition identifier, or
None. - condition_index: A zero-based Timer condition index, or
None.
Select a Timer by its stable condition ID.
Arguments:
- condition_id: The non-empty ID assigned by
Timer.by_duration.
Returns:
A TimerId using the ID selector.
Select a Timer by zero-based Wait-tree position.
Arguments:
- condition_index: The non-negative flattened Timer index.
Returns:
A TimerId using the index selector.
Raises:
- ValueError: If
condition_indexis negative.
Contain one validated SDK value before protocol mapping.
Attributes:
Report an application-value encoding or decoding failure.
Attributes:
- operation: The mapping operation, such as
"encode"or"decode". - detail: The incompatible type, wire kind, or malformed payload detail.
Describe the durable conditions evaluated before a Step executes.
A Step returns a Wait from wait_for. Dex persists the condition tree and
invokes execute only when the selected readiness rule succeeds.
Attributes:
- kind: The combination rule applied by Dex.
- conditions: Direct Conditions for all-of or any-of waits.
- combinations: Alternative all-of groups for any-combination waits.
Examples:
>>> wait = Wait.any_of( ... replies.for_one(), ... Timer.by_duration(timedelta(hours=1)), ... )
Create a Wait for one Condition.
The Condition does not need a condition ID.
Arguments:
- condition: The only readiness condition.
Returns:
An all-of Wait with
condition.
Create a Wait that requires every Condition.
This combinator does not require condition IDs. Every Condition in
any_combination_of() does require one.
Arguments:
- *conditions: Conditions evaluated as one all-of group.
Returns:
An all-of Wait with Conditions in argument order.
Create a Wait that continues when any Condition is ready.
Conditions do not need condition IDs. Read Channel results from the
Channel definition and inspect Timer outcomes through Context.
Among ready Conditions, Dex selects the first in canonical order: Timers, Channels, then SubFlows, preserving argument order within each kind. An unready Condition does not block a later ready Condition. To wait for strict priority, return only the higher-priority Condition.
Channel consumption is not greedy across alternatives. Dex consumes messages only from the selected Channel Condition; other ready Channel Conditions consume nothing.
Arguments:
- *conditions: Alternative readiness Conditions.
Returns:
An any-of Wait containing the alternative Conditions.
Create a Wait that accepts any complete Condition combination.
Channel consumption is not greedy across combinations. Dex consumes messages only from Channel Conditions in the selected combination; Conditions belonging only to other ready combinations consume nothing.
Arguments:
- *combinations: Alternative all-of groups created with
ConditionCombination.of().
Returns:
A Wait containing the alternative groups.
Control what Dex does when a Step's wait_for method fails.
Attributes:
- FAIL_FLOW: Fail the Flow without invoking
execute. - PROCEED: Invoke
executeand expose the failure through Context.
Identify the protocol representation carried by an encoded Value.
Attributes:
- STRING: A UTF-8 string value.
- BOOL: A Boolean value.
- INT64: A signed 64-bit integer value.
- DOUBLE: A finite IEEE-754 double value.
- BYTES: An uninterpreted byte sequence.
- JSON: A canonical JSON document stored as text.
Host synchronous Step and RPC handlers through outbound pull streams.
A Worker is one-shot. Call start on a dedicated thread because it blocks,
then call stop or close during shutdown. Workers need only an
outbound connection to the Dex Server. Handlers run concurrently in a bounded
thread pool and must synchronize shared application state.
Attributes:
- registry: The Flow Registry served by this Worker.
- blob_cache: The shared cache used to hydrate large values.
- options: The effective WorkerOptions.
Examples:
>>> worker = Worker(registry, cache) >>> thread = threading.Thread(target=worker.start, daemon=True) >>> thread.start() >>> worker.wait_pull_registered(timeout=30) >>> worker.stop()
Construct a Worker without starting its outbound pull streams.
Arguments:
- registry: A Registry created for synchronous handlers.
- blob_cache: An open cache shared for value hydration.
- options: Networking, startup, and bearer credential options;
Noneuses defaults and sends no authorization metadata.
Raises:
- ValueError: If the pull Worker ID is invalid.
Negotiate protocol, synchronize Attribute indexes, and serve.
start may be called exactly once. It first contacts FlowService using the
configured synchronization timeout, then starts outbound
pull streams and waits until stop finishes. Permanent stream rejections
raise PullRegistrationError when stop_on_permanent_stream_rejection is
enabled; otherwise streams retry.
Raises:
- PullRegistrationError: A permanent stream rejection stops this Worker.
- RuntimeError: If lifecycle state is invalid, Server protocol 4 is unsupported, concurrency exceeds matching.maxConcurrentTasksCap, or index synchronization fails.
Wait until the required execution slot kinds have registered.
Start the Worker separately before waiting, or start it concurrently.
Readiness requires one STEP_AND_RPC stream and, when RPC reservation is
configured, one RPC_ONLY stream. Successful registration latches after stop. Use
pull_registration_state for subsequent readiness probes. A permanent
rejection before readiness raises PullRegistrationError only when no
other stream is registered for that slot kind. Startup failures wake waiters.
When the first registration reports a Server per-Worker limit below
max_concurrent_tasks, every waiter raises that limit error and the
Worker never becomes ready.
Multiple callers may wait independently, including before start.
Arguments:
- timeout: Maximum seconds to wait; None waits indefinitely.
Raises:
- PullRegistrationError: A slot kind is permanently rejected before readiness.
- TimeoutError: The timeout expires.
- RuntimeError: The Worker stops before readiness, or
max_concurrent_tasksexceeds the Server'smatching.maxConcurrentTasksCap. - BaseException: The original startup failure, if start fails.
Examples:
>>> start_thread = threading.Thread(target=worker.start) >>> start_thread.start() >>> worker.wait_pull_registered(timeout=30) >>> is_ready = worker.pull_registration_state().is_ready
Return the deprecated reserved RPC decline counter, always zero.
Reserved RPC capacity uses RPC_ONLY slots. Steps are never declined for capacity. This property remains available for source compatibility and is safe to read concurrently, including after stop.
Returns:
Zero.
Examples:
>>> declines = worker.pull_reserved_rpc_declines
Return an immutable snapshot of execution slot kind registrations.
Transient failures, reconnects, and stop preserve initial readiness. A permanent rejection makes readiness false only while that slot kind has no registered stream. Re-registration clears its error. Calls are safe concurrently with start, stop, and other waiters. See wait_pull_registered for an example.
Returns:
Current state grouped by STEP_AND_RPC and, when configured, RPC_ONLY.
Drain handlers and release Worker resources.
Calls before start and repeated calls after shutdown are safe. Shutdown
drains for PullWorkerOptions.stop_drain_timeout (30 seconds by default),
then requests cancellation and waits for every handler to return. A
synchronous handler must check Context.is_cancellation_requested to
respond promptly to forced shutdown. Call stop from a thread other
than the pull Worker's start thread.
Idle streams half-close immediately. Busy streams send final output before
half-closing. Tasks received after stop begins are dropped without running
handlers. A timed-out busy stream is reset before cancellation.
Half-closed streams keep reading server frames until EOF or the remaining drain
deadline before the channel closes. When the deadline expires, every stream
that is still open is reset, even one blocked on a send or without Server EOF,
so stop returns once handlers return. The Server may count a task it sent in
that window as an attempt that may have run.
One exception applies to a stream whose final output was queued or written
but not yet confirmed by Server EOF or by the next assignment. Such a stream
is not reset at the drain deadline, even if its handler finished or it
already half-closed. It gets up to 5 more seconds to send the output,
half-close, and receive Server EOF, and is reset after that. Stop can
therefore return up to 5 seconds after the drain deadline.
Raises:
- RuntimeError: If called on the pull Worker's
startthread.
Stop the Worker and permanently mark it closed.
Uses the same shutdown contract as stop: idle streams half-close, busy
streams send final output before half-closing, and later tasks are dropped.
A handler exceeding the drain timeout has its stream reset.
Pull streams finish their bounded EOF wait before the channel closes; a
stream still open at the deadline is reset.
close is idempotent; a closed Worker cannot be started again.
Expose both FlowService and nested Worker handler failure details.
Attributes:
- code: The outer FlowService gRPC status.
- sub_status: Usually
WORKER_API_ERROR. - detail: The outer service detail.
- operation: The Client operation that invoked the Worker.
- flow_id: The targeted Flow ID, if available.
- worker_code: The nested Worker gRPC status, or
Nonewhen unavailable. - worker_error_type: The Worker-reported application error type.
- worker_error_detail: The Worker-reported human-readable detail.
Create an error from outer and nested Worker failure metadata.
Arguments:
- code: The outer FlowService gRPC status.
- sub_status: The Dex-specific classification.
- detail: The outer service detail.
- operation: The Client operation name.
- flow_id: The targeted Flow ID, if any.
- worker_code: The nested Worker gRPC status, if available.
- worker_error_type: The nested application error type.
- worker_error_detail: The nested human-readable detail.
Configure Worker networking, pull polling, and startup synchronization.
attribute_index_sync_timeout is the RPC deadline used before the Worker
opens its outbound task streams. It defaults to two minutes and must be
positive.
Attributes:
- server_address: The Dex FlowService target used for startup synchronization
and blob hydration. Defaults to
"localhost:8801". - attribute_index_sync_timeout: Maximum duration for startup Attribute index synchronization. Defaults to two minutes and must be positive.
- pull: Outbound polling options. Defaults to
PullWorkerOptions().Noneis invalid because push Workers are no longer supported. Workers connect toserver_addresswithout an inbound listener. - credential: Supplies the current bearer token for FlowService calls and
each pull stream open.
Nonesends no authorization metadata. Token errors fail calls before Dex is contacted. Protect the plaintext network path. A rotated token is used on the next call or reconnect. AsyncWorker accepts awaitable tokens and bounds pull stream token lookup to 30 seconds. Synchronous token lookup must return promptly.
Configure outbound task polling for a Worker or AsyncWorker.
Pass this to WorkerOptions(pull=PullWorkerOptions()). Each stream runs
one task at a time.
Handlers may run concurrently and must synchronize shared state.
Attributes:
- max_concurrent_tasks: Execution slots, each owning one long-lived poll stream. Zero uses the CPU count. Negative values are invalid. Start fails if this exceeds the Server's nonzero per-Worker limit reported by matching.maxConcurrentTasksCap.
- reserved_rpc_tasks: Slots accepting only RPCs. The remaining slots accept Steps and RPCs. Steps are never declined for capacity. None reserves one slot when the resolved task limit is at least two, otherwise zero. Zero disables reservation. Values must be nonnegative and smaller than the resolved task limit. With one slot and no explicit reserve, start warns that RPCs may wait behind long-running Steps.
- idle_streams_per_queue: Deprecated; accepted for source compatibility and has no effect. The Worker opens exactly max_concurrent_tasks streams. Zero remains valid; negative values remain invalid.
- app_worker_id: Unique process identity for sticky routing. Empty generates an identity with eight random base32 characters. A supplied value must contain 1-63 ASCII letters, digits, dots, underscores, or hyphens, differ from every registered Flow type, and change after restart.
- stop_drain_timeout: Grace period for active tasks on stop. Zero uses 30 seconds. Idle streams half-close immediately. Busy streams send final output before half-closing. Tasks arriving after stop begins are dropped. On timeout a busy stream is reset, its handler is canceled, and stop waits for the handler to return. Negative values are invalid. Busy Reconnect waits this long for server EOF after the final frame. During Stop, half-closed streams use the remaining drain deadline.
- stop_on_permanent_stream_rejection: Whether the first permanent rejection leaving a slot kind without a registered stream drains the Worker and raises PullRegistrationError from start. Defaults to False, which retries indefinitely. Applies before and after initial readiness.
Raises:
- ValueError: If a numeric option is negative, the reserve reaches the task limit, or app_worker_id is invalid.
Examples:
>>> pull = PullWorkerOptions(max_concurrent_tasks=8, reserved_rpc_tasks=2) >>> worker = Worker(registry, cache, WorkerOptions(pull=pull))
Identify a permanent rejection leaving a slot kind without a registered stream.
Raised by wait_pull_registered before initial readiness, or by start
when stop_on_permanent_stream_rejection is enabled. Inspect queue
and error for the rejected slot kind and original gRPC error. Permanent codes
are INVALID_ARGUMENT, RESOURCE_EXHAUSTED, UNIMPLEMENTED, PERMISSION_DENIED,
and UNAUTHENTICATED.
Attributes:
- queue: Rejected slot kind:
STEP_AND_RPCorRPC_ONLY. The field name is retained for source compatibility. - error: Original permanent transport error, also exposed as
__cause__.
Create a slot kind registration rejection.
Arguments:
- queue: Rejected slot kind label, retained in the
queuefield. - error: Original permanent transport error.
Describe pull registration readiness independently of the Worker lifecycle.
Obtain snapshots with worker.pull_registration_state(). After every required slot kind
has registered once, transient failures, reconnects, and stop keep readiness.
A permanent error with no registered stream makes is_ready false until
re-registration. When the first registration reports a Server per-Worker
limit below max_concurrent_tasks, is_ready stays false permanently.
Attributes:
- is_ready: Whether STEP_AND_RPC and, when reserved, RPC_ONLY registered once, the first registration passed the Server per-Worker limit check, and no required slot kind is permanently rejected without a registered stream.
- queues: Immutable snapshots ordered STEP_AND_RPC then RPC_ONLY when reserved.
Examples:
>>> state = worker.pull_registration_state() >>> is_ready = state.is_ready
Describe one execution slot kind in an immutable readiness snapshot.
Attributes:
- queue:
STEP_AND_RPCorRPC_ONLY; the field name is retained. - has_registered_stream: Whether a stream is currently registered.
- last_permanent_error: Last permanent transport error, or
None. A new Registered acknowledgement clears it. Treat errors as immutable.
Configure one durable Attribute match wait.
The server derives a stable Request ID from the Attribute condition when
request_id is empty. Reuse an override only for the same logical predicate.
request_timeout bounds the entire SDK call across transparent transport
reattachments. Temporal permits 10 in-flight Updates per Workflow Execution.
internal_handler_timeout reclaims accepted waits that outlive callers and
could consume those slots. Active callers transparently start another
generation, which adds another Update to history.
Attributes:
- request_id: An optional override for the server-derived stable ID.
- request_timeout: The total SDK call budget. Zero waits indefinitely.
- internal_handler_timeout: The Temporal Update handler generation lifetime. Set it only when abandoned waits can approach Temporal's in-flight Update limit. Zero disables rollover. Prefer a value longer than normal request timeouts and reconnect gaps because every generation counts toward Temporal's 2,000-Update history limit.
Configure one durable Step completion wait.
The server derives a stable Request ID from the Step execution when
request_id is empty. Reuse an override only for the same logical wait.
request_timeout bounds the entire SDK call across transparent transport
reattachments. Temporal permits 10 in-flight Updates per Workflow Execution.
internal_handler_timeout reclaims accepted waits that outlive callers and
could consume those slots. Active callers transparently start another
generation, which adds another Update to history.
Attributes:
- request_id: An optional override for the server-derived stable ID.
- request_timeout: The total SDK call budget. Zero waits indefinitely.
- internal_handler_timeout: The Temporal Update handler generation lifetime. Set it only when abandoned waits can approach Temporal's in-flight Update limit. Zero disables rollover. Prefer a value longer than normal request timeouts and reconnect gaps because every generation counts toward Temporal's 2,000-Update history limit.
End this execution path without scheduling work or closing the Flow.
Returns:
A dead-end decision; other active paths may continue.
Request immediate successful completion of the Flow.
Arguments:
- output: Optional codec-supported Flow result. Omit it for no output.
Returns:
A force-completion decision that does not await other active Steps.
Complete only when selected Channels are empty, otherwise schedule fallback.
Arguments:
- output: The codec-supported completion result.
- fallback: Movement scheduled when any selected Channel is non-empty.
- *channels: Registered Channel or ChannelMap definitions to inspect.
Returns:
A conditional force-completion decision.
Request immediate Flow failure with an application reason.
Arguments:
- reason: Non-empty human-readable failure detail.
Returns:
A force-failure decision.
Request successful completion after already-scheduled Steps finish.
Arguments:
- output: Optional codec-supported Flow result. Omit it for no output.
Returns:
A graceful-completion decision.
Create a heartbeat frame for a synchronous Step generator.
Yield heartbeat() to clear persisted heartbeat details. Passing a value,
including None, persists that codec-supported value for the next regular
activity attempt. Local activity execution ignores heartbeat frames.
Arguments:
- value: Optional checkpoint value. Omitting it clears prior details.
Returns:
A StepOutput that must be yielded by the current synchronous handler.
Examples:
>>> def execute(context: Context, input: int) -> Generator[StepOutput, None, StepDecision]: ... yield heartbeat({"completed": input}) ... return graceful_complete()
Create a decision that schedules one next Step.
The Worker validates the target when it applies the returned decision. An unknown StepRef, ambiguous class, or unregistered instance raises FlowDefinitionError at that point, not when this function is called.
Arguments:
- step: Destination class, registered instance, or StepRef. A class must be unambiguous.
- input: A value compatible with the destination input annotation.
Returns:
A next decision containing one StepMovement.
Create a decision that schedules several next Steps.
The Worker validates each movement target when it applies the returned decision,
as described for go_to.
Arguments:
- *movements: Typed destination movements applied in argument order.
Returns:
A next decision with all movements.
Open or create a native BlobCache.
Arguments:
- config: Valid directory, byte capacity, and admission-policy settings.
Returns:
An open cache owned by the caller; call
BlobCache.close()at shutdown.
Raises:
- ValueError: If a configuration value is invalid.
- OSError: If the directory cannot be created, locked, or opened.
Examples:
>>> cache = open_blob_cache(BlobCacheConfig(".dex-cache", 64 * 1024**2)) >>> try: ... cache.put("blob-1", b"payload") ... finally: ... cache.close()
Mark a Flow method as a typed RPC handler.
Use @rpc with defaults or call @rpc(...) with configuration. A handler
receives Context plus zero or one typed input and returns RPCResult[T] or
None. Registry construction validates annotations and Attribute locks.
Arguments:
- handler: The decorated bound-method function when used as
@rpc. - name: Optional protocol RPC name; defaults to the method name.
- timeout: Optional non-negative handler timeout.
- lock_attributes: Attribute locks held for the entire handler invocation.
- is_transactional: Request transactional reads and writes even without locks.
- load_attribute_maps: AttributeMaps whose current instances are loaded.
- load_attribute_map_instances: Exact instances created by
AttributeMap.load. - load_channels: Channels whose pending messages are loaded.
- load_channel_maps: ChannelMaps whose current instance messages are loaded.
- load_channel_map_instances: Exact instances created by
ChannelMap.load_messages.
Returns:
The original handler, or a decorator that returns it after attaching options.
Raises:
- ValueError: If
nameis empty ortimeoutis negative.
Examples:
>>> class Orders(Flow[None]): ... @rpc(timeout=timedelta(seconds=10), lock_attributes=(status.lock(),)) ... def cancel(self, context: Context, reason: str) -> RPCResult[bool]: ... status.set(context, "canceled") ... return RPCResult(True)
Creates an error that sets the delay before Dex retries a worker method.
Raise the returned error from a Step method after an external dependency reports that it is not ready. For example::
raise retry_after(5, RuntimeError("service is not ready"))
Arguments:
- after_seconds (int): Positive delay in seconds before the next retry.
- cause (BaseException): Failure that Dex records as the worker error.
Returns:
RetryAfterError: Error to raise from the worker method.
Raises:
- ValueError: If after_seconds is not positive or cause is None.