SyncRuntime#

Understand request ownership, phases, and runtime limits. See the guide.

class zett_agent.sync_runtime.SyncRuntime[source]#

Own one background event loop for all blocking calls in a scope.

Reuse this scope when calling a provider, Agent, or service repeatedly. Calls preserve context variables and propagate original exceptions. Different calling threads may share a runtime. Closing cancels streams, awaits their cleanup, and shuts down the loop and its worker pool. Objects passed into this runtime remain caller-owned: close their SDK/database resources first.

Construction starts a dedicated thread immediately. A runtime is not a process-wide singleton: multiple views may share one runtime, or independent runtimes may own separate threads. Repeated calls reuse the same loop.

Examples

Call any asynchronous function without writing an async entry point:

with SyncRuntime() as runtime:
    reply = runtime.call(agent.run, "Hello")
    with runtime.stream(agent.stream, "Continue") as events:
        for event in events:
            print(event.type)

Warning

Do not move already-used asynchronous SDK clients between event loops. Construct and use them within the same runtime. A blocking call from the runtime’s own loop is rejected instead of deadlocking.

call(function: ~collections.abc.Callable[[~_P], ~collections.abc.Awaitable[~zett_agent.sync_runtime._T]], /, *args: ~typing.~_P, **kwargs: ~typing.~_P) → _T[source]#
call(function: ~collections.abc.Callable[[~_P], ~zett_agent.sync_runtime._T], /, *args: ~typing.~_P, **kwargs: ~typing.~_P) → _T

Invoke a callable on the owned loop and wait for its final value.

Accept a callable and its arguments, not an already-created coroutine. If the callable returns an awaitable, await it before returning. Use stream() for async iterators and wrap() for a complete service object. KeyboardInterrupt requests cancellation of the submitted operation.

Ordinary callables execute directly on the loop thread, not in a worker. Do not submit long-running blocking I/O here; use an async wrapper with asyncio.to_thread when that operation must run outside the loop.

Parameters:
  • function (Callable[[~_P], Any])

  • args (~_P)

  • kwargs (~_P)

Return type:

Any

stream(function, /, *args, **kwargs)[source]#

Create a lazy, closeable iterator with one owner task for its lifetime.

Each next() requests exactly one item; this bridge does not prefetch. Provider SDKs and transports may maintain their own buffers. Use a with block when breaking early. Closing cancels an in-flight next() and waits for the producer’s finally blocks.

Parameters:
  • function (Callable[[~_P], AsyncIterator[_T]])

  • args (~_P)

  • kwargs (~_P)

Return type:

SyncStream[_T]

wrap(value)[source]#

Expose an object’s async methods as blocking methods on this runtime.

Parameters:

value (_T)

Return type:

SyncObject[_T]

context(value)[source]#

Return a synchronous with adapter; enter and exit share one async task.

The value yielded by the source remains unchanged. Its asynchronous methods still need runtime.call; they are not automatically wrapped.

Parameters:

value (AbstractAsyncContextManager[_T])

Return type:

SyncContext[_T]

close()[source]#

Cancel owned streams and join the loop thread; repeated calls are safe.

Return type:

None