Skip to content

Repository files navigation

Model Switchboard

Flip between local LLM runtimes from your menu bar.
Activate one runtime, or stop everything.

Download DMG Star on GitHub Docs

Version License: MIT Platform Swift Release Changelog


Terminal demo: model-switchboardctl summary, inventory, and profile directory Model Switchboard Plus menu bar panel: ready count, CPU/RAM/GPU sparklines, runtime filter, active-model hero card with live tok/s, and a dense standby model list

Running local models on an Apple Silicon Mac often means juggling terminal windows and ad-hoc launch scripts, with no single view of what's actually running. Model Switchboard puts common local runtimes -- llama.cpp, MLX, Ollama, vLLM, SGLang, TGI, MLC-LLM, Mistral.rs, oMLX, vLLM-MLX, rVLLM MLX, LM Studio, Jan, and named command launchers -- behind one menu bar panel.

Click Activate. Every other model stops. The one you picked comes up at an OpenAI-compatible endpoint. Readiness comes from health probes, not process presence alone.


Activate

Pick a profile. Click Activate. Every other model stops. The chosen runtime comes up at an OpenAI-compatible endpoint, and the menu bar reflects the state in real time.

Probe-based readiness

A profile is marked ready when a real /v1/models probe (or your custom HTTP check) succeeds. If a managed runtime crashes overnight, the controller restarts it automatically.

35+ runtimes under one contract

Native adapters for llama.cpp, MLX, vLLM-MLX, rVLLM MLX, Ollama, vLLM, SGLang, TGI. Named launchers and external endpoints round out the rest. See RUNTIME_SUPPORT.


What's in the panel

Activate stops every other profile and brings the chosen one up. The previous process is stopped on switch, not left loaded. Profiles are marked ready after a successful /v1/models probe (or your custom HTTP check). The UI is SwiftUI with MenuBarExtra. The app does not ship Electron, a bundled inference engine, or a resident background worker that keeps the CPU busy when idle.

Base edition panel: ready count, runtime filter, active-model hero card, and the standby model list with activate/stop and overflow actions

Base
One-tap Activate / Stop per model, hero card for the live model, additive Start, Restart, and Copy Endpoint URL in the overflow menu.

Plus edition with CPU/RAM/GPU sparklines, live tok/s on the hero card, Benchmarks, and Sync Droid

Plus
Adds live CPU / RAM / GPU sparklines, benchmark tok/s inline, Sync Droid, and the Benchmarks side panel.

In-app Benchmarks panel: latest-run summary, prefill scaling (TTFT by context), and ranked best-decode bars per model

Benchmarks panel
Latest-run summary, prefill scaling (TTFT at 1k/4k/8k context), and ranked decode bars. Export CSV; every run lands as JSON + Markdown under Controller/benchmark-results/.


Runtimes supported

Model Switchboard keys off the runtime. Any backend that exposes an OpenAI-compatible endpoint can be health-checked, switched, and tagged. Qwen, Gemma, Llama, Mistral, GLM, DeepSeek, and other local models work through whichever backend serves them.

Support level Runtimes and providers
Native command adapters llama.cpp, MLX / mlx_lm.server, rVLLM MLX, vLLM-MLX, Ollama, vLLM, SGLang, Hugging Face TGI, llama-cpp-python
Named launcher profiles DDTree MLX, TurboQuant, oMLX, Mistral.rs, MLC-LLM, LightLLM, FastChat, OpenLLM, Nexa, ExLlamaV2, Aphrodite, LMDeploy, MLX Omni Server, MLX OpenAI Server, MLX Serve, text-generation-webui, KoboldCpp, TabbyAPI, llamafile
External endpoints LM Studio, Jan, LocalAI, LiteLLM, ollmlx, Triton-backed OpenAI-compatible servers, or any local/custom OpenAI-compatible base URL

Use RUNTIME_TAGS for model-level traits such as coding, q8, long-context, vision, or agentic. The full canonical runtime table lives in Controller/RUNTIME_SUPPORT.md.


Remote gateways

Remote Linux/Unix hosts appear as named panel sections; ready-count and Stop Everything span all gateways.

