xr-ai-runtime#
xr-ai-agent-runtime provides typed, participant-scoped publication and fan-out
for composable agents. It does not own models, planning, memory, tools, media,
application state, or agent-created tasks. Refer to API Reference for the
exact public APIs.
Agents and tools#
An Agent owns private state and exposes ordinary finite Tool or streaming
AsyncTool objects from xr-ai-tools. Direct callers execute those tools
directly; model loops expose finite tools through a ToolSet. The runtime never
wraps or redispatches tool calls.
from pydantic import BaseModel
from xr_ai_runtime import Agent, AgentRuntime
from xr_ai_tools import Tool
class Text(BaseModel):
text: str
class TextAgent(Agent):
def __init__(self) -> None:
self.uppercase = Tool(
"uppercase", "Uppercase text.", Text, Text, self._uppercase
)
super().__init__((self.uppercase,))
async def _uppercase(self, request: Text) -> Text:
return Text(text=request.text.upper())
runtime = AgentRuntime()
text = runtime.register("text", TextAgent())
async with runtime:
result = await text.uppercase.execute(Text(text="hello"))
When one model sees tools from several agents, namespace only the model-visible
catalog with ToolSet.namespaced(...). Participant identity remains a typed
tool input supplied at the application boundary.
Publication and ownership#
publish(topic, event) is asynchronous fan-out. It waits for all subscriber
deliveries and then propagates failures. A subscriber that performs lengthy
work must hand it to its own bounded queue and return promptly. The runtime
owns delivery tasks only; an agent owns creation, synchronization, cancellation,
and cleanup for its resources and background work.
Callbacks and tools may run concurrently. Agents protect shared mutable state with their own lock or queue so unrelated and streaming work is not globally serialized. Participant controls such as start, stop, and status are ordinary application tools; agent lifetime is not a model tool.
Topics use full Relay telemetry by default. High-cardinality transport topics
may declare telemetry="none" when their consumer emits one semantic operation
scope instead. Runtime publication and delivery scopes carry participant,
message, correlation, parent-message, source, and subscriber metadata. A
detached agent task whose lifetime exceeds its callback starts a fresh Relay
scope stack and preserves logical correlation in metadata rather than retaining
an ended delivery scope. Pass context=nemo_relay.fork_asyncio_context() to
asyncio.create_task(), then enter
nemo_relay.use_scope_stack(nemo_relay.create_scope_stack()) inside the
detached coroutine before opening its operation scope or calling tools.