Skip to main content

Yunshu

A fast local LLM / VLM inference engine for Apple Silicon.

One process, OpenAI- and Anthropic-compatible, running on-device via MLX. Built for low latency on a single Mac: fast first token, fast lossless decode, and prefix reuse that skips work you already paid for. The first fully tuned model is Qwen3.8-27B.

Python 3.13+ License: Apache 2.0 CI

English · 简体中文 · 繁體中文


Yunshu console overview: live request phases, tok/s, prefix-cache hits

Highlights

  • Lossless speculative decoding — DFlash2 or MTP drafting with batch-invariant kernels: greedy output with speculation on is token-identical to speculation off.
  • Prefix cache for hybrid models — exact checkpoints for attention + GatedDeltaNet models, in RAM and on SSD, surviving restarts; optional extra storage tiers.
  • The full API on the fast path — tools, JSON schema, stop, logprobs, reasoning, cancellation, across OpenAI Chat / Responses, Anthropic Messages and Ollama.
  • Typed decisions — /v1/decisions answers yes/no, choice and score questions with calibrated probabilities in one forward pass of a decision checkpoint, without generating text.
  • Native coding-agent support — Claude Code, Codex and opencode work through their own APIs, including server-side web search / fetch and MCP.
  • Lossless by default — anything that can change output is an explicit setting.
  • Local and private — no usage analytics; diagnostics never contain prompts.
  • A real console — a local web console with live request phases (queued, prefill with cache-hit segments, decode), request history, logs, models, cache, a playground, API keys with per-key quotas, settings (including a CORS editor) and host power / clock / temperature, plus a floating status island. Console guide.
  • Web search with citations — server-side web_search / web_fetch for Claude Code, Codex and the APIs, six providers (DuckDuckGo, Wikipedia, SearXNG, Tavily, Serper, Perplexity), a Tavily-compatible API and optional research enrichment. Guide.
  • Evals and stored completions — store: true keeps chat completions locally and the OpenAI Evals API grades them with local graders. Guide.
  • Host telemetry — power, GPU clock, temperatures, thermal and memory pressure, and a per-request energy estimate, unprivileged and local. Guide.
  • Multiple API keys — scopes, expiry and daily quotas per key. Guide.

Capabilities

Everything Yunshu serves, grouped. Each row links to the guide with an example and the limits; items marked partial or experimental say so. The same list is machine-readable in docs/feature_index.json.

Inference

Capability What it does
Lossless speculative decoding DFlash2, MTP and prompt-copy drafting with batch-invariant kernels; greedy output equals speculation off
Text-model speculation n-gram / suffix and Gemma 4 assistant drafting for mlx-lm models (external-draft experiments stay experimental)
Prefix cache (APC) exact hybrid checkpoints in RAM, SSD and optional storage tiers; image and audio keys
Structured output JSON schema, regex, choice and grammar enforced during decoding, including tool arguments
Lossy memory options int8 KV, compressed WARM tier and load-time quantization; off by default
Large MoE loading (partial) bits and group size derived from tensor shapes for community DeepSeek-V4-Flash packs; fit check before loading
Round driver (experimental) own speculative round driver for dense Qwen3.5-family models
Model support what is tuned, generic and validated, per family

APIs

Capability What it does
OpenAI API Chat, Completions, Responses (HTTP and WebSocket), Embeddings, Models, Files, Batches, Conversations
Anthropic API Messages, count_tokens, batches, thinking, tools, cache_control, documents and citations
Ollama API /api/chat, /api/generate and native model management (pull, copy, delete, show)
Responses extras client-executed computer tool, conversations, compaction, previous_response_id
Stored chat completions store: true, then list, retrieve, update, delete and list messages
Evals OpenAI Evals API with string-check, similarity and local model graders
Typed decisions yes/no, choice and score answers with probabilities in one forward pass, no text generation
Batches, Files and tokenizer OpenAI and Anthropic batches, Files API, tokenize / detokenize / apply-template / props
Transports SSE, WebSocket streams, Unix socket, HTTP/2, optional WebRTC
Yunshu extensions request ids, live phases, prefill progress, cancel, deadlines, x_yunshu stats, warmup, memory units
Prompt-caching APIs cache_control and cached-token accounting across the three dialects

Agents & tools