Item Summary
Install/pair/update In Settings → Remote Gateways, paste a modelswitchboard-gateway:// pairing code, or enter user + host and click Install Agent on Host. Update on the dashboard, Remote Hosts, or Settings pushes the bundled agent over SSH and refreshes models. curl | bash is the no-app fallback.
SSH (recommended) Loopback-only agent; app-managed BatchMode tunnel uses your SSH config/keys/agent, never passwords, and forwards model ports for Mac-reachable copied URLs.
Tailscale/direct --tailscale binds only the WireGuard tailnet address and uses MagicDNS without a tunnel. Plain-LAN non-loopback binds require --unsafe-bind plus a ≥16-byte bearer token stored in Keychain.
Profiles/runtimes Remote default: ~/model-profiles/; Mac controller also supports --profiles-dir. link scans existing .env / .json. Templates: vLLM, llama.cpp, SGLang, TGI; use START_COMMAND otherwise.

See RemoteAgent/README.md for commands, auth/security, discovery/API routes, and troubleshooting; also see SETUP.md.


Base or Plus

Same codebase, two apps. Pick at install time. They live side by side as Model Switchboard.app and Model Switchboard Plus.app under ~/Applications/.

Both editions share the controller contract, profile discovery, runtime tags, and launcher support. Plus adds operator UI on top: live utilization badges, benchmarks, reopen-last, and integrations.

Base Plus
Profile list with live status
Activate / Start / Stop / Restart
Refresh / Stop All
Launch At Login + attached Settings / Help
CPU / RAM / GPU utilization badges -
Benchmark All + per-profile Benchmark -
In-app Benchmarks panel + CSV export -
Reopen Last -
Sync Droid and future integration adapters -

Requirements

  • macOS 14 (Sonoma) or later
  • Apple Silicon recommended. Intel Macs run the app fine, but MLX runtimes require Apple Silicon
  • A running controller that exposes the controller contract. This repo ships a reference controller under Controller/

Download

Signed DMG (recommended). Grab the latest from Releases:

  • Model-Switchboard-<version>.dmg (Base)
  • Model-Switchboard-Plus-<version>.dmg (Plus)

Open, drag to Applications, launch.

From source.

git clone https://github.com/AdityaVG13/Model-Switchboard.git
cd Model-Switchboard
./Scripts/install.sh                   # Base
./Scripts/install.sh --variant plus    # Plus

The installer places a fresh build under ~/Applications/ and installs model-switchboardctl to ~/.local/bin. It writes bash/zsh/fish completions and registers the app with Launch Services. It also forces a Spotlight import so Raycast and Alfred pick it up. Use ./Scripts/install.sh --help for quiet mode, custom install paths, --verify, and --skip-open.

Agent-facing CLI entrypoints: model-switchboardctl capabilities, robot-docs guide, triage, and doctor --json for machine-readable contracts, paste-ready guidance, one-call health triage, and structured diagnostics. Mutating commands (start, stop, restart, switch/activate, stop-all) accept --dry-run/--plan. Add --json for a structured result envelope.


Quickstart

Model Switchboard is the control surface. It does not run models itself. You need a controller that knows how to launch and health-check models.

1. Install the reference controller:

./Controller/install-model-switchboard-controller.sh

The controller exposes its API at http://127.0.0.1:8877 under a per-user LaunchAgent. Use --root, --host, --port, --no-start, or --verify when installing a dedicated controller checkout.

2. Drop a profile manifest into the controller's model-profiles/ folder (the exact path is shown in Settings). With the reference controller in this repo, that is Controller/model-profiles/. With a dedicated controller root, it is <controller-root>/model-profiles/. A minimal llama.cpp example:

DISPLAY_NAME=Qwen 3.5 35B Local
RUNTIME=llama.cpp
MODEL_PATH=/path/to/model.gguf
PORT=8080
REQUEST_MODEL=qwen35-local
SERVER_MODEL_ID=qwen35-local

3. Open the menu bar icon. Your profile appears. Click Activate.

Every profile must resolve to a unique endpoint. Reusing the same HOST:PORT or BASE_URL across two profiles is a configuration error. The controller doctor will flag it.

Using your own runtime or launcher? Any OpenAI-compatible endpoint works. The controller has adapters and tags for MLX, Ollama, vLLM, SGLang, TGI, llama-cpp-python, rVLLM MLX, vLLM-MLX, DDTree MLX, TurboQuant, Mistral.rs, MLC-LLM, LightLLM, FastChat, OpenLLM, Nexa, ExLlamaV2, Aphrodite, LMDeploy, LiteLLM, external endpoints, and generic binaries. See runtime support.

I already downloaded the app; set up the controller for me

