xr_ai_launcher#
xr-ai-launcher — process management for the xr-ai stack.
Intentionally stdlib-only so it can be added to any sample without pulling in the dependency chain of the processes it manages.
Typical usage:
from xr_ai_launcher import Parallel, Process, run_stack
_BASE = Path(__file__).resolve().parent
PROCESSES = [
Process("hub", "../../services/device-io-hub", "device_io_hub"),
Parallel([
Process("stt", "../../services/stt-server", "stt_server"),
Process("tts", "../../services/pocket-tts", "pocket_tts_server"),
]),
Process("worker", "worker", "my_agent_worker"),
]
def run() -> None:
run_stack(PROCESSES, _BASE)
Attributes#
CloudXR device profiles that use direct native transport instead of WSS. |
|
OpenXR runtime selector written by the CloudXR runtime service. |
Exceptions#
Raised when visible GPUs cannot be inventoried or safely classified. |
Classes#
Launcher-facing process ownership and credentials for model endpoints. |
|
A group of processes that start simultaneously. |
|
Declare one process in a launcher-managed stack. |
Functions#
|
True if profile names a native-transport CloudXR device profile. |
|
Parse an |
|
Return NV_DEVICE_PROFILE from the environment, or from yaml_path when unset. |
|
Read one scalar from a top-level YAML worker configuration. |
|
Ensure each named token is available in os.environ. |
|
Inject saved credentials into os.environ. |
|
Exit non-zero unless each named token is available; NEVER prompts. |
|
Non-interactively surface missing optional tokens — NEVER prompts. |
|
Select an existing config only when the complete topology is supported. |
|
Load a JSON deployment profile directly (no worker YAML indirection). |
|
Load the JSON profile selected by a YAML worker configuration. |
|
Run cmd as a subprocess and terminate it cleanly when the context exits. |
|
Start processes in declaration order, waiting for each item to signal |
Package Contents#
- xr_ai_launcher.NATIVE_DEVICE_PROFILES#
CloudXR device profiles that use direct native transport instead of WSS.
- xr_ai_launcher.XR_RUNTIME_VAR = 'XR_RUNTIME_JSON'#
OpenXR runtime selector written by the CloudXR runtime service.
- xr_ai_launcher.is_native_profile(profile: str) bool#
True if profile names a native-transport CloudXR device profile.
- xr_ai_launcher.load_cloudxr_env(path: pathlib.Path) None#
Parse an
export KEY=VALUEenv file and merge intoos.environ.
- xr_ai_launcher.read_device_profile(yaml_path) str#
Return NV_DEVICE_PROFILE from the environment, or from yaml_path when unset.
- xr_ai_launcher.read_config_scalar(
- path: pathlib.Path,
- key: str,
- default: str = '',
Read one scalar from a top-level YAML worker configuration.
- xr_ai_launcher.ensure_credentials(*names: str) None#
Ensure each named token is available in os.environ.
- Priority (highest first):
Already set in os.environ
Saved in ~/.config/xr-ai/credentials.json
Stored in ~/.cache/huggingface/token (HF_TOKEN only)
Prompted interactively and then saved to both locations
Any token entered interactively is saved so future runs are non-interactive. Pressing Enter without typing skips the token (left unset, not saved).
- xr_ai_launcher.load_credentials() None#
Inject saved credentials into os.environ.
Values already set in os.environ are left unchanged. For HF_TOKEN, also checks ~/.cache/huggingface/token (written by huggingface-cli login) as a fallback so existing HuggingFace logins are picked up automatically.
- xr_ai_launcher.require_credentials(*names: str, allow_missing: bool = False) None#
Exit non-zero unless each named token is available; NEVER prompts.
allow_missing (wired to the orchestrators’
--allow-anonymousflag) downgrades towarn_if_missingbehavior.
- xr_ai_launcher.warn_if_missing(*names: str) None#
Non-interactively surface missing optional tokens — NEVER prompts.
Loads saved / huggingface-cli login / env tokens, then prints an actionable notice per still-missing token and continues, so a missing token is visible early without blocking startup on an interactive prompt.
- exception xr_ai_launcher.GPUInventoryError#
Raised when visible GPUs cannot be inventoried or safely classified.
- xr_ai_launcher.detect_gpu_config() str#
Select an existing config only when the complete topology is supported.
This compatibility bridge remains until capability-based service planning replaces named config selection. It deliberately has no fallback.
- class xr_ai_launcher.ModelDeployment#
Launcher-facing process ownership and credentials for model endpoints.
- profile_path: pathlib.Path#
Resolved path of the selected model profile.
- services: dict[str, Literal['own', 'reuse']]#
Launcher ownership mode keyed by managed service name.
- required_credentials: tuple[str, Ellipsis]#
Environment-variable names required by configured model endpoints.
- launch_mode(service: str) Literal['own', 'reuse'] | None#
Return the configured ownership mode for service, if managed.
- xr_ai_launcher.load_deployment_profile(profile_path: pathlib.Path) ModelDeployment#
Load a JSON deployment profile directly (no worker YAML indirection).
- xr_ai_launcher.load_model_deployment(worker_config: pathlib.Path) ModelDeployment#
Load the JSON profile selected by a YAML worker configuration.
- async xr_ai_launcher.ManagedProcess(
- name: str,
- cmd: list[str],
- cwd: pathlib.Path | None = None,
- env: dict[str, str] | None = None,
Run cmd as a subprocess and terminate it cleanly when the context exits.
Output routing: when the parent has called
xr_ai_logging.setup_logging()(per-run env vars stamped), stdout and stderr are appended line-by-line to<log_dir>/<name>.logwithHH:MM:SS.mmmtimestamps. Lines emitted via a customlovr.logcallback (LOVR_LOG\t<level>\t<tag>\t<msg>) are parsed for authoritative severity;warn/errorrecords are also mirrored to the parent’s stdout so they reach the terminal. Native bytes that don’t carry the marker fall back to a regex match on LOVR’s level vocabulary (case-insensitiveerror|warn|warning|fatal|panic) before being mirrored.When the env vars are unset (standalone use), output is not lost: lines fall back to prefixed
[name] …prints on the parent’s stdout.Sends SIGTERM on exit; escalates to SIGKILL after
_STOP_TIMEOUTseconds. env, if given, replaces the child’s environment entirely; otherwise the child inherits the parent’s.
- class xr_ai_launcher.Parallel(processes: Sequence[Process])#
A group of processes that start simultaneously.
The launcher spawns every member at once and waits for all of them to signal readiness before advancing to the next item in the stack sequence. If any member exits before signaling ready the launcher shuts everything down, just as it would for a serial process.
Example:
Parallel([ Process("stt", "../../services/stt-server", "stt_server"), Process("tts", "../../services/pocket-tts", "pocket_tts_server"), ])
- class xr_ai_launcher.Process#
Declare one process in a launcher-managed stack.
- name: str#
Label used in launcher output and readiness diagnostics.
- project: str | pathlib.Path#
uv project path, relative to the sample root or absolute.
- command: str#
Entry-point command to run inside the project’s environment.
- config: str | pathlib.Path | None = None#
Optional YAML path, relative to the sample root or absolute.
The launcher passes the resolved path to the process as
--config.
- gpu: str | None = None#
Optional
CUDA_VISIBLE_DEVICESvalue, such as"0"or"0,1".
- launch_mode: str = 'own'#
Spawn and shutdown behavior.
ownstarts the process and stops it with the stack.persiststarts it but leaves it running at shutdown.reuseassumes the process is already running and neither starts nor stops it.
- port: int | None = None#
Optional service port metadata.
The launcher stores but does not inspect this value or stop persistent services by port.
- quiet_native_output: bool = False#
Route unformatted native output to DEBUG instead of the terminal.
Use this for processes that interleave native C/C++ output with Python loguru records. All output remains available in the per-process log file.
- xr_ai_launcher.run_stack( ) None#
Start processes in declaration order, waiting for each item to signal readiness before advancing to the next.
Each item is either a single
Process(started and awaited alone) or aParallelgroup (all members started at once; the launcher waits for every member before moving on).Each process receives
--ready-file <path>. When fully initialized it mustPath(ready_file).touch()to signal the launcher. Progress is printed every five seconds so model-loading or CloudXR start-up remains visible.After all processes are ready the launcher monitors them: if any exits, all others are terminated and the launcher exits. Pass
exit_after_ready=Trueto return immediately once everything is ready instead — useful for launchers whose processes are alllaunch_mode="persist"and should outlive the orchestrator (e.g.model-servers).base is the sample root — all relative paths in
Process.projectandProcess.configare resolved against it:_BASE = Path(__file__).resolve().parent PROCESSES = [ Process("hub", "../../services/device-io-hub", "device_io_hub", config="yaml/device_io_hub.yaml"), Parallel([ Process("stt", "../../services/stt-server", "stt_server"), Process("tts", "../../services/pocket-tts", "pocket_tts_server"), ]), Process("worker", "worker", "my_worker", config="yaml/my_worker.yaml"), ] def run() -> None: run_stack(PROCESSES, _BASE)