Skip to main content

Installation

Initialization

register_worker

Register the worker with a iii instance, returns a connected worker client. Blocks up to 30 seconds for the WebSocket connection to be established. If the engine is not reachable in time, a warning is logged and the client is returned anyway; it keeps retrying in the background and flushes registrations once connected. Use add_connection_state_listener to observe the actual transition. Signature
str | None
WebSocket URL of the III engine (e.g. ws://localhost:49134). When omitted, resolves from III_URL and then DEFAULT_ENGINE_URL.
InitOptions | None
Optional configuration for worker name, timeouts, reconnection, and OTel.

Methods

register_function

Register a function with the engine. Pass a handler for local execution, or an HttpInvocationConfig for HTTP-invoked functions (Lambda, Cloudflare Workers, etc.). Handlers can be synchronous or asynchronous. Sync handlers are automatically wrapped with run_in_executor so they do not block the event loop. Each handler receives a data argument containing the call’s payload, and may optionally accept a second metadata argument carrying per-invocation metadata (e.g. def handler(data, metadata=None) or def handler(data, *, metadata=None)). Metadata is only forwarded to handlers that declare a parameter literally named metadata, so existing handlers, including ones with unrelated extra parameters, *args, or **kwargs, keep working unchanged. The handler’s return value is what the function’s observers observe. An event source’s handler returns the event it is called with. What this worker does when the function is observed is set with the hook methods (on_observer_added, on_observer_removed, on_observation_match, on_observation_deliver) on the client by id or on the returned FunctionRef. Set on_observer_added and on_observer_removed by id before registering so they run for every observer: the engine may announce an observer before a hook set on the FunctionRef is in place. request_format and response_format are auto-extracted from the handler’s type hints when omitted or passed as None (the default). To opt out of auto-extraction, pass an explicit schema (RegisterFunctionFormat or dict). This behavior is Python-specific; the Node SDK relies on explicit schemas because TypeScript types are erased at runtime. Signature
str
required
Unique string identifier for the function.
RemoteFunctionHandler | HttpInvocationConfig
required
A callable handler or HttpInvocationConfig. Callable handlers receive data (the call’s payload) as the first argument and may optionally accept metadata (per-invocation metadata) as a second argument; they may return a value.
str | None
Human-readable description of what the function does.
dict[str, Any] | None
Arbitrary metadata attached to the function.
RegisterFunctionFormat | dict[str, Any] | None
Schema describing expected input. When None (default), auto-extracted from the handler’s first-parameter type hint. Pass an explicit schema to override; there is no way to register with no schema when the handler is typed.
RegisterFunctionFormat | dict[str, Any] | None
Schema describing the handler’s return value, what observers observe. Same auto-extraction semantics as request_format.
Any
JSON Schema (or a Pydantic model) of the observers’ args; the engine validates them (INVALID_ARGS). A function without it takes no args.
bool
Only this worker may call the function; others may observe it. This worker’s own calls pass the input and the result by reference: the handler and hooks treat both as read-only.
bool
False: nobody may observe the function (NOT_OBSERVABLE), for functions whose payloads carry credentials.
bool
Calls this worker makes to its own function run in-process once the engine confirmed it (default True). Like a remote invocation, each local call to a sync handler runs on a thread of its own (one per call, not pooled), so the caller can stop waiting at its timeout. A sync handler that blocks for long should be async, or set False so its calls go through the engine (also for a handler that relies on engine-side effects).

call

Call a function and return its result. The routing behavior and return type depend on the action field:
  • No action: synchronous, waits for the function to return.
  • CallAction.Enqueue(...): async via named queue, returns a dict with messageReceiptId.
  • CallAction.Void(): fire-and-forget, returns None.
A function this worker registered with a handler, once the engine confirmed it, runs in-process (local dispatch): a sync handler on a thread of its own (the caller waits up to the timeout), an async one on the SDK loop. Its observers are still delivered to, and only matched calls reach the engine. dispatch="remote" (or action=Enqueue) always goes through the engine. Whatever a local handler raises, BaseExceptions such as KeyboardInterrupt and SystemExit included, reaches the caller as InvocationError("invocation_failed") with the original as its __cause__ (for a Void call nothing reaches the caller: the error is logged). metadata is any JSON value; the handler receives it beside the payload, and None is no metadata. A request with observer (an Observer of a function this worker hosts) calls through it: the engine runs the observer’s target with its policy (max_fires, expiry, max_in_flight), metadata sidecar and loop guard, and the call resolves to the target’s result. It takes no metadata: the target’s metadata argument is the observer’s registration metadata, and a request that names metadata raises ValueError before anything is sent. The payload goes as given: filters are applied where the worker chooses observers: iii’s delivery, or obs.matches(call) in an on_observation_deliver. A host also calls through an observer to send it something of its own, such as its initial state once it is accepted (see Observer.accepted). Signature
dict[str, Any] | CallRequest
required
A CallRequest or dict with function_id (or observer), payload, and optional action / timeout_ms / metadata / namespace / dispatch ("auto", the default, or "remote").

observe

Observes a function: when source runs, target runs. Every call of the source function that passes source["filters"] invokes target, a registered function, with the Observed payload: the top-level fields plus what source["capture"] names that applies to the outcome (input, output or error, caller, metadata). capture also decides which outcomes fire the target. The worker that hosts the source can decide otherwise in its on_observation_deliver. Returns synchronously, also before the connection is up; the observer is replayed with the same id on every reconnect. The handle holds its id until its dispose() is called or the engine disposes it; the id is free again at once after that. Errors (INVALID_ARGS, INVALID_FILTER, FORBIDDEN, NOT_OBSERVABLE, the rejection of the source’s worker, UNSUPPORTED against an engine without observe) show up as state error and make ready() raise ObserveRejection. An error answer to the handle’s observe or to its replay after a reconnect is the engine’s refusal and the handle’s final answer: the engine holds no observer for it, it is not replayed, it holds its id until its dispose(), and that dispose() sends nothing. An observer that moves to error after its answer stays held: it is replayed, and its dispose() sends an unobserve. Signature
str | ObserveSource | dict[str, Any]
required
What is observed: a function id, or function_id, namespace, args (read by the worker that hosts the function, validated against its args_format), filters, and capture.
Target
required
The function to invoke: its id, or {"function_id": ..., "namespace": ...}. Register it first; until then the observer stays pending.
ObserverOptions | dict[str, Any] | None
id, max_fires, expires_in_ms, max_in_flight, metadata (the activation’s metadata sidecar).

trigger

Deprecated since 0.26.0. Use call.
Call a function: call under its 0.25 name. Signature

Parameters

dict[str, Any] | CallRequest
required

add_connection_state_listener

Subscribe to connection-state transitions. The handler is fired immediately with the current state (on the caller’s thread), then once per transition. Transitions fire on the SDK’s background event-loop thread: keep handlers fast and do not call sync SDK methods from them (they would raise RuntimeError). Treat calls as state notifications, not edges. A state may rarely be observed twice around subscription. Registering the same handler twice fires it twice. Returns an idempotent unsubscribe function that removes only its own registration. Signature
ConnectionStateCallback
required

call_async

Call a function and return its result (on any event loop). The routing behavior and return type depend on the action field:
  • No action: synchronous, waits for the function to return.
  • CallAction.Enqueue(...): async via named queue, returns a dict with messageReceiptId.
  • CallAction.Void(): fire-and-forget, returns None.
A function this worker registered with a handler, once the engine confirmed it, runs in-process on the calling loop (a sync handler on a thread of its own); dispatch="remote" (or action=Enqueue) always goes through the engine. A request with observer calls through it, as for call. Signature
dict[str, Any] | CallRequest
required
A CallRequest or dict with function_id (or observer), payload, and optional action / timeout_ms / metadata / namespace / dispatch ("auto" or "remote").

connect_async

Connect to the III Engine via WebSocket. Initializes OpenTelemetry (if configured), attaches the event loop, and establishes the WebSocket connection. This is called automatically during construction; use it only if you need to reconnect manually from an async context. Signature

current_invocation

The invocation the calling code runs in (in every handler, remote or local), or None outside any handler. Signature

Example


get_address

Return the engine address this worker resolved to. The explicit register_worker argument, else III_URL, else DEFAULT_ENGINE_URL. Mirrors the Rust SDK’s address() and the Node SDK’s getAddress(). Signature

get_connection_state

Return the current WebSocket connection state. Signature

Example


is_observed

Whether a call of a function this worker hosts would reach anyone, so a source can skip building an event nobody wants. Without call: whether the function has any active observer (one load). With call (a Call or a partial one, {"input": ...}) on a function matched by keys (on_observation_match): whether an observer is filed under one of the call’s keys. On any other function the call is ignored. Signature
str
required
CallLike | None

observers

The active observers of a function this worker hosts, as it sees them. Signature
str
required

on_observation_deliver

Delivers each call of one function itself: hook(call, observers) receives the Call and the observers that matched it (on_observation_match, or all of them), as a snapshot taken when the call started, and what it returns (or raises) is what the caller gets back. iii delivers nothing for that function: the hook calls through observers (iii.call({"observer": obs, ...})) and applies their capture and filters with obs.matches(call). Works as a decorator (@iii.on_observation_deliver("fn::id")) and returns the hook; None restores iii’s delivery. The hook runs inside the function’s invocation, so the calls it makes have that invocation as their caller. A sync hook runs on a thread of its own. A function with a deliver hook needs an engine with observe.v2. Signature
str
required
Any

on_observation_match

Sets which observers want each call of one function. By default every observer wants every call; iii’s delivery then applies their capture and filters, and an on_observation_deliver receives only the observers that matched. As a predicate, hook(obs, call) -> bool is asked once per observation: for every call, once for each active observer, before its filters; a predicate that raises counts as no match for that observer. As keys, observer= and call= key functions each return a string or strings, and an observer wants a call when they share a key: the SDK computes an observer’s keys once, when it is accepted, and files it under each, so a call costs one lookup per key whatever the number of observers. An observer whose key function raises is rejected (OBSERVE_REJECTED); a call whose key function raises matches no observer. A match hook that raises is logged once per function. Wildcards are keys too: file an observer of every table under "primary|*|insert" and give a change the keys "primary|orders|insert", "primary|orders|*", "primary|*|insert" and "primary|*|*". Works as a decorator for a predicate (@iii.on_observation_match("fn::id")) and returns the hook (a MatchKeys for the keys form); None removes it, and every observer gets every call again. A match hook set while the function has active observers is applied to them on the SDK loop, which rebuilds the index from them. Predicates and key functions are sync: they run on every call. A function with a match hook needs an engine with observe.v2: against an older one it stays unregistered, and UNSUPPORTED is logged once. Signature
str
required
Any
Any
Any

on_observer_added

Sets what runs when an observer is added, before it is accepted: for every function this worker hosts (on_observer_added(hook)), or for one (on_observer_added("fn::id", hook)). Works as a decorator, @iii.on_observer_added or @iii.on_observer_added("fn::id"), and returns the hook; None removes it. The worker’s hook runs, then the function’s; the observer is accepted (acked, then active) once both resolved. A hook that raises rejects the observer with the ObserveRejection it raised, or OBSERVE_REJECTED; one still running after 30 s rejects it with OWNER_TIMEOUT. Then the on_observer_removed of each level whose hook already resolved runs. Hooks may be sync (on a thread of their own) or async, and the hooks of one function run one at a time. Hooks are stored by function id, so they can be set before the function is registered (which makes them run for every observer) and stay in place across unregister(). A hook set while the function already has active observers runs for each of them at once (a raise rejects that observer) and logs one warning. A second after the connection is registered, and after the last hook set by id, a warning names each hook id this worker has not registered. Signature
Any
required
Any

on_observer_removed

Sets what runs when an accepted observer goes (on unobserve, on disposal, after a late rejection): for every function this worker hosts, or for one. The same forms as on_observer_added. It runs exactly once for every accepted observer, the function’s hook and then the worker’s, and never for an observer that was not accepted. An error it raises is logged; the observer is removed either way. Signature
Any
required
Any

register_trigger

Removed in 0.26.0: raises an error that points to observe. Signature

Parameters

Any
Any

register_trigger_type

Removed in 0.26.0: raises an error that points to register_function and the observer hooks. Signature

Parameters

Any
Any

shutdown

Gracefully shut down the client, releasing all resources. Cancels any pending reconnection attempts, rejects all in-flight invocations with an error, closes the WebSocket connection, and stops the background event-loop thread. After this call the instance must not be reused. Signature

Example


shutdown_async

Gracefully shut down the client, releasing all resources. Cancels any pending reconnection attempts, rejects all in-flight invocations with an error, closes the WebSocket connection, and stops the background event-loop thread. After this call the instance must not be reused. Signature

Example


trigger_async

Deprecated since 0.26.0. Use call_async.
Call a function: call_async under its 0.25 name. Signature

Parameters

dict[str, Any] | CallRequest
required

unregister_trigger_type

Removed in 0.26.0: raises an error that points to FunctionRef.unregister. Signature

Parameters

Any
Any

Types

iii

Call · CallAction · CallActionEnqueue · CallActionVoid · Caller · EnqueueResult · InitOptions · InvocationContext · MatchKeys · MiddlewareFunctionInput · Observed · Observer · ObserverHandle · ObserverOptions · ObserveSource · StreamRequest · StreamResponse · TelemetryOptions

Call

One call of a function, as its observers observe it and its on_observation_match and on_observation_deliver hooks receive it.

CallAction

Factory for creating call actions used with call().

CallActionEnqueue

Routes the call through a named queue for async processing. Requires the queue worker in worker-compose.yaml and a matching entry under that worker’s queue_configs. Without it the call rejects with enqueue_error (no queue provider).

CallActionVoid

Fire-and-forget routing. No response is returned.

Caller

Who started an invocation: the worker (worker_id / worker_name, stamped by the engine), and the function and invocation the call was made from (absent for top-level calls). parent is that handler’s own caller, one level only.

EnqueueResult

Result returned when a function is called with CallAction.Enqueue.

InitOptions

Configuration options passed to register_worker.

InvocationContext

The invocation the current code runs in, in every handler, remote or local. Returned by iii.current_invocation().

MatchKeys

on_observation_match as keys: an observer wants a call when they share a key, and the SDK keeps an index instead of asking every observer.

MiddlewareFunctionInput

Input passed to the RBAC middleware function on every function invocation through the RBAC port.

Observed

What an observer’s target receives when iii delivers: the top-level fields, plus whichever of the observer’s capture apply to the outcome. Fields not captured, or that do not apply, are absent.

Observer

An observer as the worker that hosts its function sees it (on_observer_added, observers(), a deliver hook’s observers): what was asked for. Never who observes, the observer’s metadata or its policy. The object stays the same for the observer’s whole lifetime, so a worker keeps it in its own index (route table, consumers, …).

ObserverHandle

What iii.observe returns, synchronously. The observer is replayed with the same id on every reconnect until it is disposed. An error answer to its observe or to a replay is the engine’s refusal and is final: the engine holds nothing for it, and it is not replayed. State listeners are called in order, on a callback thread of the SDK (never on the event loop that reads the engine’s frames), so they may call the sync API.

ObserverOptions

The observer itself: its id, policy and metadata (the third argument of iii.observe).

ObserveSource

The left side of iii.observe: what is observed.

StreamRequest

Incoming streaming request received by a function that serves a streaming HTTP route.

StreamResponse

Streaming response built on top of a ChannelWriter.

TelemetryOptions

Worker metadata reported to the engine (language, framework, project).

iii.channel

Channel · ChannelReader · ChannelWriter · StreamChannelRef

Channel

A streaming channel pair for worker-to-worker data transfer.

ChannelReader

WebSocket-backed reader for streaming binary data and text messages.

ChannelWriter

WebSocket-backed writer for streaming binary data and text messages.

StreamChannelRef

Reference to a streaming channel for worker-to-worker data transfer.

iii.engine

EngineFunctions · EngineSources

EngineFunctions

Engine function ids for internal operations (parity with the Node SDK).

EngineSources

Ids of the engine-hosted functions that are called once per event: observe them with iii.observe(EngineSources.FUNCTIONS_CHANGED, target) (parity with the Node SDK).

iii.errors

InvocationError · ObserveRejection · RegistrationRejectedError

InvocationError

Raised when an invocation dispatched by the SDK fails. Inspect err.code to react to a specific category (e.g. 'FORBIDDEN' for RBAC denials, 'TIMEOUT' for timeouts). Catch this class to handle every rejection. except Exception continues to work because InvocationError inherits from Exception. Attributes are read-only after construction. stacktrace is the engine-side trace when the remote handler raised; it may include internal file paths and should not be surfaced to end users. str(err) intentionally never includes the stacktrace.

ObserveRejection

Rejects an observer with a code of its own. Raise it from an on_observer_added hook, at either level (HTTP_ROUTE_CONFLICT, INVALID_CRON_EXPRESSION, …); any other exception rejects the observer with OBSERVE_REJECTED. Raised from an on_observation_deliver hook, its code is the error the caller gets. Also what ObserverHandle.ready() and Observer.accepted() raise when the observer turns error or disposed, with the engine’s or the hook’s code (INVALID_ARGS, FORBIDDEN, UNSUPPORTED, …).

RegistrationRejectedError

Raised when the engine rejects this worker’s registration. The engine pushes a registrationrejected message and closes the connection on a registration collision (e.g. another live worker already owns (namespace, worker_name)). This is fatal: the SDK does not reconnect. Inspect the attributes to identify the conflict.

iii.protocol

CallRequest · MessageType · RegisterFunctionFormat · RegisterFunctionInput · RegisterFunctionMessage · SDK_FEATURES

CallRequest

Request object for call() / call_async(). Name the function to call with function_id, or call through an observer with observer: the call runs the observer’s target, and only the worker that hosts the observed function may make it.

MessageType

Message types for iii communication.

RegisterFunctionFormat

Format definition for function parameters.

RegisterFunctionInput

Input for registering a function, matches Node.js RegisterFunctionInput.

RegisterFunctionMessage


SDK_FEATURES

iii.runtime

FunctionRef

FunctionRef

A registered function: the client’s hook methods with its id filled in, its observers, and unregister(). Each hook method works as a decorator and as a plain call, returns the hook, and removes the hook when given None. Hooks are stored by function id, so they stay in place across unregister() and a later registration of the same id. The SDK loop runs on a thread of its own, so the engine may announce an observer before a hook set here is in place: on_observer_added then runs for that observer after it became active, and a warning says so. For on_observer_added and on_observer_removed to run for every observer, set them on the client by id before register_function.

iii.state

IState · StateDeleteInput · StateDeleteResult · StateEventData · StateEventType · StateGetInput · StateListInput · StateSetInput · StateSetResult · StateUpdateInput · StateUpdateResult

IState

Abstract interface for state management operations.

StateDeleteInput

Input for deleting a state value.

StateDeleteResult

Result of a state delete operation.

StateEventData

Payload for state change events.

StateEventType

Types of state change events.

StateGetInput

Input for retrieving a state value.

StateListInput

Input for listing all values in a state scope.

StateSetInput

Input for setting a state value.

StateSetResult

Result of a state set operation.

StateUpdateInput

Input for atomically updating a state value.

StateUpdateResult

Result of a state update operation.

iii.stream

IStream · StreamDeleteInput · StreamDeleteResult · StreamGetInput · StreamListGroupsInput · StreamListInput · StreamSetInput · StreamSetResult · StreamUpdateInput · StreamUpdateResult

IStream

Abstract interface for stream operations. .. deprecated:: IStream is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams Defining a subclass emits a FutureWarning attributed to the line of the class statement (once per subclass definition, never per request). Importing this module does not warn.

StreamDeleteInput

Input for stream delete operation. .. deprecated:: StreamDeleteInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamDeleteResult

Result of stream delete operation.

StreamGetInput

Input for stream get operation. .. deprecated:: StreamGetInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamListGroupsInput

Input for stream list groups operation. .. deprecated:: StreamListGroupsInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamListInput

Input for stream list operation. .. deprecated:: StreamListInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamSetInput

Input for stream set operation. .. deprecated:: StreamSetInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamSetResult

Result of stream set operation.

StreamUpdateInput

Input for stream update operation. .. deprecated:: StreamUpdateInput is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now. Migration guide: https://iii.dev/docs/upgrading/migrate-from-streams

StreamUpdateResult

Result of stream update operation.