Capability What it does
Coding agents Claude Code, Codex and opencode through their own APIs; yunshu launch
Web search and fetch server-side web_search / web_fetch with citations; DuckDuckGo, Wikipedia, SearXNG, Tavily, Serper, Perplexity
Tavily-compatible API search, extract, crawl, map, research and MCP, with provider health backoff
MCP native MCP endpoint, and remote MCP servers named in a request
Client setup curl, OpenAI and Anthropic SDKs, Open WebUI

Multimodal

Capability What it does
Image, audio and video input mlx-vlm models take media in Chat, Responses and Messages; media enters the cache key
Speech-to-speech (partial) Qwen3-Omni native speech; a repeated image does not yet hit the cache
Realtime voice WebSocket and optional WebRTC, server VAD, barge-in, ephemeral client secrets, local voices
Speech to text, text to speech transcriptions, translations, speech and voices
OCR and image generation GLM-OCR text extraction; image generations, edits and variations
Embeddings, rerank, score, classify text and multimodal embeddings, cross-encoder rerank, trained classification heads

Operations & console

Capability What it does
Web console live request phases, history, logs, models, downloads, cache, playground, keys, settings, status island
Console process the console runs apart from the engine (port 8100): usable while the engine is down, recorded history and request log, outage gaps
API keys and quotas several keys with scopes, expiry, per-key usage and daily quotas
Settings and CORS editor write engine settings (restart-required ones prompt) and edit CORS from the console or API
Host telemetry power, GPU clock, temperatures, thermal and memory pressure, per-request energy estimate, Prometheus
Command line setup, doctor, models list/pull/rm/show, top, service, completions and inference commands
Login service launchd agent, restart and logs
Configuration one settings registry: environment, TOML file or --set
Security auth token, CORS credentials rule, redirect header stripping, secret redaction, SSRF guard
First run and troubleshooting install, choose a model, readiness checks, fixes
Benchmarks and accuracy where each number comes from, how to reproduce it, accuracy gates

Quickstart

For model selection, external storage, readiness checks and upgrades, follow the first-run guide.

Apple Silicon, macOS 14+, Python 3.13+ and uv.

uv tool install --python 3.13 "yunshu[vision]"      # or: brew install yuhuanstudio/tap/yunshu
yunshu doctor                                   # checks this Mac and says how to fix problems
yunshu pull mlx-community/Qwen3.5-9B-MLX-4bit
yunshu serve -m mlx-community/Qwen3.5-9B-MLX-4bit

The server listens on http://127.0.0.1:8000. Any OpenAI or Anthropic client works unchanged:

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="local")  # any key works
r = client.chat.completions.create(
    model="local",
    messages=[{"role": "user", "content": "Explain MLX in one sentence."}],
)
print(r.choices[0].message.content)
from anthropic import Anthropic

client = Anthropic(base_url="http://127.0.0.1:8000", api_key="local")
msg = client.messages.create(
    model="local", max_tokens=512,
    messages=[{"role": "user", "content": "Explain MLX in one sentence."}],
)
print(msg.content[0].text)

Models live in ~/.yunshu/models (yunshu config set models_dir PATH selects the directory for future downloads; it does not move existing weights); serve -m org/name also finds the Hugging Face cache and downloads only if needed. yunshu service install -m <model> runs the server at login.

Local console

The local web console shows what the engine is doing and manages it, in English, Traditional Chinese or Simplified Chinese. It runs as its own light process (yunshu console; it never loads MLX) that yunshu serve starts next to the engine, on port 8100. It serves the console and the docs, proxies the engine API on one origin, and records the history, so it stays usable, and keeps recording, while the engine restarts or crashes. It ships inside the yunshu package (pip, uv tool and Homebrew installs alike), so there is nothing to build:

yunshu serve -m <model>
open http://127.0.0.1:8100/console/
yunshu console --engine http://mac-studio.local:8000
  • Overview — live request phases, prefill and decode tok/s, prefix-cache hit segments inside the prefill bar, time to first token, Metal memory, host power, clock and temperature.
  • Requests and Logs — latency distribution (cold and warm), speculative acceptance, request history, cancel; a redacted live log with search and download.
  • Diagnostics — resource readouts, health checks, a Realtime probe and the support bundle.
  • Models, Downloads, Cache — load, unload, warm up and fit-check models, pull from Hugging Face with progress, and inspect or clear the RAM / WARM / SSD prefix-cache tiers.
  • Playground and API access — stream a chat with reasoning, compare, view the code; paste-ready setup for Claude Code, Codex, opencode, the SDKs and curl.
  • Keys and Settings — API keys with quotas and usage, every engine setting (restart-required ones prompt), and a CORS editor.
  • History that outlives the engine — the console process samples the engine once a second (1 s resolution for an hour, 10 s for a day, 1 min for 30 days) and keeps the request log (metadata only), so charts and request lists backfill on open, engine outages show as gaps, and nothing depends on a browser having been open. When the engine is down the console says so, keeps the docs, settings and last data, and reconnects by itself.
  • A floating status island follows you across pages, and the layout works on a phone.
