xr_ai_routing#
Public contracts and router for participant-scoped agent routing.
Classes#
Destination decision made before it speaks or starts work. |
|
One completed user request and destination-provided response context. |
|
The current user turn and complete exchanges selected for this destination. |
|
Optional completed response context and the router's focus transition. |
|
An agent that owns its state and handles admitted or fallback turns. |
|
Brief routing metadata, separate from the destination's full instructions. |
|
Read-only routing metadata captured from the current asynchronous context. |
|
Route a turn, check admission before speech, and retain explicit focus. |
Functions#
|
Return a stable snapshot of metadata for the active routed turn. |
Package Contents#
- class xr_ai_routing.Admission#
Destination decision made before it speaks or starts work.
acceptpermits work,declinereturns control for another route,clarifyends routing with a question, andreleaseclears focus and returns the same query to ordinary routing. Declining a turn does not clear existing focus or call the focused destination’srelease(). That owner receives the next turn unless it releases focus, becomes ineligible, another destination explicitly takes focus, or the participant leaves.- disposition: Literal['accept', 'decline', 'clarify', 'release']#
Whether the destination may begin work or should return control.
- preamble: str = ''#
Optional short acknowledgment emitted only after acceptance.
- text: str = ''#
Optional text for clarification or release; release text precedes rerouting.
- class xr_ai_routing.AgentExchange#
One completed user request and destination-provided response context.
- user: str#
User request in the completed exchange.
- assistant: str#
Completed response context, excluding progress acknowledgments.
- class xr_ai_routing.AgentTurn#
The current user turn and complete exchanges selected for this destination.
- participant_id: str#
Participant whose state and focus apply to this turn.
- turn_id: str#
Stable identity used to correlate streamed output and cancellation.
- query: str#
Unmodified current user request.
- history: tuple[AgentExchange, Ellipsis] = ()#
Newest whole completed exchanges that fit the router’s byte budget.
- timestamp_us: int | None = None#
Originating input time in Unix microseconds, when supplied by the caller.
- class xr_ai_routing.DestinationResult#
Optional completed response context and the router’s focus transition.
- disposition: Literal['answer', 'focus', 'release', 'clarify'] = 'answer'#
Destinations return answer, focus, or release; the router returns clarify before answer work.
A post-answer
releaseconsumes this turn and clears focus. It does not reroute the request; useAdmission("release")for that behavior. Returningfocustakes focus and calls the previous owner’srelease()if a different destination held it.
- text: str = ''#
Optional completed response context to retain in history.
This text is not delivered as output by the router. Destinations can omit it when no routing context is needed; progress acknowledgements are excluded.
- class xr_ai_routing.RoutableAgent#
An agent that owns its state and handles admitted or fallback turns.
The router calls
release()when it releases a destination during rerouting or clears its focus. On participant departure it calls an optionalparticipant_left(participant_id)method when present, otherwise it callsrelease(). Implement that optional hook when departure should clear durable participant state that route release retains; it is not required by this protocol.- routing_aid(participant_id: str) RoutingAid#
Describe this participant’s current eligibility and route scope.
- async admit(turn: AgentTurn, *, focused: bool) Admission#
Accept, decline, clarify, or release before producing spoken output.
A focused destination can decline this turn without ending its session. Use
releaseto clear focus and reroute the same query instead.
- async answer(
- turn: AgentTurn,
- *,
- started: collections.abc.Callable[[], collections.abc.Awaitable[None]],
Answer a turn after admission or as the configured default fallback.
Deliver output directly and return answer, focus, or release. After selected destinations decline through the bounded routing attempts, the front end may call an eligible configured default once without
admit(), unless that default already declined the current turn; destinations must support that standalone answer path.Destinations decline or clarify in
admit(), before output or work begins. ReturningDestinationResult("release")consumes the current request after the answer and clears focus; it does not reroute that request. Destination exceptions and task cancellation propagate to the caller. Awaitstarted()immediately before the first meaningful output or application action. It suppresses an acknowledgment that is not yet committed, waits until a committed one is delivered, supplies the first-response timing sample. Repeated calls wait for the same delivery, and calls afteranswer()returns have no effect. Output published with the turn’sturn_idas its request ID plays in order after the router’s own control text; the destination never chooses interruption. Completion and focus still use the return value, with optional response context inDestinationResult.textfor the router’s history. Destinations own output publication and stream finalization. A failure beforestarted()is reported to the participant by the router; after it, the destination owns any failure output. Cancellation must drain their output and model work before returning. A routingclarifychoice with usable scores selects the highest-scoring eligible destination through normal admission. Without eligible scores the router asks for clarification. After exhausted declines, the default is not retried if it already declined this turn, and clarification is also used when the configured default is unavailable.- Parameters:
turn – Current request and bounded completed history.
started – Response-start signal for this answer.
- async release(participant_id: str) None#
Handle route release; the router also uses it for departure fallback.
This method runs when the router releases a destination during rerouting or clears its focus. It is also the departure fallback when no
participant_left()hook is present. Declining a turn while focused does not call this method; a different destination taking focus does. A destination can clear active resources here while retaining completed participant data; use the optional departure hook for stronger cleanup.
- class xr_ai_routing.RoutingAid#
Brief routing metadata, separate from the destination’s full instructions.
- name: str#
Stable route name used for attribution and decision labels.
- description: str#
Concise description used as a route-selection hint, not an authorization rule.
- priority: int = 0#
Tie-breaking preference after request relevance.
- eligible: bool = True#
Whether this destination may be selected for this participant now.
- eligibility: str = 'available'#
Current participant-specific eligibility state.
- class xr_ai_routing.RoutingTurnContext#
Read-only routing metadata captured from the current asynchronous context.
The snapshot is stable after it is returned, including when the router later updates its private context for another completed exchange. Outside an active routed turn, the identifiers are
Noneand the history metadata tuples are empty.- exchange_id: object | None#
Identity of the current exchange, when one is active.
- history_exchange_ids: tuple[object | None, Ellipsis]#
Exchange identities aligned with the active bounded history.
- history_timestamps: tuple[int | None, Ellipsis]#
History timestamps aligned with the active history, in Unix microseconds.
- xr_ai_routing.current_turn_context() xr_ai_routing.RoutingTurnContext#
Return a stable snapshot of metadata for the active routed turn.
Agent code called within a routed turn can use this accessor to associate work with the current exchange and its bounded history. The returned snapshot does not change when routing advances to another exchange. Outside an active routed turn, it contains
Noneand empty history tuples.
- class xr_ai_routing.AgentRouter(
- destinations: collections.abc.Mapping[str, xr_ai_routing.RoutableAgent],
- decisions: xr_ai_models.DecisionService,
- *,
- output: collections.abc.Callable[[xr_ai_routing.AgentTurn, str, str], collections.abc.Awaitable[None]] | None = None,
- default_destination: str = 'conversation',
- context_budget_bytes: int = _DEFAULT_CONTEXT_BUDGET_BYTES,
- acknowledgment_llm: xr_ai_models.LLMService | None = None,
- acknowledgment_delay_secs: float | None = None,
- query_topic: xr_ai_runtime.Topic[xr_ai_interaction.UserQuery] | None = None,
- output_topic: xr_ai_runtime.Topic[xr_ai_interaction.ResponseOutput] | None = None,
- participant_joined_topic: xr_ai_runtime.Topic[xr_ai_interaction.ParticipantJoined] | None = None,
- participant_left_topic: xr_ai_runtime.Topic[xr_ai_interaction.ParticipantLeft] | None = None,
- interrupted_topic: xr_ai_runtime.Topic[xr_ai_interaction.Interrupted] | None = None,
- initialize_participant: collections.abc.Callable[[str], collections.abc.Awaitable[None]] | None = None,
- cleanup_participant: collections.abc.Callable[[str], collections.abc.Awaitable[None]] | None = None,
- set_status: collections.abc.Callable[[Literal['processing', 'idle'], str], collections.abc.Awaitable[None]] | None = None,
Route a turn, check admission before speech, and retain explicit focus.
A focused destination receives the participant’s next turn first. It can answer, release focus, or decline only this turn while retaining focus. Route choices use the typed decision service; a declined route is excluded from one bounded reroute. If selected destinations decline through those attempts, the configured eligible default gets one final answer attempt without another admission check unless it already declined this turn. An uncertain routing choice uses the highest-scoring eligible destination when usable scores are available. If routing cannot select an eligible destination, the router asks for clarification. Destinations own their model and application state.
acknowledgment_llmandacknowledgment_delay_secsopt into a brief, route-aware intention acknowledgment. Both must be supplied together. A configured acknowledgment client must default to non-reasoning generation; the per-call setting cannot override every provider’s model-level extras. Acknowledgments use a bounded output-token budget. A positive delay sets the fallback silence threshold; a confident prediction of a slower first answer can start the acknowledgment earlier. The destination still starts immediately. Itsstarted()response signal cancels any acknowledgment that has not yet been emitted. This suppresses pending output, not audio already queued or played by downstream transports. The delay and timing examples start after admission and measure when the destination signals its first meaningful response, excluding routing, admission, waiting for committed acknowledgment delivery, and audio playback.Supply
query_topicandoutput_topictogether, then registerruntime_subscriber()withxr_ai_runtime.AgentRuntime. This requires thexr-ai-routing[runtime]extra; ordinaryhandle()use never imports the runtime or interaction messages. No input or lifecycle topic is subscribed unless explicitly supplied. Runtime queries use their publication identity and originating timestamp. Destinations publish their own output; the router publishes only finite admission, clarification, acknowledgment, and failure messages for turns that fail before the destination callsstarted(). After that call, the destination owns failure output and stream finalization. Runtime background failures are logged once.- Parameters:
destinations – Registered destinations keyed by their stable route names.
decisions – Typed Clef decision service used to select routes.
output – Async callback receiving the current
AgentTurn, route name, and finite control text, including acknowledgments, admission preambles, and clarification or release messages. Required for directhandle()calls; runtime queries publish control text throughoutput_topic. Destinations publish their own answer output directly.default_destination – Registered route used for exhausted-decline fallback.
context_budget_bytes – UTF-8 byte budget for retained completed history and routing history input.
acknowledgment_llm – Optional non-reasoning client for intention acknowledgments and duration prediction.
acknowledgment_delay_secs – Finite positive delay before fallback acknowledgment; requires
acknowledgment_llm.query_topic – Explicit runtime input topic carrying
xr_ai_interaction.UserQuery.output_topic – Explicit topic carrying
xr_ai_interaction.ResponseOutputfor router control output.participant_joined_topic – Optional explicit
ParticipantJoinedtopic reopening admission.participant_left_topic – Optional explicit
ParticipantLefttopic for departure cleanup; requiresparticipant_joined_topicso a departed participant can be readmitted.interrupted_topic – Optional explicit
Interruptedtopic for participant or global interruption.initialize_participant – Optional initialization, awaited once before the participant’s first turn, whether called directly or published. An interrupted initialization runs again on the next turn.
cleanup_participant – Optional idempotent application cleanup after destination work is drained. Used also for initialized participants who never selected a route; failed cleanup can be retried.
set_status – Optional asynchronous callback receiving
processingoridle, then the participant ID, around each runtime turn. Its failures are logged and do not fail the turn. All callbacks run on the delivery event loop and must not reenter this router’s operations.
Topic payload classes must match exactly and configured topic names must be distinct. Incomplete runtime configuration raises
ValueError; a wrong payload class raisesTypeError. A missingruntimeextra raisesImportErrorwhen runtime configuration is supplied. The application must awaitstop()before stopping its runtime or closing destination resources.- runtime_subscriber() xr_ai_runtime.Agent#
Return the runtime agent that delivers configured topics to this router.
Register it once with the application’s
AgentRuntime. Requiresquery_topicandoutput_topicon the constructor.
- async handle(
- turn: xr_ai_routing.AgentTurn,
Route one turn while emitting only router-owned control text.
The supplied
turn.historyseeds this participant when the router has no completed history of its own. Later turns use the router’s completed history rather than merging a new caller-supplied history. The participant’s current turn is kept separate from that history.A turn that fails before its destination signals a response start publishes one failure message through the router’s output; once the response starts, the destination owns failure output. Exceptions still propagate to the caller for supervision, not for further user-facing recovery. Destinations own direct output delivery, cancellation, and finalization. The router suppresses interrupted control output but cannot retract already-published output.
The application drives turns and lifecycle operations. Destination and output callbacks, including their child tasks, must not recursively await this router’s operations, which would wait for their own response or cleanup to finish.
Raises
RuntimeErrorwhile departure or shutdown is pending, or after cleanup fails until the caller retries it successfully. Canceling a cleanup waiter does not abandon the owned operation; cancellation propagates after that operation completes.Configure
outputon the constructor before calling this direct entry point.- Parameters:
turn – Current request and optional initial completed history.
- Returns:
The final answer, focus, release, or clarification disposition.
- async interrupt(participant_id: str | None = None) None#
Cancel active response work while retaining completed history and focus.
If
participant_idisNone, cancel work for all participants. Awaiting this method drains the canceled response tasks. Output already delivered to a callback cannot be retracted.
- async participant_left(participant_id: str) None#
Release one participant’s state and history.
Attempts every destination used by the participant, calling its
participant_left()hook when present and otherwiserelease(). Failed releases remain tracked for a caller-triggered retry and are raised together in anExceptionGroup. Until this method completes successfully,handle()raisesRuntimeErrorfor that participant. Concurrent callers join the same owned departure. Canceling a waiter waits for that operation to finish before propagating cancellation. Retry after a cleanup failure. With runtime topics configured, departure also closes directhandle()admission until a participant-joined event. Without runtime topics, successful cleanup permits a fresh later turn.
- async stop() None#
Drain responses and release all participant state in one owned operation.
Concurrent callers join the same shutdown. Canceling a waiter does not cancel shutdown: cancellation propagates only after cleanup completes. Release failures are raised together in an
ExceptionGroupafter every participant is attempted; admission stays closed until a laterstop()retries successfully. Successful shutdown clears history-only participants too, and permits later turns to begin a fresh lifecycle.