Release migration#
Overview#
Required changes when upgrading from v0.3.0-beta to v0.4.0-beta. Apply the
entries relevant to your application’s SDK usage, configuration, clients, and
services.
Changes since v0.3.0-beta#
If upgrading from an older release, first apply Migration notes through v0.3.0-beta, then apply the changes below. Refer to Migration history for completed migration guides.
Stop running model services with the old checkout’s commands and configuration before updating the checkout. Start the updated services before restarting workers; launchers can reuse a running service whose identity matches even when its application code predates the update.
Simple VLM example deprecation#
The Simple VLM example sample is deprecated. Existing demo and launch commands remain available. Use Conversation example as the starting point for new voice-and-vision applications. Its routed conversation and visual-question behavior differs from Simple VLM; migrating application code requires reviewing that composition rather than only renaming the launcher.
vLLM 0.31 security update#
The four vLLM service environments pin vllm==0.31.0. Their standalone YAMLs
and the shared Docker default now use nvcr.io/nvidia/vllm:26.10.pre1-py3 in
place of nvcr.io/nvidia/vllm:26.09-py3. The NGC preview supports Linux
x86_64 and aarch64 with CUDA 13.4 and NVIDIA vLLM 0.31.0+88641915. Qualify
this preview on the target GPU and model workload before deployment.
Refresh pip environments with
uv --config-file uv.toml sync --project <service-directory>.
Per-request mm_processor_kwargs and media_io_kwargs overrides are rejected
by default. Move trusted processor settings into server configuration; do not
enable --trust-request-mm-kwargs for untrusted clients. Custom image overrides
must include the fixes for CVE-2026-114050 and CVE-2026-114055 independently of
the host Python environment. Refer to AI inference servers for runtime
selection and container replacement.
Interaction messages move to xr-ai-interaction#
Code that imports UserQuery, VoiceOutput, VoiceParticipantJoined,
VoiceParticipantLeft, or VoiceInterrupted from xr_ai_voice must import
them from the new xr-ai-interaction package under their new names, and add
xr-ai-interaction to its dependencies:
Before ( |
After ( |
|---|---|
|
|
|
|
|
|
|
|
|
|
Apart from the ResponseOutput fields described under Voice output declares
reply or update, fields, validation rules, and the voice.output and
voice.transcript topics are unchanged; VOICE_OUTPUT_TOPIC, VoiceTranscript, and VoiceAgent stay in
xr_ai_voice.
Two validation error messages now say response output instead of voice output: non-final response output needs a response_id and complete response output must contain text. Update tests or callers that match the previous
wording.
# Before
from xr_ai_voice import VOICE_OUTPUT_TOPIC, UserQuery, VoiceOutput
# After
from xr_ai_interaction import ResponseOutput, UserQuery
from xr_ai_voice import VOICE_OUTPUT_TOPIC
Replace xr-ai-routing[voice] with xr-ai-routing[runtime]. Remove the
[voice] extra from xr-ai-conversation, which now depends on
xr-ai-interaction directly.
Coding-agent setup skill rename#
The development preparation skill is now named setup. Replace installed
getting-started copies with setup and update saved setup prompts to use
skills/setup/SKILL.md at the selected repository ref. Use
skills/run-demo/SKILL.md to launch the browser demo. Historical release tags
retain their original files. Refer to Skills for
installation and release selection.
Dependency refresh and native tool callback exceptions#
The SDK now requires the NeMo Relay 0.10 release series. Update copied worker
dependency constraints from nemo-relay>=0.7.2,<0.8 to nemo-relay>=0.10.0,<0.11.
The dependency qualification date is now 2026-10-22T21:39:43Z, including
Relay 0.10.4. Refresh existing environments from the repository root with
uv --config-file uv.toml sync --project <project-directory>.
Native tool and model callbacks now propagate their original Python exceptions.
Catch an expected exception such as FrameUnavailable directly instead of
catching RuntimeError and parsing its message. Unexpected exceptions and
cancellation continue to propagate. Tool requests and returned SDK result types
are unchanged; Relay’s tool result envelope stays inside the adapter.
Applications that supply a Relay observability configuration must migrate it
to version = 4. Custom Relay tool execution intercepts now receive
(context, next_call); read the tool name and arguments from context.
LLM execution intercepts must accept the new codec context parameter. Refer to the
Relay 0.10.0 migration notes
for the configuration and intercept changes.
Voice output declares reply or update#
ResponseOutput replaces interrupt with kind, request_id, and urgent.
kind is required: a reply answers request_id, and an update is unprompted
and may set urgent=True to cut current speech. Updates are finite and have no
response_id. VoiceAgent now decides when each message is spoken, so
VoiceAggregationAgent and VOICE_CONTRIBUTION_TOPIC are removed. Publish
updates straight to VOICE_OUTPUT_TOPIC, and pass
combiner=llm_update_combiner(llm) to VoiceAgent to have pending updates
rewritten into one utterance. ResponseOutputs, exported from
xr_ai_interaction, builds these messages and frames streamed replies. A
producer whose queued updates became stale can drop them with
outputs.withdraw(participant_id), which publishes kind="withdraw".
# Before
from xr_ai_voice import VOICE_CONTRIBUTION_TOPIC, VoiceAggregationAgent, VoiceOutput
runtime.register("voice-aggregation", VoiceAggregationAgent(llm=llm))
await ctx.publish(VOICE_CONTRIBUTION_TOPIC, VoiceOutput(text="Timer done.", interrupt=True))
# After
from xr_ai_interaction import ResponseOutputs
from xr_ai_voice import VOICE_OUTPUT_TOPIC, VoiceAgent, llm_update_combiner
voice = VoiceAgent(..., combiner=llm_update_combiner(llm))
outputs = ResponseOutputs(ctx, VOICE_OUTPUT_TOPIC)
await outputs.update(participant_id, "Timer done.", urgent=True)
async with outputs.reply(participant_id, request_id=query_id) as reply:
await reply.send("Here is the answer.")
The aggregation tuning parameters have no replacement: coalescing follows real
playback state, and llm_update_combiner takes only prompt, max_tokens,
and timeout_s.
Scheduling changes too. A reply for a different request than the one being
spoken now cuts that reply and flushes queued audio; a playing update is never
cut by a reply and finishes first. Non-urgent updates wait while the
participant is speaking, and speech that is cut is never replayed. A streamed reply that publishes no
fragment for 15 seconds is closed and its later fragments are dropped; set
stream_idle_timeout_s on VoiceAgent for producers that pause longer.
Persistent services after startup failure#
Stacks using launch_mode="persist" now retain services that had signaled
readiness when cleanup began after a startup failure or cancellation. Owned
processes and unfinished persistent wrappers are still stopped. Persistent
pip-backend vLLM can keep loading in its separate session after its wrapper
stops. If you relied on startup failure to clear the entire model stack, run
the sample’s model command with
--stop explicitly. Refer to Customizing model servers.
Nemotron 3 embedding model#
The embedding server now defaults to nvidia/Nemotron-3-Embed-1B-BF16; the NIM
consumer profile selects nvidia/nemotron-3-embed-1b. Both the tea sample and
standalone RAG now default to 2048 dimensions. The local endpoint remains on
port 8109 with the served name embed.
Stop the agent and its persistent embedding server before updating. For the
v0.3.0-betashared model stack, run from the old checkout’s repository root:uv run --project model-server-samples/model-servers model_servers --stop
For a standalone deployment, stop the embedding process or container through the supervisor that started it. Check that its listener on port 8109 has stopped. Stopping only RAG or the embedding launcher does not stop persistent weights; the pip launcher reuses a healthy listener without comparing models.
Update the checkout and change
embedding_dim: 768toembedding_dim: 2048in copied tea and standalonerag_service.yamlfiles. While RAG is stopped, you can empty its generated cache directory (models/tea-making-rag-cachefor tea, or the configured.rag-cachefor standalone RAG) to reclaim space. Restart the embedding server before RAG. Verify theembedentry’srootin/v1/modelsidentifies Nemotron 3 Embed, not the previous model. If it still reports the previous model, stop the old listener, restart the updated embedding server, and repeat this check before starting RAG.Restart the agent. RAG’s new cache format rebuilds the
v0.3.0-betaindex automatically, including caches that already used 2048 dimensions. Refer to Embedding SDK identity and RAG cache contract for the endpoint requirements.
Independently deployed NIM endpoints must serve the new model before using the
updated NIM profile. For later model changes behind an unchanged profile, set
an explicit model revision in cache_key and restart RAG. Do not change models
while RAG is running; its document vectors must match query embeddings.
Embedding SDK identity and RAG cache contract#
EmbeddingService now requires async model_identity() -> str | None.
Custom implementations must add this method to satisfy the runtime-checkable
protocol and support RAG startup. Return a stable backing-model identifier when
known, or None only when a successful lookup finds no identity metadata. Do
not use an alias that can silently switch weights. Existing implementations
without this method no longer satisfy the protocol.
The built-in OpenAI-compatible client reads the matching model’s root from
/v1/models. HTTP failures, timeouts, invalid JSON, and malformed or ambiguous
metadata raise ModelServiceError, exported by xr_ai_models, with the original
transport or JSON error retained as the cause. Custom adapters must likewise
raise this error for lookup failures instead of returning None.
RAG permits retrieval without persistent cache reads or writes when a successful
lookup returns None. It propagates lookup failures and fails startup: fix the
endpoint, credentials, or metadata response before restarting. Cancellation
continues to propagate. cache_key does not override missing identity or a
failed lookup.
The cache format now combines the reported identity, a hash of the resolved
embedding role, manual cache_key, indexing settings, and document content.
Existing caches are rebuilt once and can be deleted afterward. cache_key
now supplements the role hash instead of replacing it. Unrelated LLM, VLM,
or TTS edits and profile formatting changes do not rebuild the index.
The reported identity is not a weight checksum. This repository’s Docker
launcher reports a resolved snapshot path; pip reports the Hugging Face
repository ID. Switching backends can rebuild the cache, while a Hub revision
change under the same pip model ID requires a new cache_key.
The post-build check raises ModelServiceError if the reported identity changes,
including appearing or disappearing, before saving or serving the vectors.
A failed lookup also aborts startup. Metadata and embeddings are separate
requests: matching before/after identities cannot detect a swap-and-restore
or mixed replicas behind one endpoint. Every replica must remain on the same
weights for the entire indexing run and serving lifetime. Health probes check
availability, not model consistency. Stop RAG before changing models, bump
cache_key if weights change under the same reported identity, and restart
against the stable deployment.
Session recording flag#
Replace --capture with --session-recording in saved launch commands for
simple_vlm_example, xr_render_demo, tea_making_sample, and
lab_instrument_monitoring. The old flag is no longer accepted. For example,
uv run simple_vlm_example --capture becomes
uv run simple_vlm_example --session-recording.
Explicit voice interaction modes#
Set an explicit mode: conversation, hybrid, wake_only, or always_on at
the top of the selected voice-gate YAML. For the default speaker ASR stack,
add this mapping to private voice-gate files retained from v0.3.0-beta:
speaker:
enabled: true
backend: required
base_url: http://127.0.0.1:8102
A retained YAML without a speaker mapping selects batch STT. The
speaker-ASR sample profiles omit the stt role, so this configuration raises
KeyError during worker construction. With the mapping above, workers wait for
speaker-stt before reporting ready. Refer to
the batch alternative if retaining batch speech.
In tea’s worker YAML, replace voice_gate_yaml: voice_gate.always-on.yaml with
voice_gate_yaml: voice_gate.yaml, then set mode: always_on in that file to
retain ungated speech. The alternate file is removed; keeping its path loads
the package’s batch-STT defaults.
Simple VLM, Conversation, lab monitoring, tea making, and XR render select
conversation. Procedure capture and guidance uses always_on for commands
and narration. For a conversation-mode sample, say
Hey agent, let's start talking after connecting and
Hey agent, let's stop talking to close the conversation.
Select wake_only for addressed turns, hybrid for addressed turns alongside
conversations, or always_on for ungated dispatch. wake_only and hybrid
require non-empty magic_phrases. Set conversation.require_wake_phrase: true
to require wake phrases during an active conversation.
In conversation mode, only accepted final transcripts during an active
conversation reach VOICE_TRANSCRIPT_TOPIC; hybrid mode also delivers accepted
closed-state one-off questions. Applications needing ungated final transcripts
must select always_on. Restart workers after configuration edits. Refer to
Conversation controls for interaction behavior.
Single LLM entry in model profiles#
XR Render and its eval commands now use llm instead of agent_llm.
For custom XR Render, NIM consumer, or shared model profiles, move any settings
from agent_llm you need into llm, then remove the old entry. The NIM smoke
check rejects the old entry.
Sample-local model-server launchers#
Replace commands targeting model-server-samples/model-servers with each agent
sample’s model-servers/ project. Before updating, run the applicable stop
command from the old checkout’s repository root:
uv run --project model-server-samples/model-servers model_servers --stop
uv run --project model-server-samples/model-servers-nim model_servers_nim --stop
The old commands discover their cleanup ports without --models. The shared
launcher rejects combining --models with --stop; the NIM launcher ignores
--models during shutdown. Stop former batch STT and NIM services before
updating; the new launcher’s stop plan omits those services.
After updating, run from agent-samples/simple-vlm-example/:
uv run --project model-servers simple_vlm_example_model_servers
uv run --project model-servers simple_vlm_example_model_servers --stop
Other samples have corresponding <sample>_model_servers commands. Keep
--gpu-profile NAME for hardware selection. Pass the same profile to --stop
when it overrides service ports. Remove --models and --allow-anonymous;
local stacks require HF_TOKEN. Move custom server settings into the
service’s own YAML under services/ or sample GPU overrides
under agent-samples/<sample>/model-servers/yaml/.
The new stop plan covers only its selected services. Stop that plan before changing its configuration or switching samples. Refer to Customizing model servers for the current workflow.
Standalone model-server defaults#
Sample model stacks now start from each service’s own YAML under services/,
so standalone launches use the values that stacks used. If you run these servers
directly and depend on the earlier defaults, set them in your YAML:
Server |
Now |
Earlier |
|---|---|---|
VLM and embedding |
|
|
Omni |
|
|
Pocket TTS |
|
|
Native NIM endpoint profiles#
Replace the managed model_servers_nim stack and
model_servers --models vlm_llm_nim with independently deployed native NIM
endpoints. Configure native model IDs with openai_compat, riva_grpc for
speech, and embedding default_extras.input_type: passage for prefix-aware
asymmetric inputs. Refer to Deploying samples with NIM endpoints for the removed
compatibility surfaces, endpoint configuration, and validation.
Native Magpie speech omits the compatibility wrapper’s 300 ms pause. Validate inference separately from Riva readiness, which checks channel connectivity.
LiveKit sample credentials#
Shipped DeviceIOHub YAML files now leave api_key and api_secret blank.
The YAML loader generates a fresh random pair when both values are missing or
blank, so samples require no credential setup. Existing private configurations
and environment overrides remain supported; partial pairs still fail startup.
Refer to Credentials for fixed-pair configuration.
Launcher failure exit statuses#
Update shell scripts and supervisors that interpret sample launcher exit statuses. An unexpected monitored process exit now fails the stack; it previously returned success. A process that exits during startup now reports its failure status; startup failures previously returned 130.
The launcher preserves positive child exit codes, maps signal termination to
128 + signal number, and maps an unexpected zero exit to 1. User cancellation
during startup returns 130. Refer to
Launcher and process model for failure diagnostics and
shutdown behavior.
Model reasoning control#
Model profiles now declare reasoning with a typed adapter.thinking setting.
A per-call enable_thinking=True or False always applies to that call. A
legacy default_extras.chat_template_kwargs.enable_thinking: true default could
previously not be turned off per call. Refer to Reasoning control.
thinking_budgetcall argument: now sent as vLLM’sthinking_token_budgetinstead ofchat_template_kwargs.thinking_budget, which the Nemotron chat templates ignored. The argument is omitted when reasoning is off. Enforcement requires server support and has been verified on Nemotron Omni with vLLM’s--reasoning-parser nemotron_v3; NIM budget support has not been qualified. Other endpoints may reject or ignore it; the SDK does not detect support or retry without the budget. Refer to Reasoning control. Rawdefault_extras.chat_template_kwargs.thinking_budgetprofile values are forwarded unchanged, not migrated to the new field.Custom
LLMServiceimplementations:enable_thinkingnow defaults toNoneinstead ofFalse. TreatNoneas the endpoint’s configured default.Profiles with the legacy key: no change is required. With a template-driven or presetless adapter, the value becomes
thinking.default, so a call can override it. It must now be a boolean, and a profile that also sets a differentthinking.defaultis rejected. Preferthinking.defaultin new profiles.
Llama Nemotron LLM removed#
The services/llama-nemotron-llm server (llama_nemotron_llm_server, port
8106) and the llama_nemotron client preset are removed; no sample or
deployment profile used them. Stop a running server with the old checkout
before updating. Its Docker container is xr-ai-vllm-llama-nemotron-llm-server.
Point custom profiles that set preset: llama_nemotron or
kind: preset:llama_nemotron at another LLM, such as nemotron3_nano.
Operator-visible runtime changes#
Recorded-video perception is enabled when video_history_enabled is omitted
from the XR Render worker YAML. Set it to false to opt out, or configure
Recorded-video prerequisites before using recorded-video tools.
Earlier releases#
Refer to Migration history for completed guides.
Refer to Swift microphone cleanup errors.
Refer to StreamKit image-capture dependencies.
Refer to DeviceIOHub rename.
Refer to Local speech service.
Refer to Removed SDK compatibility surfaces.
For current worker startup and model availability requirements, refer to Consumer model readiness.
For Clef updates between development checkouts, refer to Updating a running service. For image inputs and model cache configuration, refer to Clef-Flash local decision server.
For routing and conversation output contracts, refer to the routing contract and xr-ai-conversation.