Requests and latency distribution Playground streaming a reply API keys with quotas and usage Console on a phone

Full page-by-page tour with screenshots: Console guide.

Qwen3.8-27B

yunshu pull Jundot/Qwen3.8-27B-oQ4e-mtp
yunshu pull incoai/Qwen3.8-27B-DFlash2          # optional drafter, picked up automatically
yunshu serve -m Jundot/Qwen3.8-27B-oQ4e-mtp

With the drafter installed the startup log says Speculative decoding: dflash; without it the model's own MTP head drafts. yunshu doctor -m <model> reports the selected path and whether the model fits this Mac's memory.

From source

git clone https://github.com/YuhuanStudio/Yunshu.git && cd Yunshu
uv sync --extra vision
uv run yunshu serve -m <model>

To work on the console itself, run pnpm install && pnpm dev in frontend/ (see the console guide).

How it works

 OpenAI / Anthropic / Ollama clients ──► FastAPI gateway (one process)
                                           │  request validation, tool/reasoning parsing,
                                           │  server-side tools (web search / fetch / MCP)
                                           ▼
                                  engine (one MLX thread)
          ┌────────────────────────────────┴───────────────────────────────┐
   VLM batch runner (every mlx-vlm model)                 text fast path (mlx-lm models)
   shared continuous batch, per-row sampling              single-request generate_step
   speculative lane: DFlash2 / MTP / prompt-copy
   prefix cache: RAM ─► SSD ─► optional storage tiers

All GPU work runs on a single MLX thread, so requests never fight over the GPU. Each response returns as soon as its own generation ends.

Speculative decoding

For Qwen3.5-family models a single request decodes in a speculative lane:

  • DFlash2 — a separate block drafter proposes several tokens per round; used automatically when a matching drafter is installed.
  • MTP — the checkpoint's own multi-token-prediction head; the fallback when no drafter is present.
  • Prompt-copy drafting — when the output starts repeating text from the prompt (code edits, quoting tool results, multi-turn agents), the lane proposes the continuation of that earlier text and verifies it in the same pass. It is on by default (YUNSHU_SPEC_COPY_ROWS, 0 turns it off).

Every decode and verify matmul goes through batch-invariant kernels: a token's arithmetic is the same whether it is verified alone or among other rows. That is what makes greedy output identical with speculation on and off, and sampled output exact under position-keyed sampling. Choose the drafter with YUNSHU_VLM_DRAFT (mtp, off, or a drafter path).

Prefix cache (APC)

Qwen3.5-family models mix attention layers with recurrent GatedDeltaNet layers. A recurrent state cannot be cut back to an earlier token the way a KV cache can, so ordinary prefix caching does not work. Yunshu stores exact checkpoints (KV plus recurrent state) at prefix boundaries, keyed by the text tokens and by image pixels / audio features, so a cache hit produces exactly what a cold prefill would.

Tier Where Default
HOT ready-to-use arrays in RAM on, sized from free memory (YUNSHU_VLM_APC_MEMORY_GB)
WARM compressed in RAM (lossless zstd, or lossy int8 / int4) off (YUNSHU_VLM_APC_WARM)
SSD ~/.yunshu/cache/apc, one global disk budget with a free-space reserve, survives restarts on (YUNSHU_VLM_APC_DISK, _DIR, _GB)
Storage tiers external SSD, HDD, NAS (YUNSHU_VLM_APC_DISK_TIERS) off; each volume's speed is measured and it is used only when restoring beats recomputing

Repeated or edited long prompts, multi-turn chats and agent loops restore from the nearest checkpoint instead of prefilling again. When a conversation grows, older checkpoints of the same conversation are replaced rather than piling up. yunshu cache status and yunshu cache gc show and clean the SSD caches.

Structured output

