Skip to content

Embedding and Resource Ownership

Use query for a procedure body, bind for a complete file, and interactive for incremental fragments. Low-level compilation is appropriate when the host owns source loading, execution, and resource cleanup.

A Complete Deterministic Host

import asyncio

from kedi.lang import compile_program, parse_program

source = """
@total(values: list[int]) -> int:
    = `sum(values)`

= `total(values)`
"""

runtime = compile_program(
    parse_program(source, source_path="<totals>"),
    runtime_globals={"values": [2, 3, 5]},
)
try:
    assert runtime.run_main() == 10
finally:
    asyncio.run(runtime.aclose())

No model is called. In an async host, use await runtime.aclose() in its existing event loop rather than nesting asyncio.run. Close caller-owned adapters and external clients according to their integration's contract.

Async Hosts With Reusable Clients

An async HTTP client can keep sockets bound to the event loop where it was used. Do not reuse it across independent asyncio.run() calls. In an async server or notebook host, keep model requests on the host's existing loop while the synchronous Kedi program runs in a worker:

import asyncio

from kedi.agent_adapter.execution import use_async_owner_loop
from kedi.lang import compile_program, parse_program

async def run_program(source, adapter):
    with use_async_owner_loop():
        runtime = compile_program(parse_program(source), adapter=adapter)
        try:
            return await asyncio.to_thread(runtime.run_main)
        finally:
            await runtime.aclose()

This applies to both Pydantic and LangChain adapters. The context propagates through Kedi's workers, preserves deadlines and cancellation, and does not take ownership of the loop or client. Create and close caller-owned clients on that same host loop. Calling synchronous Kedi code directly on the selected loop is rejected instead of blocking the loop on itself. Outside this explicit scope, the existing synchronous execution behavior is unchanged.

Which Configuration Surface?

Entry point Owns Does not provide
configure, context Default/temporary backend, profile and execution options A running incremental session
query, bind Callable-local overrides and optional response memoization Executor or subagent-limit constructor parameters
interactive Incremental runtime; executor, engine, cwd, subagent limits Parallel or re-entrant fragment execution
compile_program Parsed program and low-level runtime construction High-level decorator configuration merging
session Shared conversation/artifact lifetime across separate calls Persistent Kedi variables or declaration frames

Do not pass an executor or subagent limit through configure(**adapter_kwargs) expecting it to configure the runtime. Those extra kwargs construct the adapter. See Public Parameters for accepted names.

Bound Agent Surfaces

AgentSurface binds a prepared agent, profile, tool specifications, and optional conversation/artifact manager for a complete native run. Its constructor takes agent, profile, tool_specs, conversation_state, optional artifact_manager, and close_conversation_state=False.

Keep run_scope() open for the complete run or stream consumption. A surface rejects overlapping runs, use after close, and close during an active run. close()/aclose() are idempotent and close the conversation only when explicitly requested. The caller still owns the adapter and runtime. This is an advanced integration surface, not a replacement for the ordinary decorators.

Error Ownership

A context manager restores configuration, not external side effects. A failed fragment may leave earlier writes committed. Catch KediExecutionError from kedi.errors to inspect Kedi frames and the original exception; never interpret an execution failure as a valid empty result. Interactive persistence failures use SessionPersistenceError, with SessionDumpError and SessionLoadError identifying the operation.