Paste this prompt into your favorite AI agent to wire the controller up against the runtimes you actually have installed:

I already downloaded Model Switchboard on my Mac.
Set up the reference controller for me, create working model profiles for the runtimes I actually have installed, and make the configuration portable instead of hardcoding your own assumptions.
Rules:
- This is macOS-only, and that is intentional.
- Do not hardcode Homebrew paths, repo-local build paths, or personal directories unless you first verify they exist on this machine.
- Prefer profile-driven config:
  - `MODEL_PATH` or `MODEL_FILE` with `MODEL_ROOT` for llama.cpp
  - `MODEL_DIR` or `MODEL_REPO` for MLX
  - `SERVER_BIN` when a runtime binary is not already on PATH
  - A named `RUNTIME` plus `START_COMMAND` or `SERVER_BIN` for launchers without a native adapter yet
  - JSON profiles for structured values; `.env` profiles are declarative key/value files, not shell scripts
- Use the controller contract and profile format documented in this repo's `SETUP.md`.
- Put profiles in the controller's `model-profiles` directory.
- Verify that each profile can be started, health-checked, and stopped cleanly.
- If something is missing, inspect the machine and ask me only the minimum necessary question.
End state:
- Model Switchboard opens with valid profiles visible
- `Activate` works
- health checks report ready when the endpoint responds successfully
- nothing is tied to one specific Mac beyond what is truly installed here

Troubleshooting

If something looks off, these labels tell you what the app is seeing right now:

Label Where it appears Meaning
RUNNING Profile card badge Process is currently running.
NOT RUNNING Profile card badge Process is not running.
STARTING / STOPPING / RESTARTING / ACTIVATING Profile card badge Action is in progress for that profile.
STALE Footer chip near the clock Last successful status refresh is older than ~45 seconds.
CACHED Footer chip near the clock Controller was temporarily unavailable and the app is showing last cached status.
ERROR Footer chip near the clock Latest refresh or action returned an error.
RUNNING / READY Plus Benchmarks panel Benchmark job is active / no benchmark currently running.

What's new

See CHANGELOG.md for release-by-release detail and Releases for signed DMG downloads. Current version: 1.3.0.


Integrations

  • Raycast: extension scripts under Integrations/Raycast/ call the same controller API the menu bar uses.
  • SwiftBar: drop-in plugin at Controller/swiftbar/local-models.15s.sh. It renders the same status, start, stop, and restart actions in any SwiftBar-friendly menu.
  • Factory Droid: Sync Droid (Plus only) pushes managed profiles into Droid's custom-model settings. First of several planned sync adapters.

Deep docs

Deeper material:

SETUP.md: profile formats, supported runtimes, health checks, controller API contract, build-from-source flow, release pipeline, Raycast power-user notes, troubleshooting, and known limitations. Controller/RUNTIME_SUPPORT.md: canonical runtime table, launch modes, profile templates, readiness modes. CHANGELOG.md: release-by-release changes and distribution hardening notes.

The app's Help button opens the same doc.


Contributing

PRs, issues, and profile recipes are welcome. A few ground rules that keep the project reusable:

  • Keep the app generic. Runtime-specific behavior belongs in the controller or a profile manifest.
  • The controller HTTP contract is the stability boundary. Make additive changes only.
  • External tools stay optional integrations, never required features.
  • Ship a runnable example with any new adapter.

Sync Droid is currently Factory-Droid-specific because that's the agent I run. The integration slot is generic; the adapter is not. PRs that add sync adapters for other local-model terminals or agentic tools are welcome -- for example Cursor, Windsurf, OpenAI Codex CLI, Zed, Continue, Aider, LM Studio, Ollama chat frontends, or any OpenAI-compatible consumer.

If you build one, implement the adapter in ModelSwitchboardControllerCore and register it with the native integration service. It then appears in the Plus menu automatically. Full contributor guidance lives in SETUP.md.

Before opening a PR:

swift test && ./Scripts/check-cycles && ./Scripts/build-app.sh

For maintainers, release prep is scriptable instead of hand-editing version files:

./Scripts/bump-version patch   # or minor / major / x.y.z
./Scripts/release-preflight.sh

License

MIT © 2026 AdityaVG13


Model Switchboard is a solo side project, open-sourced for free.

If it saves you time flipping between local models, a small tip helps cover the API and tooling bills that keep it moving forward.

Support on Ko-fi

About

Local Model Switchboard for Mac

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages