Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/utils/providers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ describe('supportsModelRefresh', () => {
// provider — nothing here can enumerate that, and Models → Harnesses
// ("Refresh models") is where their catalog comes from instead.
'opencode-zen',
'openrouter', 'orcarouter',
'openrouter', 'orcarouter', 'slotstream',
]);
});
});
Expand Down
12 changes: 12 additions & 0 deletions data.reference/providers.json
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,18 @@
"enabled": false,
"envVars": {}
},
"slotstream": {
"id": "slotstream",
"name": "Slotstream (SSD-streaming MoE)",
"type": "api",
"endpoint": "http://127.0.0.1:5564/v1",
"apiKey": "",
"models": ["qwen3-235b-a22b-4bit", "gpt-oss-120b-mxfp4", "qwen3-30b-a3b-4bit"],
"defaultModel": "qwen3-235b-a22b-4bit",
"timeout": 300000,
"enabled": false,
"envVars": {}
},
"nvidia-kimi": {
"id": "nvidia-kimi",
"name": "NVIDIA Kimi K2.5",
Expand Down
2 changes: 1 addition & 1 deletion docs/features/product-surfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Local AI model acceleration, multi-machine peer federation, storage classificati

| Surface / Area | Route(s) | Key Capabilities & Workflows | Related Guides |
|---|---|---|---|
| **Local LLM Runtimes** | `/models/llms` | Management of local model servers: Ollama, LM Studio, vLLM, SGLang, and llama.cpp / llama-server. | [Claude on Ollama](./claude-ollama.md) |
| **Local LLM Runtimes** | `/models/llms` | Management of local model servers: Ollama, LM Studio, vLLM, SGLang, llama.cpp / llama-server, MTPLX, and Slotstream (SSD-streaming MoE for checkpoints larger than RAM). | [Claude on Ollama](./claude-ollama.md), [MTPLX](./mtplx.md), [Slotstream](./slotstream.md) |

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Non-blocking — Row also adds MTPLX, which the linked issue did not ask for

Issue #5816 asked to list the new Slotstream page under the product surface map; this row additionally adds MTPLX and its doc link to the same cell, which the issue never requested. The mention is accurate (docs/features/mtplx.md exists) and corrects a real omission in the very row being updated, so it is a scope note, not a blocker; split it out only if the maintainers want strict single-issue scope.

| **Speculative Decoding** | `/models/llms` | Accelerated token generation using DSpark, DFlash 2, and MTPLX speculative drafting pairs. | [DFlash2 & DSpark](./dflash2.md), [MTPLX](./mtplx.md), [RTX 3090 vLLM](./qwen38-rtx3090.md), [SGLang Qwen](./sglang-qwen38.md) |
| **Embeddings Management** | `/models/embeddings` | Local text embedding models (Nomic, Ollama) and pgvector semantic index configuration. | [STORAGE.md](../STORAGE.md) |
| **LoRAs & Model Training** | `/models/loras`, `/models/training` | LoRA adapter discovery, Civitai downloads, image captioning, and local FLUX LoRA training dataset management. | — |
Expand Down
33 changes: 27 additions & 6 deletions docs/features/slotstream.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,39 @@ same port would silently steal its traffic instead of failing loudly.
progress over the `slotstream:download` socket event; the download resumes
from a partial file if interrupted, and stalls (no bytes for 20 minutes by
default) are abandoned rather than hung forever, resumable on the next
press. PortOS also accepts a plain Hugging Face `owner/name` outside the
curated list, as long as the repo is a Slotstream-loadable MoE checkpoint.
Nothing downloads without this explicit press.
press. Every downloaded file is checked against the hash Hugging Face
published for it (`expectedSha256` in `streamResumableDownload`) before it
counts as finished; a mismatch fails the install and removes the partial
file rather than leaving a silently corrupt checkpoint on disk. PortOS also
accepts a plain Hugging Face `owner/name` outside the curated list, as long
as the repo is a Slotstream-loadable MoE checkpoint. Nothing downloads
without this explicit press.
3. **Start Slotstream** on the card, or **Save configuration** to store the
choice for a later on-demand start without starting it now. Either way,
Slotstream never fetches weights at start time — a start reads
`~/.slotstream/models` (or `SLOTSTREAM_MODEL_DIR`, see below) and refuses
with a clear message if nothing servable is cached, rather than trying to
fetch the requested checkpoint.
4. Point an `api`/`tui` provider at the Slotstream endpoint (or use the
`slotstream` preset if one exists in AI Providers) and use it for
supported tasks.
4. Enable the **Slotstream (SSD-streaming MoE)** preset on **AI Providers** —
see below — or point any `api`/`tui` provider at the endpoint by hand, and
use it for supported tasks.

## AI Providers preset

`data.reference/providers.json` ships a `slotstream` preset (`api` type,
`http://127.0.0.1:5564/v1`, **disabled by default** — the same
consent-before-cost posture as the download itself) and migration 340 seeds it
into existing installs. It offers the curated catalog's three checkpoint ids
as candidate models, defaulting to the 235B-class headline case; whichever
checkpoint is actually cached and started is what answers, regardless of which
catalog id the request names.

Unlike MTPLX, Slotstream gets no `opencode-slotstream` CLI/TUI wrapper: it is
text-only, so it is not a valid CoS coding-agent runner. `isSlotstreamProvider`
(`server/services/slotstreamServerManager.js`) recognizes any `api`/`tui`
provider pointed at `PORTS.SLOTSTREAM` — including a hand-added one, not only
this preset — so `ensureSlotstreamProviderReady` lazy-starts the runtime for
it the same way.

## Memory plan

Expand Down
39 changes: 39 additions & 0 deletions scripts/migrations/340-slotstream-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Ship a disabled Slotstream provider preset to existing installs.
*
* Slotstream is a separately managed local runtime (SSD-streaming MoE) —
* see docs/features/slotstream.md. It answers ordinary text tasks through its
* loopback OpenAI-compatible endpoint the same way MTPLX's API preset does
* (migration 272): text-only, so no CLI/TUI variant is registered — Slotstream
* is not a valid CoS coding-agent runner.
*
* This migration deliberately does not install Slotstream, download a
* checkpoint, start a daemon, or contact its endpoint. The preset is disabled
* by default. An install that already owns this id is left untouched,
* preserving refreshed models and local endpoint edits.
*
* Kept in lockstep with data.reference/providers.json and
* server/lib/aiToolkit/defaults/providers.sample.json. This frozen literal is
* the historical upgrade payload; later default changes require a new
* migration rather than rewriting this record.
*/

import { makeProviderSeedMigration } from './_lib.js';

const SLOTSTREAM_API = {
id: 'slotstream',
name: 'Slotstream (SSD-streaming MoE)',
type: 'api',
endpoint: 'http://127.0.0.1:5564/v1',
apiKey: '',
models: ['qwen3-235b-a22b-4bit', 'gpt-oss-120b-mxfp4', 'qwen3-30b-a3b-4bit'],
defaultModel: 'qwen3-235b-a22b-4bit',
timeout: 300000,
enabled: false,
envVars: {},
};

export default makeProviderSeedMigration({
label: 'Slotstream',
defs: [SLOTSTREAM_API],
});
70 changes: 70 additions & 0 deletions scripts/migrations/340-slotstream-provider.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Test for migration 340 — add the Slotstream provider preset to existing
* installs. The shared idempotent write shell is asserted in _lib.test.js;
* this test pins migration 340's frozen payload and its disabled-by-default,
* text-only contract (no CLI/TUI variant — see migration 272 for why MTPLX
* gets one and Slotstream does not).
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync, readFileSync, mkdirSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';

import migration from './340-slotstream-provider.js';

const writeJson = (path, value) => writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
const readJson = (path) => JSON.parse(readFileSync(path, 'utf-8'));

describe('migration 340 — Slotstream provider', () => {
let rootDir;
let providersPath;

beforeEach(() => {
rootDir = mkdtempSync(join(tmpdir(), 'migration-340-'));
mkdirSync(join(rootDir, 'data'), { recursive: true });
providersPath = join(rootDir, 'data/providers.json');
});

afterEach(() => {
rmSync(rootDir, { recursive: true, force: true });
});

it('adds a disabled API preset without touching existing state', async () => {
writeJson(providersPath, {
activeProvider: 'claude-code',
providers: { 'claude-code': { id: 'claude-code', type: 'cli', command: 'claude' } },
});

await migration.up({ rootDir });

const out = readJson(providersPath);
const api = out.providers.slotstream;

expect(api).toMatchObject({
id: 'slotstream',
type: 'api',
endpoint: 'http://127.0.0.1:5564/v1',
models: ['qwen3-235b-a22b-4bit', 'gpt-oss-120b-mxfp4', 'qwen3-30b-a3b-4bit'],
defaultModel: 'qwen3-235b-a22b-4bit',
enabled: false,
});

// Text-only: no opencode-slotstream CLI/TUI wrapper, unlike MTPLX.
expect(out.providers['opencode-slotstream']).toBeUndefined();
expect(out.providers['opencode-slotstream-tui']).toBeUndefined();

expect(out.providers['claude-code']).toBeDefined();
expect(out.activeProvider).toBe('claude-code');
});

it('preserves an existing Slotstream provider instead of replacing its local edits', async () => {
const existing = {
id: 'slotstream', name: 'My Slotstream', type: 'api', endpoint: 'http://127.0.0.1:5564/v1', enabled: true,
};
writeJson(providersPath, { providers: { slotstream: existing } });

await migration.up({ rootDir });

expect(readJson(providersPath).providers.slotstream).toEqual(existing);
});
});
13 changes: 13 additions & 0 deletions server/lib/aiToolkit/defaults/providers.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,19 @@
"envVars": {},
"secretEnvVars": []
},
"slotstream": {
"id": "slotstream",
"name": "Slotstream (SSD-streaming MoE)",
"type": "api",
"endpoint": "http://127.0.0.1:5564/v1",
"apiKey": "",
"models": ["qwen3-235b-a22b-4bit", "gpt-oss-120b-mxfp4", "qwen3-30b-a3b-4bit"],
"defaultModel": "qwen3-235b-a22b-4bit",
"timeout": 300000,
"enabled": false,
"envVars": {},
"secretEnvVars": []
},
"grok": {
"id": "grok",
"name": "xAI Grok",
Expand Down
1 change: 1 addition & 0 deletions server/lib/aiToolkit/internal/modelFetchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const SHIPPED_REFRESHABLE = [
'antigravity-cli', 'antigravity-tui', 'cerebras', 'claude-code',
'claude-code-bedrock', 'claude-ollama', 'claude-ollama-tui', 'cursor-cli',
'cursor-tui', 'grok', 'lmstudio', 'mtplx', 'nvidia-kimi', 'ollama',
'slotstream',
'opencode-llama-tui',
'opencode-mtplx', 'opencode-mtplx-tui', 'opencode-ollama', 'opencode-ollama-tui',
'opencode-orcarouter', 'opencode-orcarouter-tui', 'orcarouter',
Expand Down