Launcher Path vs Server Path in Switchyard: Architecture and Responsibilities Explained
In Switchyard, the launcher path is the Python orchestration layer that prepares environment variables and starts a native server, while the server path is the compiled Rust service that implements the HTTP API and executes LLM routing algorithms.
NVIDIA's Switchyard project separates client-side orchestration from high-performance request routing through a deliberate dual-path architecture. Understanding this distinction helps developers extend launchers for new AI clients or modify routing behavior without crossing language boundaries unnecessarily.
What Is the Launcher Path in Switchyard?
The launcher path handles Python-level CLI logic, environment preparation, and native server invocation. It lives entirely in the Python package and serves as the entry point for end users running commands like switchyard launch claude.
Primary Responsibilities
The launcher path performs five core functions:
- Parses CLI arguments via
switchyard/cli/launch_command.py, the central dispatch module - Locates LLM binaries such as Claude Code or Codex CLI on the host system
- Spins up a
NativeServerinstance that wraps the Rust service - Patches environment variables to redirect client traffic through the Switchyard proxy
- Renders the interactive UI or manages the subprocess lifecycle
Key Implementation Files
The launcher path centers on three main modules in switchyard/cli/launchers/:
| File | Purpose |
|---|---|
launch_command.py |
CLI entry point that routes subcommands to specific launcher implementations |
native_server.py |
Python façade (NativeServer class) that instantiates and controls the Rust server |
claude_code_launcher.py |
Concrete launcher for Claude Code; implements ClaudeCodeLauncher |
Additional launchers like codex_cli_launcher.py follow the same pattern, enabling reuse of the underlying native server across multiple AI clients.
Launcher Flow Example
When a user runs switchyard launch claude, the execution flow proceeds through launcher path components:
# Simplified excerpt showing launcher path orchestration
# From switchyard/cli/launchers/claude_code_launcher.py
from switchyard.cli.launchers.native_server import NativeServer
from pathlib import Path
def launch_claude_config(config, model, claude_args=None):
# 1. Start the native server (crosses into server path)
server = NativeServer(config_path=config, port=0)
server.start()
# 2. Prepare environment variables for proxy routing
env = os.environ.copy()
env["ANTHROPIC_BASE_URL"] = server.base_url
# 3. Launch the actual Claude Code binary
return subprocess.run(["claude", *claude_args], env=env).returncode
The launcher never processes LLM requests directly—it delegates all routing to the native server it started.
What Is the Server Path in Switchyard?
The server path implements the actual LLM routing logic in Rust, exposed to Python via PyO3 bindings. This path handles HTTP request serving, provider-neutral client abstractions, and performance-critical routing algorithms.
Primary Responsibilities
The server path operates as a compiled service with four key roles:
- Binds to a TCP port and runs an Axum-based HTTP service
- Exposes OpenAI-compatible endpoints including
/v1/chat/completionsand/v1/models - Executes routing algorithms from the
libsycrate to select providers and manage load - Reports health and statistics to the launcher via the
/v1/statsendpoint
Key Implementation Files
| File | Purpose |
|---|---|
switchyard_rust/server.py |
PyO3 bindings that expose Server to Python; entry point from launcher path |
crates/libsy-llm-client/src/lib.rs |
Provider-neutral LLM client for forwarding requests to upstream services |
crates/libsy/src/algorithms.rs |
Core routing algorithms (least-loaded, weighted, etc.) |
Server Initialization Pattern
The Rust Server struct loads a TOML deployment configuration and automatically selects a port:
// From switchyard_rust::server (exposed via PyO3)
// Equivalent Python usage in native_server.py
use switchyard_rust::server::Server;
use std::path::PathBuf;
#[pyfunction]
fn create_server(toml_path: String, port: u16) -> PyResult<ServerWrapper> {
// port=0 requests any available ephemeral port from the OS
let server = Server::new(PathBuf::from(toml_path), port)?;
Ok(ServerWrapper { inner: server })
}
Once started, the server runs independently on a Tokio runtime, handling concurrent requests without Python's Global Interpreter Lock (GIL) constraints.
Critical Distinctions Between Launcher Path and Server Path
| Dimension | Launcher Path | Server Path |
|---|---|---|
| Language | Pure Python | Rust with PyO3 Python bindings |
| Location | switchyard/cli/launchers/ |
switchyard_rust/, crates/libsy*/ |
| Primary crate/module | N/A (Python package) | libsy, libsy-llm-client |
| Runtime model | Synchronous, process-spawning | Async Tokio, multi-threaded |
| Key classes | ClaudeCodeLauncher, NativeServer |
switchyard_rust::server::Server |
| HTTP serving | None (proxies to server) | Axum-based, full HTTP API |
| GIL dependency | Holds GIL during execution | Releases GIL; runs native threads |
Why Switchyard Splits These Responsibilities
The architecture separates launcher and server paths for three engineering reasons:
-
Performance isolation — Routing logic benefits from Rust's zero-cost abstractions and Tokio's async runtime, free from Python's GIL contention
-
Platform flexibility — The Python launcher adapts easily to different operating systems and client binaries, while the compiled Rust server remains consistent
-
Launcher reusability — Multiple CLI clients (Claude, Codex, OpenClaw) share the same
NativeServerand underlying Rust service, reducing code duplication
Practical Interaction: Starting a Server from Python
The launcher path instantiates the server path through a thin boundary layer:
# Complete example: launching Claude Code with Switchyard routing
from pathlib import Path
from switchyard.cli.launchers.claude_code_launcher import launch_claude_config
# TOML deployment defines routes, authentication, and upstream providers
config_path = Path("switchyard/cli/defaults/claude_switchyard.toml")
# Launcher path handles CLI, environment, and subprocess management
exit_code = launch_claude_config(
config=config_path,
model="anthropic.claude-v2",
claude_args=["--verbose"]
)
print(f"Claude Code exited with code {exit_code}")
Behind this call, launch_claude_config delegates to NativeServer, which crosses into the server path via switchyard_rust/server.py to create the actual HTTP service.
Summary
- Launcher path (
switchyard/cli/launchers/) provides Python-based CLI logic, environment setup, and native server orchestration - Server path (
switchyard_rust/,crates/libsy/) delivers compiled Rust HTTP service with LLM routing algorithms - The split eliminates Python GIL constraints from request handling while keeping client integration flexible
NativeServerinswitchyard/cli/launchers/native_server.pyserves as the bridge, using PyO3 to instantiateswitchyard_rust::server::Server- Multiple launchers can reuse the same Rust server implementation, as demonstrated by
claude_code_launcher.pyandcodex_cli_launcher.py
Frequently Asked Questions
How does the launcher communicate with the running server?
The launcher retrieves base_url and port from the NativeServer instance after construction. It passes these as environment variables to client subprocesses. The server additionally exposes a /v1/stats endpoint that the launcher can poll for health and routing information.
Can the server run without the launcher?
Yes. The Rust Server can be instantiated directly from Rust code or through the PyO3 bindings in any Python script. The launcher path adds convenience for CLI integration but is not strictly required for server operation.
Where are routing algorithms actually implemented?
Routing algorithms live in the server path at crates/libsy/src/algorithms.rs. These Rust implementations handle provider selection, load balancing, and failover logic. The launcher path only specifies which algorithm to use via the TOML deployment configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →