- DFlash 2 enables multi-token speculative drafting (yielding 2.5–3× speedups on local models like Qwen 2.5 / Qwen 3.8 and Muse-Glimmer). You can launch and manage a local llama-server with DFlash 2 speculative drafting directly from PortOS and connect using the OpenCode llama TUI provider.
+ Speculative decoding pairs a small drafter with your target model for 2–3× faster generation at identical output. You can launch and manage a local llama-server from PortOS and connect using the OpenCode llama TUI provider. DSpark (draft-dspark) works on a stock brew install llama.cpp; the DFlash 2 presets need a from-source build of an unmerged llama.cpp branch.
)}
{llamaStatus.managed ? (
@@ -981,10 +1000,10 @@ export function LocalLlmTab() {
id="llama-preset-select"
aria-label="Preset"
onChange={(e) => handlePresetSelect(e.target.value)}
- defaultValue="qwen3.8-27b"
+ defaultValue="qwen3.8-27b-dspark"
className="bg-port-card border border-port-border rounded px-2 py-1 text-xs text-port-accent focus:outline-none"
>
- {DFLASH_PRESETS.map((p) => (
+ {SPEC_DECODE_PRESETS.map((p) => (
diff --git a/docs/README.md b/docs/README.md
index e346d10a1..743fcd36e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -34,7 +34,7 @@ Start with the [product surface map](./features/product-surfaces.md) for a compl
App management: [app-wizard](./features/app-wizard.md) · [autofixer](./features/autofixer.md) · [browser](./features/browser.md) · [error-handling](./features/error-handling.md) · [jira-sprint-manager](./features/jira-sprint-manager.md)
-Chief of Staff: [chief-of-staff](./features/chief-of-staff.md) · [cos-agent-runner](./features/cos-agent-runner.md) · [cos-enhancement](./features/cos-enhancement.md) · [agent-context](./features/agent-context.md) · [agent-skills](./features/agent-skills.md) · [memory-system](./features/memory-system.md) · [claude-ollama](./features/claude-ollama.md) · [mtplx](./features/mtplx.md) · [dflash2](./features/dflash2.md) · [prompt-manager](./features/prompt-manager.md)
+Chief of Staff: [chief-of-staff](./features/chief-of-staff.md) · [cos-agent-runner](./features/cos-agent-runner.md) · [cos-enhancement](./features/cos-enhancement.md) · [agent-context](./features/agent-context.md) · [agent-skills](./features/agent-skills.md) · [memory-system](./features/memory-system.md) · [claude-ollama](./features/claude-ollama.md) · [mtplx](./features/mtplx.md) · [dflash2](./features/dflash2.md) ([DSpark vs DFlash 2](./research/2026-08-19-dspark-vs-dflash2.md)) · [prompt-manager](./features/prompt-manager.md)
Identity & self: [digital-twin](./features/digital-twin.md) · [identity-system](./features/identity-system.md) · [soul-system](./features/soul-system.md) · [privacy-center](./features/privacy-center.md) · [post](./features/post.md) (insights design spike: [plans/2026-06-03](./plans/2026-06-03-cross-domain-insights-engine.md))
diff --git a/docs/features/dflash2.md b/docs/features/dflash2.md
index 8a3d032d7..51baab882 100644
--- a/docs/features/dflash2.md
+++ b/docs/features/dflash2.md
@@ -1,8 +1,12 @@
-# DFlash 2 Speculative Decoding — llama.cpp & OpenCode llama TUI
+# Speculative Decoding (DSpark / DFlash 2) — llama.cpp & OpenCode llama TUI
[DFlash 2](https://huggingface.co/z-lab) provides deep, ultra-fast block-level speculative drafting for large language models (such as Qwen 2.5, Qwen 3.8, and Muse-Glimmer). By pairing a small speculative drafter model (typically 1.5–3 GB) with a target foundation model (e.g. 27B–30B), DFlash 2 achieves 2.5–3× end-to-end token generation speedups without sacrificing output quality.
-PortOS provides direct integration with DFlash 2 through the **OpenCode llama TUI** provider preset and local OpenAI-compatible inference servers like `llama-server`.
+[DSpark](https://arxiv.org/html/2607.05147v1) (DeepSeek) is the sibling drafter family — same lossless block-drafting idea, a rank-256 Markov head instead of DFlash 2's candidate selector, and a much wider set of published drafters.
+
+PortOS integrates both through the **OpenCode llama TUI** provider preset and the managed local `llama-server`.
+
+> **Which one to run:** `--spec-type draft-dspark` **merged into llama.cpp on 2026-07-28** ([#25173](https://github.com/ggml-org/llama.cpp/pull/25173)) and works on a stock `brew install llama.cpp`. DFlash 2's engine modules are **still an open PR** ([#27342](https://github.com/ggml-org/llama.cpp/pull/27342)) and need a from-source build of that branch. llama.cpp falls back **silently** on a spec-type/drafter mismatch rather than erroring, so a DFlash 2 drafter on a stock build degrades quietly. Start with DSpark unless you have built the DFlash 2 branch. Full comparison: [DSpark vs DFlash 2](../research/2026-08-19-dspark-vs-dflash2.md).
---
@@ -10,7 +14,7 @@ PortOS provides direct integration with DFlash 2 through the **OpenCode llama TU
1. **OpenCode llama TUI Provider**:
- An attachable `tui` coding-agent provider preset (`opencode-llama-tui`) configured to connect to `http://127.0.0.1:8080/v1`.
- - Seeded with default model aliases `["dflash", "qwen3.8-27b-dflash2", "Muse-Glimmer-30B-DFlash2"]` with default `dflash`.
+ - Seeded with default model aliases `["dflash", "qwen3.8-27b-dflash2", "Muse-Glimmer-30B-DFlash2"]` with default `dflash`. The launcher keeps `--alias dflash` for every drafter family so this alias resolves regardless of which one you run.
- Fully enabled by default and equipped with OpenCode's agentic file-writing harness, tool calling, and session persistence.
2. **Model Refresh**:
- Support for dynamic model discovery via the **Refresh Models** button on AI Providers, querying the local `llama-server` `/v1/models` endpoint.
@@ -22,7 +26,21 @@ PortOS provides direct integration with DFlash 2 through the **OpenCode llama TU
## Setup & Running with llama-server
### 1. Download Base & Draft Models
-Download your base GGUF and corresponding DFlash 2 drafter GGUF from Hugging Face:
+Download your base GGUF and a matching drafter GGUF from Hugging Face. Drafter
+checkpoints are **not standalone models** — they only produce text once an engine
+pairs them with their specific target, which is why PortOS's model search filters
+them out of the install picker.
+
+DSpark pairs (stock llama.cpp):
+
+- **Qwen 3.8 27B**: base `Qwen/Qwen3.8-27B-Instruct-GGUF` + drafter `DimInfer/Qwen3.8-27B-Dspark-v1`
+- **Qwen 3 8B**: base `Qwen/Qwen3-8B-Instruct-GGUF` + drafter converted from `deepseek-ai/dspark_qwen3_8b_block7`
+
+DSpark drafters ship without tokenizers — converting one to GGUF requires passing
+`--target-model-dir` so it reuses the target's tokenizer. Keep the drafter at bf16;
+the target can be any quant.
+
+DFlash 2 pairs (require a source build of llama.cpp [#27342](https://github.com/ggml-org/llama.cpp/pull/27342)):
- **Qwen 3.8 27B Draft Pair**:
- Base: `Qwen/Qwen3.8-27B-Instruct-GGUF` (e.g. `Qwen3.8-27B-Instruct-Q4_K_M.gguf`)
@@ -32,13 +50,14 @@ Download your base GGUF and corresponding DFlash 2 drafter GGUF from Hugging Fac
- Drafter: `z-lab/Muse-Glimmer-30B-DFlash2-GGUF`
### 2. Launch llama-server
-Start `llama-server` on loopback port `8080` with speculative decoding enabled:
+Start `llama-server` on loopback port `8080` with speculative decoding enabled
+(`--spec-type draft-dflash` for a DFlash drafter, `draft-dspark` for a DSpark one):
```bash
llama-server \
-m models/Qwen3.8-27B-Instruct-Q4_K_M.gguf \
- --draft-model models/Qwen3.8-27B-DFlash2-Q4_K_M.gguf \
- --spec-type draft-dflash \
+ --draft-model models/Qwen3.8-27B-DSpark-bf16.gguf \
+ --spec-type draft-dspark \
--port 8080 \
--host 127.0.0.1 \
--alias dflash \
@@ -51,3 +70,17 @@ llama-server \
2. Verify **OpenCode llama TUI** is enabled.
3. Click **Refresh Models** to pull the live aliases from `llama-server`, or use the default `dflash` model.
4. Select **OpenCode llama TUI** in the CoS task creator or terminal runner to execute coding and agent tasks with speculative acceleration.
+
+---
+
+## Ollama
+
+Ollama's vendored llama.cpp engine has carried DSpark since **0.32.6**
+([#17545](https://github.com/ollama/ollama/pull/17545)), but Ollama surfaces no
+control for it — its server hardcodes `--spec-type draft-mtp` whenever
+speculative decoding is on. Selecting DSpark requires overriding
+`LLAMA_ARG_SPEC_TYPE` / `LLAMA_ARG_SPEC_DRAFT_MODEL` on the Ollama process and
+pointing at a raw blob-store path; the one reported measurement through that
+path was ~13%, far below what the drafter delivers natively. Tracked upstream in
+[ollama#17016](https://github.com/ollama/ollama/issues/17016). **Use
+`llama-server` for speculative decoding in PortOS, not the Ollama backend.**
diff --git a/docs/research/2026-08-19-dspark-vs-dflash2.md b/docs/research/2026-08-19-dspark-vs-dflash2.md
new file mode 100644
index 000000000..a878bdf1d
--- /dev/null
+++ b/docs/research/2026-08-19-dspark-vs-dflash2.md
@@ -0,0 +1,178 @@
+# DSpark vs DFlash 2 — is DSpark worth adding alongside?
+
+Date: 2026-08-19
+
+Follow-up to [DFlash 2 speculative decoding](./2026-08-19-dflash2-speculative-decoding.md),
+prompted by [ARahim3/mlx-dspark](https://github.com/ARahim3/mlx-dspark). Two
+questions: is DSpark worth adding alongside DFlash 2, and is there Ollama
+support for it?
+
+## Verdict
+
+**Yes — but the thing worth adding is a preset, not a backend, and DSpark turns
+out to be the *more* available of the two on the engine PortOS already ships.**
+
+The DFlash 2 note concluded "not adoptable today" partly because "PortOS
+orchestrates no llama.cpp server." That is no longer true — PortOS now manages
+`llama-server` directly (`server/services/llamaServerManager.js`, Settings →
+Local LLMs), and its **Spec Type field is free text** that maps straight onto
+llama.cpp's `--spec-type`. So the ranking flipped:
+
+| Drafter family | llama.cpp status | Reachable from PortOS today |
+| --- | --- | --- |
+| **DSpark** | [#25173 **merged 2026-07-28**](https://github.com/ggml-org/llama.cpp/pull/25173) | **Yes** — `brew install llama.cpp`, `--spec-type draft-dspark` |
+| DFlash (v1) | merged | Yes |
+| DFlash 2 | [#27342 **still open**](https://github.com/ggml-org/llama.cpp/pull/27342) (last activity 2026-08-19) | Only from a source build of the PR branch |
+
+PortOS's shipped presets pointed exclusively at DFlash 2 drafters — the one
+family that needs an unmerged branch. DSpark needs **zero** new backend, zero
+new dependency, and no server change: a `--spec-type` string and a drafter GGUF.
+That is the whole cost, so it clears the surface-area bar the DFlash 2 note set.
+
+**`mlx-dspark` itself is not worth adopting as a backend** — see below. Its
+value here was as a signpost to the merged llama.cpp path.
+
+## Is there Ollama support for DSpark?
+
+**Partially, and not in a form PortOS can use.** Precisely:
+
+- Ollama's **vendored llama.cpp engine has had DSpark since 0.32.6**
+ ([#17545](https://github.com/ollama/ollama/pull/17545), merged 2026-08-04) —
+ the engine can decode it.
+- **Ollama surfaces no control for it.** `llm/llama_server.go` hardcodes
+ `--spec-type draft-mtp` whenever speculative decoding is on, so a Modelfile
+ `DRAFT` + `PARAMETER draft_num_predict` pairing selects MTP, never DSpark.
+- The only working recipe is an **environment-variable override** on the Ollama
+ server process — `LLAMA_ARG_SPEC_TYPE=draft-dspark`,
+ `LLAMA_ARG_SPEC_DRAFT_N_MAX`, `LLAMA_ARG_SPEC_DRAFT_MODEL` pointed at a raw
+ **blob-store sha256 path**, with `DRAFT` set but `draft_num_predict` left
+ unset. The one reported measurement through that path was **~13%**, an order
+ of magnitude below the 2–3× the drafter delivers natively.
+- The feature request, [ollama#17016](https://github.com/ollama/ollama/issues/17016)
+ (opened 2026-07-03), is **still open**. There is no `x/models/dspark` package
+ (404) to match the `x/models/dflash` one.
+
+A recipe that requires editing PortOS's own Ollama service environment and
+hand-copying a content-addressed blob path is not something to wire into a UI.
+**No Ollama work is warranted** — the DSpark path for PortOS is `llama-server`,
+which PortOS already manages and where the flag is a first-class argument.
+
+## What DSpark is, versus DFlash 2
+
+Both are lossless EAGLE-family block drafters: a small head proposes several
+tokens, the target verifies them in one pass, and greedy output is unchanged.
+They differ in how they fight *suffix decay* (later positions in a block are
+guessed without knowing the earlier ones):
+
+- **DFlash 2** — block diffusion. Keeps the top-16 candidates per position and
+ adds a bilinear selector plus two-tap depthwise convolutions to trace one
+ coherent path. Both live in the engine, which is why #27342 is a real code
+ change and not just a checkpoint.
+- **DSpark** (DeepSeek, [arXiv:2607.05147](https://arxiv.org/html/2607.05147v1))
+ — a 5-layer parallel backbone over the target's hidden states plus a
+ **rank-256 Markov head** that conditions each drafted token on the previously
+ sampled one, and a confidence head for adaptive block length. In llama.cpp it
+ is layered on the already-merged DFlash drafter machinery, which is why it
+ landed as a smaller patch and merged first.
+
+Head-to-head on the same target (`mlx-dspark`'s M4 Pro benchmarks,
+Qwen3.8-27B 8-bit) they are close — DFlash 2 measures **3.63×** and DSpark's
+hybrid **2.72×**. DFlash 2 is the faster algorithm where it runs. Availability,
+not throughput, is what separates them for PortOS today.
+
+DSpark also has far **wider target coverage**: DeepSeek ships official heads for
+Qwen3 4B/8B/14B, and the community has published drafters for DeepSeek-V4-Flash,
+Kimi-K3, GLM-5.2, Gemma-4, Nemotron, Muse-Glimmer and Bonsai. DFlash 2's
+published set is much narrower.
+
+## Why `mlx-dspark` is not a backend PortOS should adopt
+
+[ARahim3/mlx-dspark](https://github.com/ARahim3/mlx-dspark) (MIT, ~450 stars,
+active) is a genuinely nice piece of work: a native MLX port of both DSpark and
+DFlash, a drafter registry that auto-resolves target→drafter pairs, per-machine
+calibration, and an OpenAI- *and* Anthropic-compatible server. Adopting it would
+still be wrong:
+
+- It is a **fourth local inference backend** (after Ollama, LM Studio, and
+ `llama-server`) — pip-installed, Apple-Silicon-only, Python ≥3.10, MLX
+ safetensors only, **no GGUF** — bought for a speedup PortOS can already get
+ through a backend it manages. That is the same trade the DFlash 2 note and the
+ [Qwen3.8 27B MLX evaluation](./2026-08-16-qwen38-mlx-macos.md) both declined.
+- **A user who wants it needs nothing from PortOS.** `mlx-dspark serve` listens
+ on `127.0.0.1:8080` and speaks `/v1/chat/completions` and `/v1/models` — the
+ exact endpoint the shipped `opencode-llama-tui` provider preset already points
+ at. Stop `llama-server`, start `mlx-dspark serve`, hit **Refresh Models**. Any
+ integration PortOS wrote would duplicate that for no gain.
+- Its own caveats narrow the win further: MoE targets (Nemotron 1.10×,
+ Qwen3.6-35B-A3B 1.32×) and 2-bit Bonsai (1.07×) barely clear break-even, and
+ hybrid targets can't batch.
+
+Its **drafter registry is worth borrowing as data, not code** — it is the best
+published target→drafter mapping for both families, and the source for the
+preset drafter names added here.
+
+## What shipped with this note
+
+Two changes, both small and both consequences of the investigation.
+
+### 1. DSpark presets on the llama-server launcher
+
+`client/src/components/settings/LocalLlmTab.jsx`: `DFLASH_PRESETS` →
+`SPEC_DECODE_PRESETS`, with two `draft-dspark` presets leading (Qwen3.8-27B and
+Qwen3-8B) and the DFlash 2 presets kept but relabelled with the build they
+require. The default selection and the form's initial `specType` move to
+`draft-dspark`, so the out-of-box preset is one a stock Homebrew `llama.cpp`
+can actually run. No server change — `specType` was already a pass-through
+string, and `alias` stays `dflash` so the `opencode-llama-tui` provider's
+default model alias keeps resolving.
+
+Note for anyone reading the DFlash 2 presets: llama.cpp **silently falls back**
+on a spec-type/drafter mismatch rather than erroring, so a DFlash 2 drafter run
+against stock `--spec-type draft-dflash` degrades quietly instead of failing
+loudly. The relabelled preset names are the warning.
+
+### 2. DSpark drafters filtered out of the MLX model search
+
+Same hazard the DFlash 2 note fixed, same class, new family. The MLX branch of
+`server/services/huggingFaceCatalog.js` matched `mtp|dflash\d*|drafter` in the
+repo name; `dspark` joins it, because a real mlx-community repo slips the tag
+predicate:
+
+`mlx-community/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-DSpark-bf16` declares
+`mlx`, `dspark` and `speculative-decoding` — **but no `draft-model` tag**. Under
+the shipped filter PortOS offered it as a normal one-click LM Studio MLX
+install, handing the user a 30B target's sidecar that cannot chat. Covered by a
+new test in `huggingFaceCatalog.test.js` (verified failing before the fix).
+
+**Residual, accepted — the GGUF branch is left alone.** Sampling the 40
+most-downloaded `dspark` repos on Hugging Face, roughly half of the GGUF ones
+carry no `draft-model`/`drafter` tag (`YanissAmz/DeepSeek-V4-Flash-DSpark-draft-GGUF`,
+`Lucebox/DeepSeek-V4-Flash-0731-DSpark-GGUF`, `magnitudedev/Qwen3.8-27B-DSpark-GGUF`,
+…), so the tag predicate misses them. Widening the GGUF filter to match `dspark`
+in the *name* is what the DFlash 2 note explicitly rejected for this branch, and
+the reason holds here: that space also contains repos whose names carry `dspark`
+while shipping a complete model (abliterated/merged DeepSeek-V4-Flash variants),
+so a name match would hide mainstream installs — the worse failure. The MLX
+space stays curated enough for name matching; the GGUF long tail does not.
+
+## When to re-evaluate
+
+- **DFlash 2 merges into llama.cpp master (#27342).** Then both families run on
+ a stock build, DFlash 2's higher measured speedup becomes the reason to prefer
+ it, and the preset ordering here should flip back. This is the same trigger
+ already tracked in [#4568](https://github.com/atomantic/PortOS/issues/4568).
+- **Ollama surfaces a real DSpark control** (closing
+ [ollama#17016](https://github.com/ollama/ollama/issues/17016) with a Modelfile
+ directive or a `spec_type` parameter, not the `LLAMA_ARG_*` workaround). Then
+ the Ollama backend — which serves the Claude-Ollama and OpenCode-Ollama
+ providers — gets the speedup too, and PortOS's work is again a catalog entry.
+
+## Sources
+
+- [ARahim3/mlx-dspark](https://github.com/ARahim3/mlx-dspark) — MLX port, benchmarks, drafter registry
+- [DSpark paper — arXiv:2607.05147](https://arxiv.org/html/2607.05147v1) · [DSpark in SGLang](https://www.lmsys.org/blog/2026-07-06-dspark-sglang/)
+- [llama.cpp #25173 — spec: add DSpark speculative decoding](https://github.com/ggml-org/llama.cpp/pull/25173) (merged 2026-07-28)
+- [llama.cpp #27342 — spec: add DFlash2 support](https://github.com/ggml-org/llama.cpp/pull/27342) (open)
+- [ollama #17016 — dspark option](https://github.com/ollama/ollama/issues/17016) · [ollama #17545 — llama.cpp update](https://github.com/ollama/ollama/pull/17545) (merged 2026-08-04)
+- [DFlash 2 speculative decoding](./2026-08-19-dflash2-speculative-decoding.md) — the prior evaluation this revises
+- [Qwen3.8 27B MLX options on macOS](./2026-08-16-qwen38-mlx-macos.md) — the original sidecar decision
diff --git a/server/services/huggingFaceCatalog.js b/server/services/huggingFaceCatalog.js
index 935ebb8dd..744c1f310 100644
--- a/server/services/huggingFaceCatalog.js
+++ b/server/services/huggingFaceCatalog.js
@@ -595,8 +595,12 @@ function hasDrafterTag(model) {
// suffix reliably marks a sidecar — unlike the GGUF long tail above. `dflash`
// joins the pre-existing `mtp`/`drafter` tokens because DFlash drafters ship as
// MLX safetensors too (`jfan/Qwen3.8-27B-heretic-dflash`), and `\d*` covers the
-// DFlash2 generation.
-const MLX_DRAFTER_NAME_RE = /(?:^|[\/_-])(?:mtp|dflash\d*|drafter)(?:[\/_\-.]|$)/i
+// DFlash2 generation. `dspark` is the same sidecar class from DeepSeek's
+// drafter family — `mlx-community/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-DSpark-bf16`
+// declares `mlx` + `dspark` + `speculative-decoding` but no `draft-model` tag, so
+// the tag predicate alone would offer a 30B target's drafter as a one-click
+// install (docs/research/2026-08-19-dspark-vs-dflash2.md).
+const MLX_DRAFTER_NAME_RE = /(?:^|[\/_-])(?:mtp|dflash\d*|dspark|drafter)(?:[\/_\-.]|$)/i
function isMlxDrafter(model) {
return hasDrafterTag(model) || MLX_DRAFTER_NAME_RE.test(repoIdOf(model))
diff --git a/server/services/huggingFaceCatalog.test.js b/server/services/huggingFaceCatalog.test.js
index 621c0d190..2a0218c85 100644
--- a/server/services/huggingFaceCatalog.test.js
+++ b/server/services/huggingFaceCatalog.test.js
@@ -724,6 +724,22 @@ describe('huggingFaceCatalog', () => {
expect(results.some((r) => r.repository === drafterRepo)).toBe(false)
})
+ // Real listing: `mlx` + `dspark` + `speculative-decoding` tags, no
+ // `draft-model` tag — so only the repo-name match keeps it out.
+ it('does not offer a DSpark drafter published as MLX safetensors', async () => {
+ const drafterRepo = 'mlx-community/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-DSpark-bf16'
+ fetch.mockImplementation(urlRouter([
+ ['filter=mlx', [mlxListing(drafterRepo, ['model.safetensors'])]],
+ ['filter=gguf', []],
+ ]))
+
+ const results = await searchHuggingFaceModels({
+ backend: 'lmstudio', query: 'nemotron', appleSilicon: true
+ })
+
+ expect(results.some((r) => r.repository === drafterRepo)).toBe(false)
+ })
+
it('does not surface MLX on a non-Apple host even for LM Studio', async () => {
const mlxRepo = 'mlx-community/Hidden-On-Intel-4bit'
fetch.mockImplementation(urlRouter([