JSON schema, JSON object, regex and grammar constraints are enforced during decoding (llguidance by default), including tool-call arguments. Unsupported schema constructs are rejected with an error instead of being silently ignored.

API compatibility

API Routes
OpenAI /v1/chat/completions, /v1/completions, /v1/responses (HTTP and WebSocket), /v1/embeddings, /v1/models, /v1/audio/*, /v1/images/*, /v1/realtime, /v1/files, /v1/batches
Anthropic /v1/messages (thinking, tools, cache_control, server tools web_search / web_fetch, mcp_servers), /v1/messages/count_tokens, /v1/messages/batches, Files
Ollama /api/chat, /api/generate and the model routes
Yunshu extensions live request phases (/v1/requests), cancel by request id, warmup, deadlines, queue headers, prefill progress in streams, /v1/yunshu/status

Parameters covered on chat: tools / tool_choice / parallel_tool_calls, response_format (json_object, strict json_schema), stop, logprobs / top_logprobs (also streamed), n, seed, penalties, logit_bias, reasoning_effort (reasoning returned separately), streaming with usage, and cached-token counts in usage. Errors use each API's own error shape. Extensions are namespaced (x_yunshu, X-Yunshu-*), so the official SDKs ignore them. The full matrix, with how each row was verified, is in API surface.

Typed decisions

POST /v1/decisions (and the System One wire format, /v1/systemone) asks a decision checkpoint predicate, choice and ordinal-score questions about text or images. One forward pass returns typed answers with probabilities; no tokens are generated, and a non-finite result is a refusal, never a made-up probability. Supported today: Cloudflare Clef / Clef-flash MLX checkpoints. Other decision-head families (OpenJev, Laya, D1) are not loaded yet.

decision = client.decisions.create(
    model="abenzerps/Clef-MLX",
    input="The customer asks for a refund after a broken delivery.",
    questions=[{"type": "choice", "name": "route", "instructions": "Choose the support team.",
                "choices": [{"value": "support"}, {"value": "sales"}]}],
)
print(decision.answers)  # typed value, confidence, every option's probability

Also new on main for 0.1.5: stored chat completions, Evals (/v1/evals) and Realtime client secrets.

Decisions, Evals, web search and Tavily API

Coding agents

yunshu launch claude      # or: codex, opencode

yunshu launch writes the client configuration (base URL, model, context window and output limits, reasoning effort) and starts the agent. For Claude Code it also installs a status line showing live prefill progress, decode speed and cache hits.

  • Claude Code — Messages API with streaming, thinking, count_tokens for /context, model discovery, and its WebSearch tool, which Yunshu runs server-side.
  • Codex — Responses API with reasoning items, function calls, local compaction and web_search.
  • opencode — Chat Completions with tools and usage.

Server-side web search defaults to best-effort DuckDuckGo HTML and Wikipedia; queries leave the machine. YUNSHU_WEB_SEARCH_PROVIDER=none disables it. Auto metasearch runs configured keyed providers in parallel with the keyless sources; SearXNG is optional. MCP servers named in a request are connected by the gateway. What each agent calls and how it was checked is in Agent compatibility.

Performance

Qwen3.8-27B (oQ4e) on an M5 Max, 128 GB, single request, greedy. Methods, raw results and the full comparison tables are in docs/BENCHMARKS.md.

Yunshu TensorFold 0.6.1
Cold TTFT, 8K prompt 8.6 s 8.5 s
Cold TTFT, 32K prompt 38.3 s 39.4 s
Repeated / edited long prompt restores from the prefix cache instead of re-prefilling —
Follow-up turn TTFT, 8K / 32K code 512 / 721 ms 505 / 670 ms
Decode, short code prompt ~110 tok/s (DFlash2) ~140 tok/s (DFlash2)
JSON-schema / tool-call output, warm 111 / 78 tok/s (speculative decoding stays on) —

Snapshot of main on 2026-10-02/03 (unreleased after v0.1.4; the table's TensorFold column is the 2026-10-02 same-window run); a fresh same-window 0.1.4 comparison is pending. TensorFold is still faster at single-request decode and at 32K follow-up turns; closing those gaps is the main ongoing work. Structured output keeps speculative decoding (23 tok/s without it). Speculation never changes Yunshu's greedy output. Accuracy against the stock MLX path is checked at three levels (logit alignment, greedy divergence, paired downstream evals) in Accuracy.

Models

Models Serving path
Qwen3.5 / 3.6 / 3.8 family (Qwen3.8-27B tuned first) VLM batch runner with prefix cache and MTP / DFlash2 speculation
Other mlx-vlm models (Gemma, GLM, Qwen-VL, Qwen-Omni, …) Same runner; images, audio and video input where the model supports them; prefix cache where the cache layout allows
Text-only mlx-lm models Single-request fast path with constraints, tools and logprobs

/v1/models returns each model's card: context length, output limit, and which inputs and features it actually supports.

Other capabilities

Capability Endpoint Extra
Qwen3-Omni speech-to-speech /v1/omni/speech/stream (example) omni
Realtime voice, ASR, TTS /v1/realtime, /v1/audio/transcriptions, /v1/audio/speech audio
OCR /v1/ocr (GLM-OCR) vision
Image generation and editing /v1/images/generations, /v1/images/edits generation
Embeddings, rerank, scoring /v1/embeddings, /v1/rerank, /v1/score embeddings

Command line

Command What it does
yunshu doctor check this Mac, dependencies and a model; says how to fix problems
yunshu pull / yunshu model download and manage models
yunshu serve / yunshu service run the server, or install it as a login service
yunshu launch / yunshu statusline start a coding agent wired to Yunshu; live engine status line
yunshu chat, complete, embed, transcribe, speak, ocr, image use a running server from the terminal
yunshu status, cancel server state, cancel an in-flight request
yunshu top (0.1.5) live view of server health, memory, engine and models (--json for one snapshot)
yunshu config effective settings and where each came from
yunshu cache status / gc inspect and clean the SSD prefix caches
yunshu bench, eval, diagnose benchmarks, accuracy evals, system diagnostics

Every command has --help.

Configuration

Every setting goes through one registry: environment variables, a TOML file (yunshu serve --config yunshu.toml) or --set KEY=VALUE. yunshu config shows the effective values and their sources. Common ones:

Setting Purpose
YUNSHU_VLM_DRAFT drafter choice: mtp, off, or a drafter path
YUNSHU_SPEC_COPY_ROWS prompt-copy drafting width (0 = off)
YUNSHU_VLM_APC_MEMORY_GB, YUNSHU_VLM_APC_DISK_GB, YUNSHU_VLM_APC_DISK_DIR prefix-cache RAM and SSD budgets and location
YUNSHU_VLM_APC_DISK_TIERS extra storage tiers, e.g. /Volumes/Ext/apc@200,/Volumes/NAS/apc
YUNSHU_VLM_APC_WARM, YUNSHU_KV_PRECISION lossy memory savers (off by default)
YUNSHU_AUTH_TOKEN, YUNSHU_QUEUE_LIMIT API key and request queue limit

All settings: Configuration.

Docs

Privacy

No usage analytics or crash reports are sent. Host telemetry (power, thermal, memory) is sampled locally for the console and never leaves the machine (guide). Yunshu connects out only to download models, to web-search / MCP providers you configure, and for web fetches a request asks for.

Built on and license

MLX, mlx-lm, mlx-vlm, mlx-audio. Vendored kernels from oMLX and TensorFold keep their notices in THIRD_PARTY_NOTICES. Yunshu is Apache 2.0: LICENSE.

Metadata

Release files for yunshu 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yunshu 0.1.5
File Size Uploaded
yunshu-0.1.5.tar.gz 14.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for yunshu 0.1.5
File Interpreter ABI Platform
yunshu-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size: 22.4 MB

Release files / yunshu-0.1.5.tar.gz

Download URL yunshu-0.1.5.tar.gz
Size 14.1 MB
Tags Source
SHA-256 checksum
How to use checksums
d97d2494d97c91c6623905b5f9bb8cf1917b7d0dfb27ccd6bff8382af45ba282
BLAKE2b-256 checksum
How to use checksums
540c3b617c59ca41e460da7e205f4b1e73daba9a1ca6e79007f2324c058a4039
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / yunshu-0.1.5-py3-none-any.whl

Download URL yunshu-0.1.5-py3-none-any.whl
Size 8.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
5c5ac585722957e21ff47c1503e40afeea14a3b723500ace59bfeeccd2f66b81
BLAKE2b-256 checksum
How to use checksums
50a57afb98b792d88199bf16e543d2c1c777f101a16b376b08b26936289ed0f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.5 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page